0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-09-19 10:45:18 +00:00
- HTMLDefinition->addElement now returns a reference to the created element object, as implied by the documentation
. Extend Injector hooks to allow for more powerful injector routines
. HTMLDefinition->addBlankElement created, as according to the HTMLModule method

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1425 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang 2007-10-02 22:50:59 +00:00
parent f5371bbad4
commit 552102f7f2
5 changed files with 77 additions and 36 deletions

5
NEWS
View File

@ -25,11 +25,16 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
- Buggy treatment of end tags of elements that have required attributes - Buggy treatment of end tags of elements that have required attributes
fixed (does not manifest on default tag-set) fixed (does not manifest on default tag-set)
- Spurious internal content reorganization error suppressed - Spurious internal content reorganization error suppressed
- HTMLDefinition->addElement now returns a reference to the created
element object, as implied by the documentation
. %Core.AcceptFullDocuments renamed to %Core.ConvertDocumentToFragment . %Core.AcceptFullDocuments renamed to %Core.ConvertDocumentToFragment
to better communicate its purpose to better communicate its purpose
. Error unit tests can now specify the expectation of no errors. Future . Error unit tests can now specify the expectation of no errors. Future
iterations of the harness will be extremely strict about what errors iterations of the harness will be extremely strict about what errors
are allowed are allowed
. Extend Injector hooks to allow for more powerful injector routines
. HTMLDefinition->addBlankElement created, as according to the HTMLModule
method
2.1.2, released 2007-09-03 2.1.2, released 2007-09-03
! Implemented Object module for trusted users ! Implemented Object module for trusted users

View File

@ -236,13 +236,26 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
/** /**
* Adds a custom element to your HTML definition * Adds a custom element to your HTML definition
* @note See HTMLPurifier_HTMLModule::addElement for detailed * @note See HTMLPurifier_HTMLModule::addElement for detailed
* parameter descriptions. * parameter and return value descriptions.
*/ */
function addElement($element_name, $type, $contents, $attr_collections, $attributes) { function &addElement($element_name, $type, $contents, $attr_collections, $attributes) {
$module =& $this->getAnonymousModule(); $module =& $this->getAnonymousModule();
// assume that if the user is calling this, the element // assume that if the user is calling this, the element
// is safe. This may not be a good idea // is safe. This may not be a good idea
$module->addElement($element_name, true, $type, $contents, $attr_collections, $attributes); $element =& $module->addElement($element_name, true, $type, $contents, $attr_collections, $attributes);
return $element;
}
/**
* Adds a blank element to your HTML definition, for overriding
* existing behavior
* @note See HTMLPurifier_HTMLModule::addBlankElement for detailed
* parameter and return value descriptions.
*/
function &addBlankElement($element_name) {
$module =& $this->getAnonymousModule();
$element =& $module->addBlankElement($element_name);
return $element;
} }
/** /**

View File

@ -110,5 +110,12 @@ class HTMLPurifier_Injector
*/ */
function handleElement(&$token) {} function handleElement(&$token) {}
/**
* Notifier that is called when an end token is processed
* @note This differs from handlers in that the token is read-only
*/
function notifyEnd($token) {}
} }

View File

