Bug 1440382, there should be no is-attribute, only internal is-value, in custom elements, r=mrbkap

This commit is contained in:
Olli Pettay 2018-06-28 14:22:58 +03:00
parent 52a6ffa381
commit 51b5db9342
19 changed files with 93 additions and 385 deletions

View File

@ -131,6 +131,12 @@ struct CustomElementData
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
void Unlink();
nsAtom* GetIs(Element* aElement)
{
// If mType isn't the same as name atom, this is a customized built-in
// element, which has 'is' value set.
return aElement->NodeInfo()->NameAtom() == mType ? nullptr : mType.get();
}
private:
virtual ~CustomElementData() {}

View File

@ -4283,6 +4283,7 @@ Element::ClearServoData(nsIDocument* aDoc) {
void
Element::SetCustomElementData(CustomElementData* aData)
{
SetHasCustomElementData();
nsExtendedDOMSlots *slots = ExtendedDOMSlots();
MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
#if DEBUG

View File

@ -520,6 +520,10 @@ public:
*/
inline CustomElementData* GetCustomElementData() const
{
if (!HasCustomElementData()) {
return nullptr;
}
const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
return slots ? slots->mCustomElementData.get() : nullptr;
}

View File

@ -9380,6 +9380,16 @@ StartElement(Element* aContent, StringBuilder& aBuilder)
aBuilder.Append(aContent->NodeName());
}
CustomElementData* ceData = aContent->GetCustomElementData();
if (ceData) {
nsAtom* isAttr = ceData->GetIs(aContent);
if (isAttr && !aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
aBuilder.Append(R"( is=")");
aBuilder.Append(nsDependentAtomString(isAttr));
aBuilder.Append(R"(")");
}
}
int32_t count = aContent->GetAttrCount();
for (int32_t i = 0; i < count; i++) {
const nsAttrName* name = aContent->GetAttrNameAt(i);

View File

@ -5549,10 +5549,6 @@ nsIDocument::CreateElement(const nsAString& aTagName,
elem->SetPseudoElementType(pseudoType);
}
if (is) {
elem->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
}
return elem.forget();
}
@ -5588,10 +5584,6 @@ nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
return nullptr;
}
if (is) {
element->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
}
return element.forget();
}

View File

@ -70,6 +70,8 @@ nsHTMLContentSerializer::SerializeHTMLAttributes(Element* aElement,
int32_t aNamespace,
nsAString& aStr)
{
MaybeSerializeIsValue(aElement, aStr);
int32_t count = aElement->GetAttrCount();
if (!count)
return true;

View File

@ -1551,6 +1551,8 @@ private:
// Set if the element might have any kind of anonymous content children,
// which would not be found through the element's children list.
ElementMayHaveAnonymousChildren,
// Set if element has CustomElementData.
ElementHasCustomElementData,
// Guard value
BooleanFlagCount
};
@ -1676,6 +1678,9 @@ public:
void SetMayHaveAnonymousChildren() { SetBoolFlag(ElementMayHaveAnonymousChildren); }
bool MayHaveAnonymousChildren() const { return GetBoolFlag(ElementMayHaveAnonymousChildren); }
void SetHasCustomElementData() { SetBoolFlag(ElementHasCustomElementData); }
bool HasCustomElementData() const { return GetBoolFlag(ElementHasCustomElementData); }
protected:
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
void SetIsInDocument() { SetBoolFlag(IsInDocument); }

View File

@ -440,24 +440,10 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
// The cloned node may be a custom element that may require
// enqueing upgrade reaction.
Element* cloneElem = clone->AsElement();
RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
CustomElementData* data = elem->GetCustomElementData();
RefPtr<nsAtom> typeAtom = data ? data->GetCustomElementType() : nullptr;
// Check if node may be custom element by type extension.
// ex. <button is="x-button">
nsAutoString extension;
if (!data || data->GetCustomElementType() != tagAtom) {
cloneElem->GetAttr(kNameSpaceID_None, nsGkAtoms::is, extension);
}
if ((data && data->GetCustomElementType() == tagAtom) ||
!extension.IsEmpty()) {
// The typeAtom can be determined by extension, because we only need to
// consider two cases: 1) Original node is a autonomous custom element
// which has CustomElementData. 2) Original node is a built-in custom
// element or normal element, but it has `is` attribute when it is being
// cloned.
RefPtr<nsAtom> typeAtom = extension.IsEmpty() ? tagAtom : NS_Atomize(extension);
if (typeAtom) {
cloneElem->SetCustomElementData(new CustomElementData(typeAtom));
MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));

View File

