From ee01a1ab1d7ea57272cb9e4e9ea1d23ed6c293c6 Mon Sep 17 00:00:00 2001 From: Dustin Wilson Date: Thu, 25 Nov 2021 23:57:59 -0600 Subject: [PATCH] Started adding Element::innerHTML and Element::outerHTML --- lib/Element.php | 57 +++++++++++++++++++++++++++++++++++++ tests/cases/TestElement.php | 17 +++++++++++ 2 files changed, 74 insertions(+) diff --git a/lib/Element.php b/lib/Element.php index 6911d2c..b6efa46 100644 --- a/lib/Element.php +++ b/lib/Element.php @@ -57,6 +57,53 @@ class Element extends Node { $this->setAttribute('id', $value); } + protected function __get_innerHTML(): string { + # On getting, return the result of invoking the fragment serializing algorithm + # on the context object providing true for the require well-formed flag (this + # might throw an exception instead of returning a string). + // TODO: If adding better XML support this should require XML well-formed + // serialization here. + return Serializer::serializeInner($this->innerNode); + } + + protected function __set_innerHTML(string $value): void { + # On setting, these steps must be run: + + # 1. Let context element be the context object's host if the context object is a + # ShadowRoot object, or the context object otherwise. + // There's no scripting in this implementation and therefore no shadow root + // object. + $context = $this; + $innerContext = $this->innerNode; + + # 2. Let fragment be the result of invoking the fragment parsing algorithm with + # the new value as markup, and with context element. + $innerFragment = Parser::parseFragment($innerContext, Parser::NO_QUIRKS_MODE, $value, 'UTF-8'); + $fragment = $innerContext->ownerDocument->getWrapperNode($innerFragment); + + # 3. If the context object is a template element, then let context object be the + # template's template contents (a DocumentFragment). + # NOTE: Setting innerHTML on a template element will replace all the nodes in + # its template contents (template.content) rather than its children. + if ($this instanceof HTMLTemplateElement) { + $context = $this->content; + $innerContext = $this->getInnerNode($context); + } + + # 4. Replace all with fragment within the context object. + while ($innerContext->hasChildNodes()) { + $innerContext->removeChild($innerContext->firstChild); + } + + if (!$this instanceof HTMLTemplateElement) { + $context->appendChild($fragment); + } else { + while ($innerFragment->hasChildNodes()) { + $context->appendChild($fragment->firstChild); + } + } + } + protected function __get_localName(): ?string { // PHP's DOM does this correctly already. return $this->innerNode->localName; @@ -71,6 +118,16 @@ class Element extends Node { return (!$this->ownerDocument instanceof XMLDocument && $namespace === null) ? self::HTML_NAMESPACE : $namespace; } + protected function __get_outerHTML(): string { + # On getting, return the result of invoking the fragment serializing algorithm + # on a fictional node whose only child is the context object providing true + # for the require well-formed flag (this might throw an exception instead of + # returning a string). + // TODO: If adding better XML support this should require XML well-formed + // serialization here. + return Serializer::serialize($this->innerNode); + } + protected function __get_prefix(): ?string { $prefix = $this->innerNode->prefix; return ($prefix !== '') ? $prefix : null; diff --git a/tests/cases/TestElement.php b/tests/cases/TestElement.php index c285665..d0a080a 100644 --- a/tests/cases/TestElement.php +++ b/tests/cases/TestElement.php @@ -974,4 +974,21 @@ class TestElement extends \PHPUnit\Framework\TestCase { $documentElement->id = 'ook'; $this->assertSame('ook', $documentElement->id); } + + + public function testProperty_innerHTML() { + $d = new Document(); + $d->appendChild($d->createElement('html')); + $d->documentElement->appendChild($d->createElement('body')); + $s = $d->body->appendChild($d->createElement('span')); + $s->appendChild($d->createTextNode('ook')); + $this->assertSame('ook', $d->body->innerHTML); + + $d->body->innerHTML = '
eek
'; + $this->assertSame('
eek
', $d->body->innerHTML); + + $t = $d->body->appendChild($d->createElement('template')); + $t->innerHTML = 'ook'; + $this->assertSame('ook', $t->innerHTML); + } } \ No newline at end of file