Compare commits

...

2 Commits

  1. 38
      lib/DOM/Document.php
  2. 27
      lib/DOM/Element.php
  3. 18
      lib/TreeBuilder.php
  4. 87
      tests/cases/TestDOM.php

38
lib/DOM/Document.php

@ -36,19 +36,7 @@ class Document extends \DOMDocument {
}
public function createAttribute($name) {
// Normalize the attribute name per modern DOM specifications.
$name = strtolower(trim($name));
try {
return parent::createAttribute($name);
} catch (\DOMException $e) {
// The element name is invalid for XML
// Replace any offending characters with "UHHHHHH" where H are the
// uppercase hexadecimal digits of the character's code point
$this->mangledAttributes = true;
$name = $this->coerceName($name);
return parent::createAttribute($name);
}
return $this->createAttributeNS(null, $name);
}
public function createAttributeNS($namespaceURI, $qualifiedName) {
@ -66,32 +54,12 @@ class Document extends \DOMDocument {
// uppercase hexadecimal digits of the character's code point
$this->mangledAttributes = true;
$qualifiedName = $this->coerceName($qualifiedName);
return parent::createAttributeNS($namespaceURI, $qualifiedName);
return $this->createAttributeNS($namespaceURI, $qualifiedName);
}
}
public function createElement($name, $value = "") {
// Normalize the element name per modern DOM specifications.
$name = strtolower(trim($name));
try {
if ($name !== 'template') {
$e = parent::createElementNS(null, $name, $value);
} else {
$e = new TemplateElement($this, $name, $value);
$this->templateElements[] = $e;
$e->content = $this->createDocumentFragment();
}
return $e;
} catch (\DOMException $e) {
// The element name is invalid for XML
// Replace any offending characters with "UHHHHHH" where H is the
// uppercase hexadecimal digits of the character's code point
$this->mangledElements = true;
$name = $this->coerceName($name);
return parent::createElementNS(null, $name, $value);
}
return $this->createElementNS(null, $name, $value);
}
public function createElementNS($namespaceURI, $qualifiedName, $value = "") {

27
lib/DOM/Element.php

@ -63,28 +63,7 @@ class Element extends \DOMElement {
}
public function setAttribute($name, $value) {
// Normalize the attribute name per modern DOM specifications.
$name = strtolower(trim($name));
// If setting a class attribute and classList has been invoked use classList to
// set it.
if ($name === 'class' && $this->_classList !== null) {
$this->_classList->value = $value;
} else {
try {
parent::setAttribute($name, $value);
} catch (\DOMException $e) {
// The attribute name is invalid for XML
// Replace any offending characters with "UHHHHHH" where H are the
// uppercase hexadecimal digits of the character's code point
$this->ownerDocument->mangledAttributes = true;
$name = $this->coerceName($name);
parent::setAttribute($name, $value);
}
if ($name === "id") {
$this->setIdAttribute($name, true);
}
}
$this->setAttributeNS(null, $name, $value);
}
public function setAttributeNS($namespaceURI, $qualifiedName, $value) {
@ -93,7 +72,9 @@ class Element extends \DOMElement {
$namespaceURI = trim($namespaceURI);
}
$qualifiedName = trim($qualifiedName);
if ($namespaceURI === null && ($this->namespaceURI ?? Parser::HTML_NAMESPACE) === Parser::HTML_NAMESPACE && !$this->hasAttributeNS($namespaceURI, $qualifiedName)) {
$qualifiedName = trim(strtolower($qualifiedName));
}
// If setting a class attribute and classList has been invoked use classList to
// set it.
if ($qualifiedName === 'class' && $namespaceURI === null && $this->_classList !== null) {

18
lib/TreeBuilder.php

@ -359,14 +359,6 @@ class TreeBuilder {
return true;
})());
// If attribute name coercison has occurred at some earlier point,
// we must coerce all attributes on html and body start tags in
// case they are relocated to existing elements
if ($token instanceof StartTagToken && $this->DOM->mangledAttributes && in_array($token->name, ["html", "body"])) {
foreach ($token->attributes as $attr) {
$attr->name = $this->coerceName($attr->name);
}
}
# 13.2.6.4. The rules for parsing tokens in HTML content
// OPTIMIZATION: Evaluation the "in body" mode first is
// faster for typical documents
@ -386,7 +378,10 @@ class TreeBuilder {
# not, add the attribute and its corresponding value to that element.
$top = $this->stack[0];
foreach ($token->attributes as $a) {
if (!$top->hasAttributeNS(null, $a->name)) {
// If attribute name coercison has occurred at some earlier point,
// we must coerce all attributes on html and body start tags in
// case they are relocated to existing elements
if (!$top->hasAttributeNS(null, $this->DOM->mangledAttributes ? $this->coerceName($a->name) : $a->name)) {
$top->setAttributeNS(null, $a->name, $a->value);
}
}
@ -414,7 +409,10 @@ class TreeBuilder {
$this->framesetOk = false;
$body = $this->stack[1];
foreach ($token->attributes as $a) {
if (!$body->hasAttributeNS(null, $a->name)) {
// If attribute name coercison has occurred at some earlier point,
// we must coerce all attributes on html and body start tags in
// case they are relocated to existing elements
if (!$body->hasAttributeNS(null, $this->DOM->mangledAttributes ? $this->coerceName($a->name) : $a->name)) {
$body->setAttributeNS(null, $a->name, $a->value);
}
}

87
tests/cases/TestDOM.php

@ -7,6 +7,7 @@ declare(strict_types=1);
namespace MensBeam\HTML\TestCase;
use MensBeam\HTML\Document;
use MensBeam\HTML\TemplateElement;
/**
* @covers \MensBeam\HTML\Document
@ -61,4 +62,90 @@ class TestDOM extends \PHPUnit\Framework\TestCase {
["TEST", "test"],
];
}
public function testCreateTemplateElements(): void {
$d = new Document;
$t = $d->createElement("template");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElement("TEMPLATE");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS(null, "template");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS(null, "TEMPLATE");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS("http://www.w3.org/1999/xhtml", "template");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS("http://www.w3.org/1999/xhtml", "TEMPLATE");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
}
/** @dataProvider provideNamespacedAttributeCreations */
public function testCreateNamespacedAttributes(?string $nsIn, string $nameIn, string $local, string $prefix): void {
$d = new Document;
$d->appendChild($d->createElement("html"));
$a = $d->createAttributeNS($nsIn, $nameIn);
$this->assertSame($local, $a->localName);
$this->assertSame($nsIn, $a->namespaceURI);
$this->assertSame($prefix, $a->prefix);
}
public function provideNamespacedAttributeCreations(): iterable {
return [
[null, "test", "test", ""],
[null, "test:test", "testU00003Atest", ""],
[null, "test", "test", ""],
[null, "TEST:TEST", "TESTU00003ATEST", ""],
["fake_ns", "test", "test", ""],
];
}
/** @dataProvider provideBareAttributeCreations */
public function testCreateBareAttributes(string $nameIn, string $nameOut): void {
$d = new Document;
$d->appendChild($d->createElement("html"));
$a = $d->createAttribute($nameIn);
$this->assertSame($nameOut, $a->name);
$this->assertNull($a->namespaceURI);
}
public function provideBareAttributeCreations(): iterable {
return [
["test", "test"],
["test:test", "testU00003Atest"],
["TEST", "TEST"],
["TEST:TEST", "TESTU00003ATEST"],
];
}
/** @dataProvider provideNamespacedAttributeSettings */
public function testSetNamespoacedAttributes(?string $elementNS, ?string $attrNS, string $nameIn, string $nameOut): void {
$d = new Document;
$e = $d->createElementNS($elementNS, "test");
$e->setAttributeNS($attrNS, $nameIn, "test");
$this->assertTrue($e->hasAttributeNS($attrNS, $nameOut));
}
public function provideNamespacedAttributeSettings(): iterable {
return [
[null, null, "test", "test"],
[null, null, "TEST", "test"],
["http://www.w3.org/1999/xhtml", null, "test", "test"],
["http://www.w3.org/1999/xhtml", null, "TEST", "test"],
[null, null, "test:test", "testU00003Atest"],
[null, null, "TEST:TEST", "testU00003Atest"],
["http://www.w3.org/1999/xhtml", null, "test:test", "testU00003Atest"],
["http://www.w3.org/1999/xhtml", null, "TEST:TEST", "testU00003Atest"],
[null, "http://www.w3.org/1999/xhtml", "test:test", "test"],
[null, "http://www.w3.org/1999/xhtml", "TEST:TEST", "TEST"],
["http://www.w3.org/1998/Math/MathML", null, "test", "test"],
["http://www.w3.org/1998/Math/MathML", null, "TEST", "TEST"],
[null, "http://www.w3.org/2000/xmlns/", "xmlns:xlink", "xlink"],
];
}
}

Loading…
Cancel
Save