From 70515dd48f2299c0f623df69e92f2d54688e7f12 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Wed, 1 Oct 2008 15:40:31 -0400 Subject: [PATCH] Increase test coverage, and modify handleEnd behavior to only see correct tokens. Previously, handleEnd was called for any end tag, except ones that were obviously spurious because there were no parent tags. Now, it is only called for end tags that are "approved." If an injector operates on the end tag, we automatically punt. There may be some optimizations that could be made to this procedure, but for now it's much more consistent. Signed-off-by: Edward Z. Yang --- .../HTMLPurifier/Strategy/MakeWellFormed.php | 24 +++++++----- .../MakeWellFormed/EndInsertInjector.php | 16 ++++++++ .../MakeWellFormed/EndInsertInjectorTest.php | 37 +++++++++++++++++++ .../MakeWellFormed/EndRewindInjector.php | 27 ++++++++++++++ .../MakeWellFormed/EndRewindInjectorTest.php | 32 ++++++++++++++++ .../Strategy/MakeWellFormed/SkipInjector.php | 10 +++++ .../MakeWellFormed/SkipInjectorTest.php | 26 +++++++++++++ .../Strategy/MakeWellFormed_InjectorTest.php | 4 ++ 8 files changed, 166 insertions(+), 10 deletions(-) create mode 100644 tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjector.php create mode 100644 tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjectorTest.php create mode 100644 tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjector.php create mode 100644 tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjectorTest.php create mode 100644 tests/HTMLPurifier/Strategy/MakeWellFormed/SkipInjector.php create mode 100644 tests/HTMLPurifier/Strategy/MakeWellFormed/SkipInjectorTest.php diff --git a/library/HTMLPurifier/Strategy/MakeWellFormed.php b/library/HTMLPurifier/Strategy/MakeWellFormed.php index 24a14bff..0698c4e4 100644 --- a/library/HTMLPurifier/Strategy/MakeWellFormed.php +++ b/library/HTMLPurifier/Strategy/MakeWellFormed.php @@ -274,20 +274,23 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $reprocess = true; continue; } - foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleEnd($token); - $this->processToken($token, $i); - $reprocess = true; - break; - } - if ($reprocess) continue; - // first, check for the simplest case: everything closes neatly + // first, check for the simplest case: everything closes neatly. + // Eventually, everything passes through here; if there are problems + // we modify the input stream accordingly and then punt, so that + // the tokens get processed again. $current_parent = array_pop($this->stack); if ($current_parent->name == $token->name) { $token->start = $current_parent; + foreach ($this->injectors as $i => $injector) { + if (isset($token->skip[$i])) continue; + if ($token->rewind !== null && $token->rewind !== $i) continue; + $injector->handleEnd($token); + $this->processToken($token, $i); + $this->stack[] = $current_parent; + $reprocess = true; + break; + } continue; } @@ -343,6 +346,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $this->insertBefore($new_token); } $reprocess = true; + continue; } $context->destroy('CurrentNesting'); diff --git a/tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjector.php b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjector.php new file mode 100644 index 00000000..0673aee9 --- /dev/null +++ b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjector.php @@ -0,0 +1,16 @@ +name == 'div') return; + $token = array( + new HTMLPurifier_Token_Start('b'), + new HTMLPurifier_Token_Text('Comment'), + new HTMLPurifier_Token_End('b'), + $token + ); + } +} diff --git a/tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjectorTest.php b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjectorTest.php new file mode 100644 index 00000000..ea0025a2 --- /dev/null +++ b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndInsertInjectorTest.php @@ -0,0 +1,37 @@ +obj = new HTMLPurifier_Strategy_MakeWellFormed(); + $this->config->set('AutoFormat', 'Custom', array( + new HTMLPurifier_Strategy_MakeWellFormed_EndInsertInjector() + )); + } + function testEmpty() { + $this->assertResult(''); + } + function testNormal() { + $this->assertResult('Foo', 'FooComment'); + } + function testEndOfDocumentProcessing() { + $this->assertResult('Foo', 'FooComment'); + } + function testDoubleEndOfDocumentProcessing() { + $this->assertResult('Foo', 'FooCommentComment'); + } + function testEndOfNodeProcessing() { + $this->assertResult('
Foo
', '
FooComment
'); + } + function testEmptyToStartEndProcessing() { + $this->assertResult('', 'Comment'); + } + function testSpuriousEndTag() { + $this->assertResult('', ''); + } + function testLessButStillSpuriousEndTag() { + $this->assertResult('
', '
'); + } +} + diff --git a/tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjector.php b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjector.php new file mode 100644 index 00000000..9352c1e4 --- /dev/null +++ b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjector.php @@ -0,0 +1,27 @@ +_InjectorTest_EndRewindInjector_delete)) { + $token = false; + } + } + public function handleText(&$token) { + $token = false; + } + public function handleEnd(&$token) { + $i = null; + if ( + $this->backward($i, $prev) && + $prev instanceof HTMLPurifier_Token_Start && + $prev->name == 'span' + ) { + $token = false; + $prev->_InjectorTest_EndRewindInjector_delete = true; + $this->rewind($i); + } + } +} diff --git a/tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjectorTest.php b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjectorTest.php new file mode 100644 index 00000000..ef906e84 --- /dev/null +++ b/tests/HTMLPurifier/Strategy/MakeWellFormed/EndRewindInjectorTest.php @@ -0,0 +1,32 @@ +obj = new HTMLPurifier_Strategy_MakeWellFormed(); + $this->config->set('AutoFormat', 'Custom', array( + new HTMLPurifier_Strategy_MakeWellFormed_EndRewindInjector() + )); + } + function testBasic() { + $this->assertResult(''); + } + function testFunction() { + $this->assertResult('asdf',''); + } + function testFailedFunction() { + $this->assertResult('asdasdfasdf',''); + } + function testPadded() { + $this->assertResult('asdf',''); + } + function testDoubled() { + $this->config->set('AutoFormat', 'Custom', array( + new HTMLPurifier_Strategy_MakeWellFormed_EndRewindInjector(), + new HTMLPurifier_Strategy_MakeWellFormed_EndRewindInjector(), + )); + $this->assertResult('asdf', ''); + } +} + diff --git a/tests/HTMLPurifier/Strategy/MakeWellFormed/SkipInjector.php b/tests/HTMLPurifier/Strategy/MakeWellFormed/SkipInjector.php new file mode 100644 index 00000000..9ee8e826 --- /dev/null +++ b/tests/HTMLPurifier/Strategy/MakeWellFormed/SkipInjector.php @@ -0,0 +1,10 @@ +obj = new HTMLPurifier_Strategy_MakeWellFormed(); + $this->config->set('AutoFormat', 'Custom', array( + new HTMLPurifier_Strategy_MakeWellFormed_SkipInjector() + )); + } + function testEmpty() { + $this->assertResult(''); + } + function testMultiply() { + $this->assertResult('
', '

