mirror of
https://github.com/ezyang/htmlpurifier.git
synced 2024-11-09 23:28:42 +00:00
Fix chameleon behavior with ins and del.
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@145 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
parent
19081ffdf2
commit
aa249be067
@ -175,4 +175,32 @@ class HTMLPurifier_ChildDef_Empty extends HTMLPurifier_ChildDef
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HTMLPurifier_ChildDef_Chameleon extends HTMLPurifier_ChildDef
|
||||||
|
{
|
||||||
|
|
||||||
|
var $inline;
|
||||||
|
var $block;
|
||||||
|
|
||||||
|
function HTMLPurifier_ChildDef_Chameleon($inline, $block) {
|
||||||
|
$this->inline = new HTMLPurifier_ChildDef_Optional($inline);
|
||||||
|
$this->block = new HTMLPurifier_ChildDef_Optional($block);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateChildren($tokens_of_children, $context) {
|
||||||
|
switch ($context) {
|
||||||
|
case 'unknown':
|
||||||
|
case 'inline':
|
||||||
|
$result = $this->inline->validateChildren($tokens_of_children);
|
||||||
|
break;
|
||||||
|
case 'block':
|
||||||
|
$result = $this->block->validateChildren($tokens_of_children);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
trigger_error('Invalid context', E_USER_ERROR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
@ -105,15 +105,15 @@ class HTMLPurifier_Definition
|
|||||||
$e_misc = "$e_misc_inline";
|
$e_misc = "$e_misc_inline";
|
||||||
$e_inline = "a | $e_special | $e_fontstyle | $e_phrase".
|
$e_inline = "a | $e_special | $e_fontstyle | $e_phrase".
|
||||||
" | $e_inline_forms";
|
" | $e_inline_forms";
|
||||||
|
$e__inline = "#PCDATA | $e_inline | $e_misc_inline";
|
||||||
// note the casing
|
// note the casing
|
||||||
$e_Inline = new HTMLPurifier_ChildDef_Optional("#PCDATA | $e_inline".
|
$e_Inline = new HTMLPurifier_ChildDef_Optional($e__inline);
|
||||||
" | $e_misc_inline");
|
|
||||||
$e_heading = 'h1|h2|h3|h4|h5|h6';
|
$e_heading = 'h1|h2|h3|h4|h5|h6';
|
||||||
$e_lists = 'ul | ol | dl';
|
$e_lists = 'ul | ol | dl';
|
||||||
$e_blocktext = 'pre | hr | blockquote | address';
|
$e_blocktext = 'pre | hr | blockquote | address';
|
||||||
$e_block = "p | $e_heading | div | $e_lists | $e_blocktext | table";
|
$e_block = "p | $e_heading | div | $e_lists | $e_blocktext | table";
|
||||||
$e_Flow = new HTMLPurifier_ChildDef_Optional("#PCDATA | $e_block".
|
$e__flow = "#PCDATA | $e_block | $e_inline | $e_misc";
|
||||||
" | $e_inline | $e_misc");
|
$e_Flow = new HTMLPurifier_ChildDef_Optional($e__flow);
|
||||||
$e_a_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | $e_special".
|
$e_a_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | $e_special".
|
||||||
" | $e_fontstyle | $e_phrase | $e_inline_forms | $e_misc_inline");
|
" | $e_fontstyle | $e_phrase | $e_inline_forms | $e_misc_inline");
|
||||||
$e_pre_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | a".
|
$e_pre_content = new HTMLPurifier_ChildDef_Optional("#PCDATA | a".
|
||||||
@ -123,7 +123,8 @@ class HTMLPurifier_Definition
|
|||||||
$e_form_button_content = new HTMLPurifier_ChildDef_Optional(''); // unused
|
$e_form_button_content = new HTMLPurifier_ChildDef_Optional(''); // unused
|
||||||
|
|
||||||
$this->info['ins']->child =
|
$this->info['ins']->child =
|
||||||
$this->info['del']->child =
|
$this->info['del']->child = new HTMLPurifier_ChildDef_Chameleon($e__inline, $e__flow);
|
||||||
|
|
||||||
$this->info['blockquote']->child=
|
$this->info['blockquote']->child=
|
||||||
$this->info['dd']->child =
|
$this->info['dd']->child =
|
||||||
$this->info['li']->child =
|
$this->info['li']->child =
|
||||||
@ -193,6 +194,20 @@ class HTMLPurifier_Definition
|
|||||||
$this->info['th']->child = $e_Flow;
|
$this->info['th']->child = $e_Flow;
|
||||||
$this->info['td']->child = $e_Flow;
|
$this->info['td']->child = $e_Flow;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// info[]->type : defines the type of the element (block or inline)
|
||||||
|
|
||||||
|
// reuses $e_Inline and $e_block
|
||||||
|
|
||||||
|
foreach ($e_Inline->elements as $name) {
|
||||||
|
$this->info[$name]->type = 'inline';
|
||||||
|
}
|
||||||
|
|
||||||
|
$e_Block = new HTMLPurifier_ChildDef_Optional($e_block);
|
||||||
|
foreach ($e_Block->elements as $name) {
|
||||||
|
$this->info[$name]->type = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// info[]->attr : defines allowed attributes for elements
|
// info[]->attr : defines allowed attributes for elements
|
||||||
|
|
||||||
@ -249,6 +264,7 @@ class HTMLPurifier_ElementDef
|
|||||||
var $attr = array();
|
var $attr = array();
|
||||||
var $auto_close = array();
|
var $auto_close = array();
|
||||||
var $child;
|
var $child;
|
||||||
|
var $type = 'unknown';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +47,27 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
// $i is index of start token
|
// $i is index of start token
|
||||||
// $j is index of end token
|
// $j is index of end token
|
||||||
|
|
||||||
|
// calculate parent information
|
||||||
|
if ($count = count($stack)) {
|
||||||
|
$parent_index = $stack[$count-1];
|
||||||
|
$parent_name = $tokens[$parent_index]->name;
|
||||||
|
$parent_def = $this->definition->info[$parent_name];
|
||||||
|
} else {
|
||||||
|
$parent_index = $parent_name = $parent_def = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate context
|
||||||
|
if (isset($parent_def)) {
|
||||||
|
$context = $parent_def->type;
|
||||||
|
} else {
|
||||||
|
$context = 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
// DEFINITION CALL
|
// DEFINITION CALL
|
||||||
$child_def = $this->definition->info[$tokens[$i]->name]->child;
|
$child_def = $this->definition->info[$tokens[$i]->name]->child;
|
||||||
|
|
||||||
// have DTD child def validate children
|
// have DTD child def validate children
|
||||||
$result = $child_def->validateChildren($child_tokens);
|
$result = $child_def->validateChildren($child_tokens, $context);
|
||||||
|
|
||||||
// process result
|
// process result
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
@ -79,10 +94,6 @@ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
|||||||
// current node is now the next possible start node
|
// current node is now the next possible start node
|
||||||
// unless it turns out that we need to do a double-check
|
// unless it turns out that we need to do a double-check
|
||||||
|
|
||||||
$parent_index = $stack[count($stack)-1];
|
|
||||||
$parent_name = $tokens[$parent_index]->name;
|
|
||||||
$parent_def = $this->definition->info[$parent_name];
|
|
||||||
|
|
||||||
if (!$parent_def->child->allow_empty) {
|
if (!$parent_def->child->allow_empty) {
|
||||||
// we need to do a double-check
|
// we need to do a double-check
|
||||||
$i = $parent_index;
|
$i = $parent_index;
|
||||||
|
@ -16,15 +16,21 @@ class HTMLPurifier_ChildDefTest extends UnitTestCase
|
|||||||
parent::UnitTestCase();
|
parent::UnitTestCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertSeries($inputs, $expect, $def) {
|
function assertSeries($inputs, $expect, $def, $context = array()) {
|
||||||
foreach ($inputs as $i => $input) {
|
foreach ($inputs as $i => $input) {
|
||||||
$tokens = $this->lex->tokenizeHTML($input);
|
$tokens = $this->lex->tokenizeHTML($input);
|
||||||
$result = $def->validateChildren($tokens);
|
|
||||||
|
if (isset($context[$i])) {
|
||||||
|
$result = $def->validateChildren($tokens, $context[$i]);
|
||||||
|
} else {
|
||||||
|
$result = $def->validateChildren($tokens);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_bool($expect[$i])) {
|
if (is_bool($expect[$i])) {
|
||||||
$this->assertIdentical($expect[$i], $result);
|
$this->assertIdentical($expect[$i], $result);
|
||||||
} else {
|
} else {
|
||||||
$result_html = $this->gen->generateFromTokens($result);
|
$result_html = $this->gen->generateFromTokens($result);
|
||||||
$this->assertEqual($expect[$i], $result_html);
|
$this->assertEqual($expect[$i], $result_html, "Test $i: %s");
|
||||||
paintIf($result_html, $result_html != $expect[$i]);
|
paintIf($result_html, $result_html != $expect[$i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,6 +130,29 @@ class HTMLPurifier_ChildDefTest extends UnitTestCase
|
|||||||
$this->assertSeries($inputs, $expect, $def);
|
$this->assertSeries($inputs, $expect, $def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test_chameleon() {
|
||||||
|
|
||||||
|
$def = new HTMLPurifier_ChildDef_Chameleon(
|
||||||
|
'b | i', // allowed only when in inline context
|
||||||
|
'b | i | div' // allowed only when in block context
|
||||||
|
);
|
||||||
|
|
||||||
|
$inputs[0] = '<b>Allowed.</b>';
|
||||||
|
$expect[0] = true;
|
||||||
|
$context[0] = 'inline';
|
||||||
|
|
||||||
|
$inputs[1] = '<div>Not allowed.</div>';
|
||||||
|
$expect[1] = '';
|
||||||
|
$context[1] = 'inline';
|
||||||
|
|
||||||
|
$inputs[2] = '<div>Allowed.</div>';
|
||||||
|
$expect[2] = true;
|
||||||
|
$context[2] = 'block';
|
||||||
|
|
||||||
|
$this->assertSeries($inputs, $expect, $def, $context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
@ -61,6 +61,10 @@ class HTMLPurifier_Strategy_FixNestingTest
|
|||||||
$inputs[10] = '<table></table><table></table>';
|
$inputs[10] = '<table></table><table></table>';
|
||||||
$expect[10] = '';
|
$expect[10] = '';
|
||||||
|
|
||||||
|
// block in inline ins not allowed
|
||||||
|
$inputs[11] = '<span><ins><div>Not allowed!</div></ins></span>';
|
||||||
|
$expect[11] = '<span><ins><div>Not allowed!</div></ins></span>';
|
||||||
|
|
||||||
$this->assertStrategyWorks($strategy, $inputs, $expect);
|
$this->assertStrategyWorks($strategy, $inputs, $expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user