@ -173,6 +173,8 @@ nsXHTMLContentSerializer::SerializeAttributes(Element* aElement,
int32_t contentNamespaceID = aElement->GetNameSpaceID();
MaybeSerializeIsValue(aElement, aStr);
// this method is not called by nsHTMLContentSerializer
// so we don't have to check HTML element, just XHTML

View File

@ -27,6 +27,7 @@
#include "nsContentUtils.h"
#include "nsAttrName.h"
#include "mozilla/dom/Comment.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DocumentType.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ProcessingInstruction.h"
@ -791,12 +792,13 @@ nsXMLContentSerializer::SerializeAttributes(Element* aElement,
uint32_t aSkipAttr,
bool aAddNSAttr)
{
nsAutoString prefixStr, uriStr, valueStr;
nsAutoString xmlnsStr;
xmlnsStr.AssignLiteral(kXMLNS);
uint32_t index, count;
MaybeSerializeIsValue(aElement, aStr);
// If we had to add a new namespace declaration, serialize
// and push it on the namespace stack
if (aAddNSAttr) {
@ -1809,3 +1811,22 @@ nsXMLContentSerializer::ShouldMaintainPreLevel() const
// Only attempt to maintain the pre level for consumers who care about it.
return !mDoRaw || (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre);
}
bool
nsXMLContentSerializer::MaybeSerializeIsValue(Element* aElement,
nsAString& aStr)
{
CustomElementData* ceData = aElement->GetCustomElementData();
if (ceData) {
nsAtom* isAttr = ceData->GetIs(aElement);
if (isAttr && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
NS_ENSURE_TRUE(aStr.AppendLiteral(" is=\"", mozilla::fallible), false);
NS_ENSURE_TRUE(aStr.Append(nsDependentAtomString(isAttr),
mozilla::fallible),
false);
NS_ENSURE_TRUE(aStr.AppendLiteral("\"", mozilla::fallible), false);
}
}
return true;
}

View File

@ -342,6 +342,8 @@ class nsXMLContentSerializer : public nsIContentSerializer {
return mPreLevel;
}
bool MaybeSerializeIsValue(mozilla::dom::Element* aElement, nsAString& aStr);
int32_t mPrefixIndex;
struct NameSpaceDecl {

View File

@ -81,7 +81,7 @@ function startTest() {
var extendedButton = document.createElement("button", {is: "x-extended-button"});
is(extendedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
is(extendedButton.__proto__, ExtendButton.prototype, "Created element should have the prototype of the extended type.");
is(extendedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
is(extendedButton.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type.");
is(extendedButton.type, "submit", "Created element should be a button with type of \"submit\"");
// Custom element constructor.
@ -94,6 +94,35 @@ function startTest() {
customElements.define("x-in-html-namespace", XInHTMLNamespace);
var wrongNamespaceElem = document.createElementNS("http://www.w3.org/2000/svg", "x-in-html-namespace");
isnot(wrongNamespaceElem.__proto__, XInHTMLNamespace.prototype, "Definition for element in html namespace should not apply to SVG elements.");
var div = document.createElement("div");
div.appendChild(extendedButton);
is(div.innerHTML, '<button is="x-extended-button"></button>', "'is value' should be serialized.");
const de = SpecialPowers.Ci.nsIDocumentEncoder;
var htmlencoder = SpecialPowers.Cc["@mozilla.org/layout/documentEncoder;1?type=text/html"]
.createInstance(de);
htmlencoder.init(document, "text/html", de.OutputLFLineBreak);
htmlencoder.setCharset("UTF-8");
htmlencoder.setContainerNode(div);
is(htmlencoder.encodeToString(), '<button is="x-extended-button"></button>',
"'is value' should be serialized (html).");
var xhtmlencoder = SpecialPowers.Cc["@mozilla.org/layout/documentEncoder;1?type=application/xhtml+xml"]
.createInstance(de);
xhtmlencoder.init(document, "application/xhtml+xml", de.OutputLFLineBreak);
xhtmlencoder.setCharset("UTF-8");
xhtmlencoder.setContainerNode(div);
is(xhtmlencoder.encodeToString(), '<button is="x-extended-button" xmlns="http://www.w3.org/1999/xhtml"></button>',
"'is value' should be serialized (xhtml).");
var xmlencoder = SpecialPowers.Cc["@mozilla.org/layout/documentEncoder;1?type=text/xml"]
.createInstance(de);
xmlencoder.init(document, "text/xml", de.OutputLFLineBreak);
xmlencoder.setCharset("UTF-8");
xmlencoder.setContainerNode(div);
is(xmlencoder.encodeToString(), '<button is="x-extended-button" xmlns="http://www.w3.org/1999/xhtml"></button>',
"'is value' should be serialized (xml).");
}
startTest();

View File

@ -98,7 +98,7 @@ function simpleExtendedTest() {
let el = document.createElement("button", { is: "x-extended-button"});
ok(callbackCalled, "Callback should be called.");
is(Object.getPrototypeOf(el), ExtendButton.prototype, "Created element should have the prototype of the extended type.");
is(el.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
is(el.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type.");
}
function simpleInnerHTMLTest() {

View File

@ -148,13 +148,13 @@
function basicElementCreateBuiltIn() {
let element = document.createElementNS(XUL_NS, "axulelement", { is: "test-built-in-element" });
ok(element instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement");
is(element.getAttribute("is"), "test-built-in-element", "The |is| attribute of the created element should be the extended type.");
is(element.getAttribute("is"), "", "The |is| attribute of the created element should not be the extended type.");
document.querySelector("#content").appendChild(element);
is(element.textContent, "baz", "Should have set the textContent");
let element2 = element.cloneNode(false);
is(element2.localName, "axulelement", "Should see the right tag");
is(element2.getAttribute("is"), "test-built-in-element", "The |is| attribute of the created element should be the extended type.");
is(element2.getAttribute("is"), "", "The |is| attribute of the created element should not be the extended type.");
is(element2.textContent, "", "Shouldn't have cloned the textContent");
document.querySelector("#content").appendChild(element2);
is(element2.textContent, "baz", "Should have set the textContent");
@ -186,13 +186,13 @@
function subclassElementCreateBuiltIn() {
let element = document.createElementNS(XUL_NS, "popup", { is: "test-popup-extend" });
ok(element instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement");
is(element.getAttribute("is"), "test-popup-extend", "The |is| attribute of the created element should be the extended type.");
is(element.getAttribute("is"), "", "The |is| attribute of the created element should not be the extended type.");
document.querySelector("#content").appendChild(element);
is(element.textContent, "quuz", "Should have set the textContent");
let element2 = element.cloneNode(false);
is(element2.localName, "popup", "Should see the right tag");
is(element2.getAttribute("is"), "test-popup-extend", "The |is| attribute of the created element should be the extended type.");
is(element2.getAttribute("is"), "", "The |is| attribute of the created element should not be the extended type.");
is(element2.textContent, "", "Shouldn't have cloned the textContent");
document.querySelector("#content").appendChild(element2);
is(element2.textContent, "quuz", "Should have set the textContent");
@ -266,13 +266,13 @@
let element = document.createElementNS(XUL_NS, "testwithoutdash", { is: "testwithoutdash-extended" });
ok(element instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended");
ok(element instanceof TestWithoutDash, "Should be an instance of TestWithoutDash");
is(element.getAttribute("is"), "testwithoutdash-extended", "The |is| attribute of the created element should be the extended type.");
is(element.getAttribute("is"), "", "The |is| attribute of the created element should not be the extended type.");
document.querySelector("#content").appendChild(element);
is(element.textContent, "quux", "Should have set the textContent");
let element2 = element.cloneNode(false);
is(element2.localName, "testwithoutdash", "Should see the right tag");
is(element2.getAttribute("is"), "testwithoutdash-extended", "The |is| attribute of the created element should be the extended type.");
is(element2.getAttribute("is"), "", "The |is| attribute of the created element should not be the extended type.");
is(element2.textContent, "", "Shouldn't have cloned the textContent");
document.querySelector("#content").appendChild(element2);
is(element2.textContent, "quux", "Should have set the textContent");

View File

@ -10,7 +10,3 @@
[document.createElement must create an instance of autonomous custom elements when it has is attribute]
expected: FAIL
[document.createElement with unknown "is" value should create "undefined" state element]
expected: FAIL

View File

@ -1,10 +1,3 @@
[Document-createElementNS.html]
[autonomous: document.createElementNS should create custom elements with prefixes.]
expected: FAIL
[builtin: document.createElementNS should create custom elements with prefixes.]
expected: FAIL
[builtin: document.createElementNS should check namespaces.]
expected: FAIL

View File

@ -1,335 +1,2 @@
[builtin-coverage.html]
prefs: [dom.dialog_element.enabled:true]
[a: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[abbr: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[address: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[area: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[article: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[aside: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[audio: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[b: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[base: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[bdi: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[bdo: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[blockquote: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[body: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[br: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[button: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[canvas: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[caption: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[cite: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[code: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[col: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[colgroup: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[data: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[dd: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[del: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[details: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[dfn: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[div: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[dl: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[dt: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[em: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[embed: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[fieldset: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[figcaption: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[figure: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[footer: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[form: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[h1: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[h2: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[h3: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[h4: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[h5: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[h6: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[header: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[hgroup: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[hr: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[html: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[i: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[iframe: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[img: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[input: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[ins: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[kbd: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[label: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[legend: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[li: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[link: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[main: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[map: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[mark: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[menu: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[meta: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[meter: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[nav: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[noscript: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[object: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[ol: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[optgroup: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[option: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[output: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[p: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[param: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[picture: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[pre: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[progress: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[q: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[rp: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[rt: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[ruby: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[s: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[samp: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[script: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[section: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[select: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[small: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[source: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[span: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[strong: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[style: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[sub: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[summary: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[sup: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[table: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[tbody: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[td: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[template: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[textarea: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[tfoot: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[th: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[thead: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[time: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[title: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[tr: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[track: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[u: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[ul: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[var: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[video: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[wbr: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[datalist: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[dialog: Operator 'new' should instantiate a customized built-in element]
expected: FAIL
[slot: Operator 'new' should instantiate a customized built-in element]
expected: FAIL

View File

@ -1,4 +0,0 @@
[serializing-html-fragments.html]
["is" value should be serialized if the custom element has no "is" content attribute]
expected: FAIL

View File

@ -1,4 +0,0 @@
[Node-cloneNode.html]
[Node.prototype.cloneNode(false) must be able to clone as a customized built-in element when it has an inconsistent "is" attribute]
expected: FAIL