'); + } + function testMultiplyMultiply() { + $this->config->set('AutoFormat', 'Custom', array( + new HTMLPurifier_Strategy_MakeWellFormed_SkipInjector(), + new HTMLPurifier_Strategy_MakeWellFormed_SkipInjector() + )); + $this->assertResult('
', '



'); + } +} + diff --git a/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php b/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php index 80685727..212b2d4c 100644 --- a/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php +++ b/tests/HTMLPurifier/Strategy/MakeWellFormed_InjectorTest.php @@ -16,9 +16,13 @@ class HTMLPurifier_Strategy_MakeWellFormed_InjectorTest extends HTMLPurifier_Str $mock = new HTMLPurifier_InjectorMock(); $b = new HTMLPurifier_Token_End('b'); $b->skip = array(0 => true); + $b->start = new HTMLPurifier_Token_Start('b'); + $b->start->skip = array(0 => true, 1 => true); $mock->expectAt(0, 'handleEnd', array($b)); $i = new HTMLPurifier_Token_End('i'); + $i->start = new HTMLPurifier_Token_Start('i'); $i->skip = array(0 => true); + $i->start->skip = array(0 => true, 1 => true); $mock->expectAt(1, 'handleEnd', array($i)); $mock->expectCallCount('handleEnd', 2); $mock->setReturnValue('getRewind', false);