diff --git a/lib/Element.php b/lib/Element.php index b6efa46..a01f49c 100644 --- a/lib/Element.php +++ b/lib/Element.php @@ -128,6 +128,42 @@ class Element extends Node { return Serializer::serialize($this->innerNode); } + protected function __set_outerHTML(string $value): void { + # On setting, the following steps must be run: + # + # 1. Let parent be the context object's parent. + $parent = $this->parentNode; + $innerParent = $this->innerNode->parentNode; + + # 2. If parent is null, terminate these steps. There would be no way to obtain a + # reference to the nodes created even if the remaining steps were run. + if ($innerParent === null) { + return; + } + + # 3. If parent is a Document, throw a "NoModificationAllowedError" DOMException. + if ($innerParent instanceof \DOMDocument) { + throw new DOMException(DOMException::NO_MODIFICATION_ALLOWED); + } + + # 4. If parent is a DocumentFragment, let parent be a new Element with: + # • body as its local name, + # • The HTML namespace as its namespace, and + # • The context object's node document as its node document. + if ($innerParent instanceof \DOMDocumentFragment) { + $innerParent = $this->innerNode->ownerDocument->createElement('body'); + } + + # 5. Let fragment be the result of invoking the fragment parsing algorithm with + # the new value as markup, and parent as the context element. + $innerFragment = Parser::parseFragment($innerParent, Parser::NO_QUIRKS_MODE, $value, 'UTF-8'); + $fragment = $this->innerNode->ownerDocument->getWrapperNode($innerFragment); + + # 6. Replace the context object with fragment within the context object's + # parent. + $parent->replaceChild($fragment, $this); + } + 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 d0a080a..8fcf7bd 100644 --- a/tests/cases/TestElement.php +++ b/tests/cases/TestElement.php @@ -342,6 +342,37 @@ class TestElement extends \PHPUnit\Framework\TestCase { } + /** + * @covers \MensBeam\HTML\DOM\Element::insertAdjacent + * + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::__get_documentElement + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DOMException::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Element::insertAdjacentText + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument + * @covers \MensBeam\HTML\DOM\Node::appendChild + * @covers \MensBeam\HTML\DOM\Node::getInnerDocument + * @covers \MensBeam\HTML\DOM\Node::getInnerNode + * @covers \MensBeam\HTML\DOM\Node::getRootNode + * @covers \MensBeam\HTML\DOM\Node::postInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::key + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::set + * @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty + */ public function testMethod_insertAdjacent__errors() { $this->expectException(DOMException::class); $this->expectExceptionCode(DOMException::SYNTAX_ERROR); @@ -351,6 +382,50 @@ class TestElement extends \PHPUnit\Framework\TestCase { } + /** + * @covers \MensBeam\HTML\DOM\Element::insertAdjacentElement + * + * @covers \MensBeam\HTML\DOM\Collection::__construct + * @covers \MensBeam\HTML\DOM\Collection::item + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::__get_body + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\Document::load + * @covers \MensBeam\HTML\DOM\Document::serialize + * @covers \MensBeam\HTML\DOM\DocumentOrElement::getElementsByTagName + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Element::insertAdjacent + * @covers \MensBeam\HTML\DOM\HTMLCollection::item + * @covers \MensBeam\HTML\DOM\HTMLCollection::offsetGet + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\Node::__get_firstChild + * @covers \MensBeam\HTML\DOM\Node::__get_nextSibling + * @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument + * @covers \MensBeam\HTML\DOM\Node::__get_parentNode + * @covers \MensBeam\HTML\DOM\Node::__toString + * @covers \MensBeam\HTML\DOM\Node::appendChild + * @covers \MensBeam\HTML\DOM\Node::getInnerDocument + * @covers \MensBeam\HTML\DOM\Node::getInnerNode + * @covers \MensBeam\HTML\DOM\Node::getRootNode + * @covers \MensBeam\HTML\DOM\Node::hasChildNodes + * @covers \MensBeam\HTML\DOM\Node::insertBefore + * @covers \MensBeam\HTML\DOM\Node::postInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\ParentNode::walkInner + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::key + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::set + * @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty + */ public function testMethod_insertAdjacentElement() { $d = new Document('

Ook

', 'UTF-8'); $body = $d->body; @@ -382,6 +457,46 @@ class TestElement extends \PHPUnit\Framework\TestCase { } + /** + * @covers \MensBeam\HTML\DOM\Element::insertAdjacentText + * + * @covers \MensBeam\HTML\DOM\Collection::__construct + * @covers \MensBeam\HTML\DOM\Collection::item + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::__get_body + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\Document::load + * @covers \MensBeam\HTML\DOM\Document::serialize + * @covers \MensBeam\HTML\DOM\DocumentOrElement::getElementsByTagName + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Element::insertAdjacent + * @covers \MensBeam\HTML\DOM\HTMLCollection::item + * @covers \MensBeam\HTML\DOM\HTMLCollection::offsetGet + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\Node::__get_firstChild + * @covers \MensBeam\HTML\DOM\Node::__get_nextSibling + * @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument + * @covers \MensBeam\HTML\DOM\Node::__get_parentNode + * @covers \MensBeam\HTML\DOM\Node::__toString + * @covers \MensBeam\HTML\DOM\Node::appendChild + * @covers \MensBeam\HTML\DOM\Node::getInnerDocument + * @covers \MensBeam\HTML\DOM\Node::getInnerNode + * @covers \MensBeam\HTML\DOM\Node::hasChildNodes + * @covers \MensBeam\HTML\DOM\Node::insertBefore + * @covers \MensBeam\HTML\DOM\Node::postInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::key + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::set + * @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty + */ public function testMethod_insertAdjacentText() { $d = new Document('

Ook

', 'UTF-8'); $body = $d->body; @@ -523,6 +638,50 @@ class TestElement extends \PHPUnit\Framework\TestCase { } + /** + * @covers \MensBeam\HTML\DOM\Element::removeAttributeNode + * + * @covers \MensBeam\HTML\DOM\Attr::__get_localName + * @covers \MensBeam\HTML\DOM\Attr::__get_namespaceURI + * @covers \MensBeam\HTML\DOM\Attr::__get_ownerElement + * @covers \MensBeam\HTML\DOM\Attr::__set_value + * @covers \MensBeam\HTML\DOM\Collection::__construct + * @covers \MensBeam\HTML\DOM\Collection::__get_length + * @covers \MensBeam\HTML\DOM\Collection::count + * @covers \MensBeam\HTML\DOM\Collection::item + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::__get_documentElement + * @covers \MensBeam\HTML\DOM\Document::createAttributeNS + * @covers \MensBeam\HTML\DOM\Document::load + * @covers \MensBeam\HTML\DOM\DocumentOrElement::getElementsByTagNameNS + * @covers \MensBeam\HTML\DOM\DocumentOrElement::validateAndExtract + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Element::__get_attributes + * @covers \MensBeam\HTML\DOM\Element::__get_namespaceURI + * @covers \MensBeam\HTML\DOM\Element::getAttributeNode + * @covers \MensBeam\HTML\DOM\Element::getAttributeNodeNS + * @covers \MensBeam\HTML\DOM\Element::setAttributeNode + * @covers \MensBeam\HTML\DOM\Element::setAttributeNodeNS + * @covers \MensBeam\HTML\DOM\Element::setAttributeNS + * @covers \MensBeam\HTML\DOM\HTMLCollection::item + * @covers \MensBeam\HTML\DOM\HTMLCollection::offsetGet + * @covers \MensBeam\HTML\DOM\NamedNodeMap::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument + * @covers \MensBeam\HTML\DOM\Node::getInnerDocument + * @covers \MensBeam\HTML\DOM\Node::getInnerNode + * @covers \MensBeam\HTML\DOM\Node::hasChildNodes + * @covers \MensBeam\HTML\DOM\Inner\Document::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::key + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::set + * @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty + */ public function testMethod_removeAttributeNode() { $d = new Document('', 'UTF-8'); $svg = $d->getElementsByTagNameNS(Node::SVG_NAMESPACE, 'svg')[0]; @@ -545,6 +704,33 @@ class TestElement extends \PHPUnit\Framework\TestCase { } + /** + * @covers \MensBeam\HTML\DOM\Element::removeAttributeNode + * + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::__get_documentElement + * @covers \MensBeam\HTML\DOM\Document::createAttribute + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\DOMException::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\Node::appendChild + * @covers \MensBeam\HTML\DOM\Node::getInnerDocument + * @covers \MensBeam\HTML\DOM\Node::getInnerNode + * @covers \MensBeam\HTML\DOM\Node::getRootNode + * @covers \MensBeam\HTML\DOM\Node::postInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\Inner\Document::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::key + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::set + * @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty + */ public function testMethod_removeAttributeNode__errors() { $this->expectException(DOMException::class); $this->expectExceptionCode(DOMException::NOT_FOUND); @@ -976,6 +1162,45 @@ class TestElement extends \PHPUnit\Framework\TestCase { } + /** + * @covers \MensBeam\HTML\DOM\Element::__get_innerHTML + * @covers \MensBeam\HTML\DOM\Element::__set_innerHTML + * + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::__get_body + * @covers \MensBeam\HTML\DOM\Document::__get_documentElement + * @covers \MensBeam\HTML\DOM\Document::createDocumentFragment + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentFragment::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\HTMLTemplateElement::__construct + * @covers \MensBeam\HTML\DOM\HTMLTemplateElement::__get_content + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\Node::__get_firstChild + * @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument + * @covers \MensBeam\HTML\DOM\Node::appendChild + * @covers \MensBeam\HTML\DOM\Node::containsInner + * @covers \MensBeam\HTML\DOM\Node::getInnerDocument + * @covers \MensBeam\HTML\DOM\Node::getInnerNode + * @covers \MensBeam\HTML\DOM\Node::getRootNode + * @covers \MensBeam\HTML\DOM\Node::postInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionBugFixes + * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\Serializer::getTemplateContent + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__construct + * @covers \MensBeam\HTML\DOM\Inner\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::get + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::has + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::key + * @covers \MensBeam\HTML\DOM\Inner\NodeCache::set + * @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\Inner\Reflection::setProtectedProperties + */ public function testProperty_innerHTML() { $d = new Document(); $d->appendChild($d->createElement('html')); @@ -991,4 +1216,28 @@ class TestElement extends \PHPUnit\Framework\TestCase { $t->innerHTML = 'ook'; $this->assertSame('ook', $t->innerHTML); } + + + public function testProperty_outerHTML() { + $d = new Document(); + $d->appendChild($d->createElement('html')); + $d->documentElement->appendChild($d->createElement('body')); + $d->body->setAttribute('class', 'ook'); + $s = $d->body->appendChild($d->createElement('span')); + $s->appendChild($d->createTextNode('ook')); + $this->assertSame('ook', $d->body->outerHTML); + + $d->body->outerHTML = 'eek'; + $this->assertSame('eek', $d->body->outerHTML); + + $f = $d->createDocumentFragment(); + $div = $f->appendChild($d->createElement('div')); + $div->outerHTML = 'ook'; + $this->assertSame('ook', (string)$f); + + $div = $d->createElement('div'); + $div->appendChild($d->createTextNode('ook')); + $div->outerHTML = '
eek
'; + $this->assertSame('
ook
', (string)$div); + } } \ No newline at end of file