diff --git a/library/HTMLPurifier/ElementDef.php b/library/HTMLPurifier/ElementDef.php
index 30c80cd4..eb6ff271 100644
--- a/library/HTMLPurifier/ElementDef.php
+++ b/library/HTMLPurifier/ElementDef.php
@@ -93,7 +93,7 @@ class HTMLPurifier_ElementDef
var $safe = false;
/**
- * Factory constructor for creating new standalone element defs
+ * Low-level factory constructor for creating new standalone element defs
* @static
*/
function create($safe, $content_model, $content_model_type, $attr) {
diff --git a/library/HTMLPurifier/HTMLModule.php b/library/HTMLPurifier/HTMLModule.php
index a4d4af73..c6de7670 100644
--- a/library/HTMLPurifier/HTMLModule.php
+++ b/library/HTMLPurifier/HTMLModule.php
@@ -151,6 +151,8 @@ class HTMLPurifier_HTMLModule
$this->info[$element] = HTMLPurifier_ElementDef::create(
$safe, $content_model, $content_model_type, $attr
);
+ // literal object $contents means direct child manipulation
+ if (!is_string($contents)) $this->info[$element]->child = $contents;
}
/**
@@ -171,8 +173,12 @@ class HTMLPurifier_HTMLModule
* into separate content model and content model type
* @param $contents Allowed children in form of:
* "$content_model_type: $content_model"
+ * @note If contents is an object, an array of two nulls will be
+ * returned, and the callee needs to take the original $contents
+ * and use it directly.
*/
function parseContents($contents) {
+ if (!is_string($contents)) return array(null, null); // defer
switch ($contents) {
// check for shorthand content model forms
case 'Empty':
diff --git a/library/HTMLPurifier/HTMLModule/Tables.php b/library/HTMLPurifier/HTMLModule/Tables.php
index 003ff624..732f6bcb 100644
--- a/library/HTMLPurifier/HTMLModule/Tables.php
+++ b/library/HTMLPurifier/HTMLModule/Tables.php
@@ -10,75 +10,66 @@ class HTMLPurifier_HTMLModule_Tables extends HTMLPurifier_HTMLModule
{
var $name = 'Tables';
- var $elements = array('caption', 'table', 'td', 'th', 'tr', 'col',
- 'colgroup', 'tbody', 'thead', 'tfoot');
- var $content_sets = array('Block' => 'table');
function HTMLPurifier_HTMLModule_Tables() {
- foreach ($this->elements as $e) {
- $this->info[$e] = new HTMLPurifier_ElementDef();
- $this->info[$e]->attr = array(0 => array('Common'));
- $attr =& $this->info[$e]->attr;
- if ($e == 'caption') continue;
- if ($e == 'table'){
- $attr['border'] = 'Pixels';
- $attr['cellpadding'] = 'Length';
- $attr['cellspacing'] = 'Length';
- $attr['frame'] = new HTMLPurifier_AttrDef_Enum(array(
+
+ $this->addElement('table', true, 'Block',
+ new HTMLPurifier_ChildDef_Table(), 'Common',
+ array(
+ 'border' => 'Pixels',
+ 'cellpadding' => 'Length',
+ 'cellspacing' => 'Length',
+ 'frame' => new HTMLPurifier_AttrDef_Enum(array(
'void', 'above', 'below', 'hsides', 'lhs', 'rhs',
'vsides', 'box', 'border'
- ), false);
- $attr['rules'] = new HTMLPurifier_AttrDef_Enum(array(
+ ), false),
+ 'rules' => new HTMLPurifier_AttrDef_Enum(array(
'none', 'groups', 'rows', 'cols', 'all'
- ), false);
- $attr['summary'] = 'Text';
- $attr['width'] = 'Length';
- continue;
- }
- if ($e == 'col' || $e == 'colgroup') {
- $attr['span'] = 'Number';
- $attr['width'] = 'MultiLength';
- }
- if ($e == 'td' || $e == 'th') {
- $attr['abbr'] = 'Text';
- $attr['colspan'] = 'Number';
- $attr['rowspan'] = 'Number';
- }
- $attr['align'] = new HTMLPurifier_AttrDef_Enum(array(
+ ), false),
+ 'summary' => 'Text',
+ 'width' => 'Length'
+ )
+ );
+
+ $this->addElement('caption', true, false, 'Inline', 'Common');
+
+ // common attributes
+ $cell_align = array(
+ 'align' => new HTMLPurifier_AttrDef_Enum(array(
'left', 'center', 'right', 'justify', 'char'
- ), false);
- $attr['valign'] = new HTMLPurifier_AttrDef_Enum(array(
+ ), false),
+ 'valign' => new HTMLPurifier_AttrDef_Enum(array(
'top', 'middle', 'bottom', 'baseline'
- ), false);
- $attr['charoff'] = 'Length';
- }
- $this->info['caption']->content_model = '#PCDATA | Inline';
- $this->info['caption']->content_model_type = 'optional';
+ ), false),
+ 'charoff' => 'Length',
+ );
- // Is done directly because it doesn't leverage substitution
- // mechanisms. True model is:
- // 'caption?, ( col* | colgroup* ), (( thead?, tfoot?, tbody+ ) | ( tr+ ))'
- $this->info['table']->child = new HTMLPurifier_ChildDef_Table();
+ $cell_t = array_merge(
+ array(
+ 'abbr' => 'Text',
+ 'colspan' => 'Number',
+ 'rowspan' => 'Number',
+ ),
+ $cell_align
+ );
+ $this->addElement('td', true, false, 'Flow', 'Common', $cell_t);
+ $this->addElement('th', true, false, 'Flow', 'Common', $cell_t);
- $this->info['td']->content_model =
- $this->info['th']->content_model = '#PCDATA | Flow';
- $this->info['td']->content_model_type =
- $this->info['th']->content_model_type = 'optional';
+ $this->addElement('tr', true, false, 'Required: td | th', 'Common', $cell_align);
- $this->info['tr']->content_model = 'td | th';
- $this->info['tr']->content_model_type = 'required';
+ $cell_col = array_merge(
+ array(
+ 'span' => 'Number',
+ 'width' => 'MultiLength',
+ ),
+ $cell_align
+ );
+ $this->addElement('col', true, false, 'Empty', 'Common', $cell_col);
+ $this->addElement('colgroup', true, false, 'Optional: col', 'Common', $cell_col);
- $this->info['col']->content_model_type = 'empty';
-
- $this->info['colgroup']->content_model = 'col';
- $this->info['colgroup']->content_model_type = 'optional';
-
- $this->info['tbody']->content_model =
- $this->info['thead']->content_model =
- $this->info['tfoot']->content_model = 'tr';
- $this->info['tbody']->content_model_type =
- $this->info['thead']->content_model_type =
- $this->info['tfoot']->content_model_type = 'required';
+ $this->addElement('tbody', true, false, 'Required: tr', 'Common', $cell_align);
+ $this->addElement('thead', true, false, 'Required: tr', 'Common', $cell_align);
+ $this->addElement('tfoot', true, false, 'Required: tr', 'Common', $cell_align);
}
diff --git a/tests/HTMLPurifier/HTMLModuleTest.php b/tests/HTMLPurifier/HTMLModuleTest.php
index 7f584a48..3803a715 100644
--- a/tests/HTMLPurifier/HTMLModuleTest.php
+++ b/tests/HTMLPurifier/HTMLModuleTest.php
@@ -83,6 +83,13 @@ class HTMLPurifier_HTMLModuleTest extends UnitTestCase
array('optional', 'a | b | c')
);
+ // object pass-through
+ generate_mock_once('HTMLPurifier_AttrDef');
+ $this->assertIdentical(
+ $module->parseContents(new HTMLPurifier_AttrDefMock()),
+ array(null, null)
+ );
+
}
function test_mergeInAttrIncludes() {