From 552102f7f233fb34deb286e3942f5da00ebfd22a Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Tue, 2 Oct 2007 22:50:59 +0000 Subject: [PATCH] [2.1.3] - 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 --- NEWS | 5 ++ library/HTMLPurifier/HTMLDefinition.php | 19 ++++- library/HTMLPurifier/Injector.php | 7 ++ .../HTMLPurifier/Strategy/MakeWellFormed.php | 69 ++++++++++--------- .../Strategy/MakeWellFormed_InjectorTest.php | 13 ++++ 5 files changed, 77 insertions(+), 36 deletions(-) diff --git a/NEWS b/NEWS index b8fc3d79..fe81e382 100644 --- a/NEWS +++ b/NEWS @@ -25,11 +25,16 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier - Buggy treatment of end tags of elements that have required attributes fixed (does not manifest on default tag-set) - 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 to better communicate its purpose . Error unit tests can now specify the expectation of no errors. Future iterations of the harness will be extremely strict about what errors 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 ! Implemented Object module for trusted users diff --git a/library/HTMLPurifier/HTMLDefinition.php b/library/HTMLPurifier/HTMLDefinition.php index fe6bd141..e13e0c62 100644 --- a/library/HTMLPurifier/HTMLDefinition.php +++ b/library/HTMLPurifier/HTMLDefinition.php @@ -236,13 +236,26 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition /** * Adds a custom element to your HTML definition * @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(); // assume that if the user is calling this, the element // 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; } /** diff --git a/library/HTMLPurifier/Injector.php b/library/HTMLPurifier/Injector.php index 261a2a80..3b847097 100644 --- a/library/HTMLPurifier/Injector.php +++ b/library/HTMLPurifier/Injector.php @@ -110,5 +110,12 @@ class HTMLPurifier_Injector */ 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) {} + + } diff --git a/library/HTMLPurifier/Strategy/MakeWellFormed.php b/library/HTMLPurifier/Strategy/MakeWellFormed.php index 930bfca1..4b6f498f 100644 --- a/library/HTMLPurifier/Strategy/MakeWellFormed.php +++ b/library/HTMLPurifier/Strategy/MakeWellFormed.php @@ -36,28 +36,23 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $definition = $config->getHTMLDefinition(); - // CurrentNesting - $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 + // local variables $result = array(); - $this->outputTokens =& $result; - - // %Core.EscapeInvalidTags - $escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags'); $generator = new HTMLPurifier_Generator(); - + $escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags'); $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 -- $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); } + // 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 -- $token = false; @@ -116,7 +115,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy if ($token->type === 'text') { // injector handler code; duplicated for performance reasons foreach ($this->injectors as $i => $x) { - if (!$x->skip) $x->handleText($token); + if (!$x->skip) $this->injectors[$i]->handleText($token); if (is_array($token)) { $this->currentInjector = $i; break; @@ -174,7 +173,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy // injector handler code; duplicated for performance reasons if ($ok) { foreach ($this->injectors as $i => $x) { - if (!$x->skip) $x->handleElement($token); + if (!$x->skip) $this->injectors[$i]->handleElement($token); if (is_array($token)) { $this->currentInjector = $i; break; @@ -204,6 +203,9 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $current_parent = array_pop($this->currentNesting); if ($current_parent->name == $token->name) { $result[] = $token; + foreach ($this->injectors as $i => $x) { + $this->injectors[$i]->notifyEnd($token); + } continue; } @@ -240,16 +242,16 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy // okay, we found it, close all the skipped tags // note that skipped tags contains the element we need closed - $size = count($skipped_tags); - for ($i = $size - 1; $i > 0; $i--) { - if ($e && !isset($skipped_tags[$i]->armor['MakeWellFormed_TagClosedError'])) { + for ($i = count($skipped_tags) - 1; $i >= 0; $i--) { + if ($i && $e && !isset($skipped_tags[$i]->armor['MakeWellFormed_TagClosedError'])) { $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'); @@ -257,17 +259,18 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $context->destroy('InputIndex'); $context->destroy('CurrentToken'); - // we're at the end now, fix all still unclosed tags - // not using processToken() because at this point we don't - // care about current nesting + // we're at the end now, fix all still unclosed tags (this is + // duplicated from the end of the loop with some slight modifications) + // not using $skipped_tags since it would invariably be all of them if (!empty($this->currentNesting)) { - $size = count($this->currentNesting); - for ($i = $size - 1; $i >= 0; $i--) { + for ($i = count($this->currentNesting) - 1; $i >= 0; $i--) { if ($e && !isset($this->currentNesting[$i]->armor['MakeWellFormed_TagClosedError'])) { $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $this->currentNesting[$i]); } - $result[] = - new HTMLPurifier_Token_End($this->currentNesting[$i]->name); + $result[] = $new_token = new HTMLPurifier_Token_End($this->currentNesting[$i]->name); + foreach ($this->injectors as $j => $x) { // $j, not $i!!! + $this->injectors[$j]->notifyEnd($new_token); + } } } diff --git a/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php b/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php index 04756201..0249d5ef 100644 --- a/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php +++ b/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php @@ -11,6 +11,19 @@ class HTMLPurifier_Strategy_MakeWellFormed_InjectorTest extends HTMLPurifier_Str $this->obj = new HTMLPurifier_Strategy_MakeWellFormed(); $this->config->set('AutoFormat', 'AutoParagraph', 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('asdf', 'asdf'); } function testOnlyAutoParagraph() {