@ -36,28 +36,23 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$definition = $config->getHTMLDefinition(); $definition = $config->getHTMLDefinition();
// CurrentNesting // local variables
$this->currentNesting = array();
$context->register('CurrentNesting', $this->currentNesting);
// InputIndex
$this->inputIndex = false;
$context->register('InputIndex', $this->inputIndex);
// InputTokens
$context->register('InputTokens', $tokens);
$this->inputTokens =& $tokens;
// OutputTokens
$result = array(); $result = array();
$this->outputTokens =& $result;
// %Core.EscapeInvalidTags
$escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags');
$generator = new HTMLPurifier_Generator(); $generator = new HTMLPurifier_Generator();
$escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags');
$e =& $context->get('ErrorCollector', true); $e =& $context->get('ErrorCollector', true);
// member variables
$this->currentNesting = array();
$this->inputIndex = false;
$this->inputTokens =& $tokens;
$this->outputTokens =& $result;
// context variables
$context->register('CurrentNesting', $this->currentNesting);
$context->register('InputIndex', $this->inputIndex);
$context->register('InputTokens', $tokens);
// -- begin INJECTOR -- // -- begin INJECTOR --
$this->injectors = array(); $this->injectors = array();
@ -95,6 +90,10 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
trigger_error("Cannot enable $name injector because $error is not allowed", E_USER_WARNING); trigger_error("Cannot enable $name injector because $error is not allowed", E_USER_WARNING);
} }
// warning: most foreach loops follow the convention $i => $x.
// be sure, for PHP4 compatibility, to only perform write operations
// directly referencing the object using $i: $x is only safe for reads
// -- end INJECTOR -- // -- end INJECTOR --
$token = false; $token = false;
@ -116,7 +115,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
if ($token->type === 'text') { if ($token->type === 'text') {
// injector handler code; duplicated for performance reasons // injector handler code; duplicated for performance reasons
foreach ($this->injectors as $i => $x) { foreach ($this->injectors as $i => $x) {
if (!$x->skip) $x->handleText($token); if (!$x->skip) $this->injectors[$i]->handleText($token);
if (is_array($token)) { if (is_array($token)) {
$this->currentInjector = $i; $this->currentInjector = $i;
break; break;
@ -174,7 +173,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// injector handler code; duplicated for performance reasons // injector handler code; duplicated for performance reasons
if ($ok) { if ($ok) {
foreach ($this->injectors as $i => $x) { foreach ($this->injectors as $i => $x) {
if (!$x->skip) $x->handleElement($token); if (!$x->skip) $this->injectors[$i]->handleElement($token);
if (is_array($token)) { if (is_array($token)) {
$this->currentInjector = $i; $this->currentInjector = $i;
break; break;
@ -204,6 +203,9 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$current_parent = array_pop($this->currentNesting); $current_parent = array_pop($this->currentNesting);
if ($current_parent->name == $token->name) { if ($current_parent->name == $token->name) {
$result[] = $token; $result[] = $token;
foreach ($this->injectors as $i => $x) {
$this->injectors[$i]->notifyEnd($token);
}
continue; continue;
} }
@ -240,16 +242,16 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
// okay, we found it, close all the skipped tags // okay, we found it, close all the skipped tags
// note that skipped tags contains the element we need closed // note that skipped tags contains the element we need closed
$size = count($skipped_tags); for ($i = count($skipped_tags) - 1; $i >= 0; $i--) {
for ($i = $size - 1; $i > 0; $i--) { if ($i && $e && !isset($skipped_tags[$i]->armor['MakeWellFormed_TagClosedError'])) {
if ($e && !isset($skipped_tags[$i]->armor['MakeWellFormed_TagClosedError'])) {
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$i]); $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$i]);
} }
$result[] = new HTMLPurifier_Token_End($skipped_tags[$i]->name); $result[] = $new_token = new HTMLPurifier_Token_End($skipped_tags[$i]->name);
foreach ($this->injectors as $j => $x) { // $j, not $i!!!
$this->injectors[$j]->notifyEnd($new_token);
}
} }
$result[] = new HTMLPurifier_Token_End($skipped_tags[$i]->name);
} }
$context->destroy('CurrentNesting'); $context->destroy('CurrentNesting');
@ -257,17 +259,18 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$context->destroy('InputIndex'); $context->destroy('InputIndex');
$context->destroy('CurrentToken'); $context->destroy('CurrentToken');
// we're at the end now, fix all still unclosed tags // we're at the end now, fix all still unclosed tags (this is
// not using processToken() because at this point we don't // duplicated from the end of the loop with some slight modifications)
// care about current nesting // not using $skipped_tags since it would invariably be all of them
if (!empty($this->currentNesting)) { if (!empty($this->currentNesting)) {
$size = count($this->currentNesting); for ($i = count($this->currentNesting) - 1; $i >= 0; $i--) {
for ($i = $size - 1; $i >= 0; $i--) {
if ($e && !isset($this->currentNesting[$i]->armor['MakeWellFormed_TagClosedError'])) { if ($e && !isset($this->currentNesting[$i]->armor['MakeWellFormed_TagClosedError'])) {
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $this->currentNesting[$i]); $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $this->currentNesting[$i]);
} }
$result[] = $result[] = $new_token = new HTMLPurifier_Token_End($this->currentNesting[$i]->name);
new HTMLPurifier_Token_End($this->currentNesting[$i]->name); foreach ($this->injectors as $j => $x) { // $j, not $i!!!
$this->injectors[$j]->notifyEnd($new_token);
}
} }
} }

View File

@ -11,6 +11,19 @@ class HTMLPurifier_Strategy_MakeWellFormed_InjectorTest extends HTMLPurifier_Str
$this->obj = new HTMLPurifier_Strategy_MakeWellFormed(); $this->obj = new HTMLPurifier_Strategy_MakeWellFormed();
$this->config->set('AutoFormat', 'AutoParagraph', true); $this->config->set('AutoFormat', 'AutoParagraph', true);
$this->config->set('AutoFormat', 'Linkify', true); $this->config->set('AutoFormat', 'Linkify', true);
generate_mock_once('HTMLPurifier_Injector');
}
function testEndNotification() {
$mock = new HTMLPurifier_InjectorMock();
$mock->skip = false;
$mock->expectAt(0, 'notifyEnd', array(new HTMLPurifier_Token_End('b')));
$mock->expectAt(1, 'notifyEnd', array(new HTMLPurifier_Token_End('i')));
$mock->expectCallCount('notifyEnd', 2);
$this->config->set('AutoFormat', 'AutoParagraph', false);
$this->config->set('AutoFormat', 'Linkify', false);
$this->config->set('AutoFormat', 'Custom', array($mock));
$this->assertResult('<i><b>asdf</b>', '<i><b>asdf</b></i>');
} }
function testOnlyAutoParagraph() { function testOnlyAutoParagraph() {