summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2020-01-19 21:22:39 -0500
committerGaming4JC <g4jc@hyperbola.info>2020-01-26 15:50:44 -0500
commita0014ac6e9e751f9f9bf68c1a345394bbcfd096f (patch)
tree6a7190495965549a03736feb6edc0b6a19e42da0
parentdd6749f7ddd4db014fb7d76a9b698d07f2b859f5 (diff)
downloadUXP-a0014ac6e9e751f9f9bf68c1a345394bbcfd096f.tar
UXP-a0014ac6e9e751f9f9bf68c1a345394bbcfd096f.tar.gz
UXP-a0014ac6e9e751f9f9bf68c1a345394bbcfd096f.tar.lz
UXP-a0014ac6e9e751f9f9bf68c1a345394bbcfd096f.tar.xz
UXP-a0014ac6e9e751f9f9bf68c1a345394bbcfd096f.zip
Bug 1378079 - Part 3: Complete the steps related to custom elements in "create an element for a token".
With fixup for missing header due to unified builds. Tag UXP Issue #1344
-rw-r--r--dom/base/CustomElementRegistry.cpp6
-rw-r--r--dom/base/nsContentCreatorFunctions.h4
-rw-r--r--dom/html/nsHTMLContentSink.cpp25
-rw-r--r--dom/tests/mochitest/webcomponents/mochitest.ini3
-rw-r--r--dom/tests/mochitest/webcomponents/test_document_register_base_queue.html48
-rw-r--r--dom/tests/mochitest/webcomponents/test_document_register_parser.html4
-rw-r--r--parser/html/nsHtml5TreeOperation.cpp250
-rw-r--r--parser/html/nsHtml5TreeOperation.h4
8 files changed, 203 insertions, 141 deletions
diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp
index 0f03b16bd..f17c2c7d9 100644
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -1044,8 +1044,12 @@ CustomElementReactionsStack::PopAndInvokeElementQueue()
// element, see https://github.com/w3c/webcomponents/issues/635.
// We usually report the error to entry global in gecko, so just follow the
// same behavior here.
+ // This may be null if it's called from parser, see the case of
+ // attributeChangedCallback in
+ // https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
+ // In that case, the exception of callback reactions will be automatically
+ // reported in CallSetup.
nsIGlobalObject* global = GetEntryGlobal();
- MOZ_ASSERT(global, "Should always have a entry global here!");
InvokeReactions(elementQueue, global);
}
diff --git a/dom/base/nsContentCreatorFunctions.h b/dom/base/nsContentCreatorFunctions.h
index 9576d9ba8..a5c210500 100644
--- a/dom/base/nsContentCreatorFunctions.h
+++ b/dom/base/nsContentCreatorFunctions.h
@@ -24,6 +24,7 @@ namespace mozilla {
namespace dom {
class Element;
class NodeInfo;
+struct CustomElementDefinition;
} // namespace dom
} // namespace mozilla
@@ -41,7 +42,8 @@ nsresult
NS_NewHTMLElement(mozilla::dom::Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser,
- const nsAString* aIs = nullptr);
+ const nsAString* aIs = nullptr,
+ mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
// First argument should be nsHTMLTag, but that adds dependency to parser
// for a bunch of files.
diff --git a/dom/html/nsHTMLContentSink.cpp b/dom/html/nsHTMLContentSink.cpp
index 2827f5ff6..518a3675e 100644
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -255,7 +255,8 @@ DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, nsIAtom* aLocalName
nsresult
NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
- FromParser aFromParser, const nsAString* aIs)
+ FromParser aFromParser, const nsAString* aIs,
+ mozilla::dom::CustomElementDefinition* aDefinition)
{
*aResult = nullptr;
@@ -276,8 +277,8 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
// We only handle the "synchronous custom elements flag is set" now.
// For the unset case (e.g. cloning a node), see bug 1319342 for that.
// Step 4.
- CustomElementDefinition* definition = nullptr;
- if (CustomElementRegistry::IsCustomElementEnabled()) {
+ CustomElementDefinition* definition = aDefinition;
+ if (!definition && CustomElementRegistry::IsCustomElementEnabled()) {
definition =
nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
nodeInfo->LocalName(),
@@ -302,9 +303,20 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT ||
aFromParser == dom::NOT_FROM_PARSER;
// Per discussion in https://github.com/w3c/webcomponents/issues/635,
- // use entry global in those places that are called from JS APIs.
- nsIGlobalObject* global = GetEntryGlobal();
- MOZ_ASSERT(global);
+ // use entry global in those places that are called from JS APIs and use the
+ // node document's global object if it is called from parser.
+ nsIGlobalObject* global;
+ if (aFromParser == dom::NOT_FROM_PARSER) {
+ global = GetEntryGlobal();
+ } else {
+ global = nodeInfo->GetDocument()->GetScopeObject();
+ }
+ if (!global) {
+ // In browser chrome code, one may have access to a document which doesn't
+ // have scope object anymore.
+ return NS_ERROR_FAILURE;
+ }
+
AutoEntryScript aes(global, "create custom elements");
JSContext* cx = aes.cx();
ErrorResult rv;
@@ -344,6 +356,7 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
// Step 6.2.
NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
+ (*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
return NS_OK;
}
diff --git a/dom/tests/mochitest/webcomponents/mochitest.ini b/dom/tests/mochitest/webcomponents/mochitest.ini
index e8f632e39..b32e9999e 100644
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -10,6 +10,7 @@ support-files =
[test_content_element.html]
[test_custom_element_adopt_callbacks.html]
[test_custom_element_callback_innerhtml.html]
+skip-if = true # disabled - See bug 1390396
[test_custom_element_clone_callbacks_extended.html]
[test_custom_element_htmlconstructor.html]
skip-if = os == 'android' # bug 1323645
@@ -20,6 +21,7 @@ support-files =
[test_custom_element_in_shadow.html]
skip-if = true || stylo # disabled - See bug 1390396 and 1293844
[test_custom_element_register_invalid_callbacks.html]
+[test_custom_element_throw_on_dynamic_markup_insertion.html]
[test_custom_element_get.html]
[test_custom_element_when_defined.html]
[test_nested_content_element.html]
@@ -31,7 +33,6 @@ skip-if = true || stylo # disabled - See bug 1390396 and 1293844
[test_document_adoptnode.html]
[test_document_importnode.html]
[test_document_register.html]
-[test_document_register_base_queue.html]
[test_document_register_lifecycle.html]
skip-if = true # disabled - See bug 1390396
[test_document_register_parser.html]
diff --git a/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html b/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html
deleted file mode 100644
index c839857b4..000000000
--- a/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=783129
--->
-<head>
- <title>Test for document.registerElement lifecycle callback</title>
- <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-<script>
-var p = Object.create(HTMLElement.prototype);
-
-var createdCallbackCallCount = 0;
-
-// By the time the base element queue is processed via the microtask,
-// both x-hello elements should be in the document.
-p.createdCallback = function() {
- var one = document.getElementById("one");
- var two = document.getElementById("two");
- isnot(one, null, "First x-hello element should be in the tree.");
- isnot(two, null, "Second x-hello element should be in the tree.");
- createdCallbackCallCount++;
- if (createdCallbackCallCount == 2) {
- SimpleTest.finish();
- }
-
- if (createdCallbackCallCount > 2) {
- ok(false, "Created callback called too much.");
- }
-};
-
-p.attributeChangedCallback = function(name, oldValue, newValue) {
- ok(false, "Attribute changed callback should not be called because callbacks should not be queued until created callback invoked.");
-};
-
-document.registerElement("x-hello", { prototype: p });
-
-SimpleTest.waitForExplicitFinish();
-</script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
-<x-hello id="one"></x-hello>
-<x-hello id="two"></x-hello>
-<script>
-</script>
-</body>
-</html>
diff --git a/dom/tests/mochitest/webcomponents/test_document_register_parser.html b/dom/tests/mochitest/webcomponents/test_document_register_parser.html
index bc4cdcbac..a4fb63139 100644
--- a/dom/tests/mochitest/webcomponents/test_document_register_parser.html
+++ b/dom/tests/mochitest/webcomponents/test_document_register_parser.html
@@ -11,7 +11,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783129
var extendedButtonProto = Object.create(HTMLButtonElement.prototype);
var buttonCallbackCalled = false;
-extendedButtonProto.createdCallback = function() {
+extendedButtonProto.connectedCallback = function() {
is(buttonCallbackCalled, false, "created callback for x-button should only be called once.");
is(this.tagName, "BUTTON", "Only the <button> element should be upgraded.");
buttonCallbackCalled = true;
@@ -21,7 +21,7 @@ document.registerElement("x-button", { prototype: extendedButtonProto, extends:
var divProto = Object.create(HTMLDivElement.prototype);
var divCallbackCalled = false;
-divProto.createdCallback = function() {
+divProto.connectedCallback = function() {
is(divCallbackCalled, false, "created callback for x-div should only be called once.");
is(buttonCallbackCalled, true, "crated callback should be called for x-button before x-div.");
is(this.tagName, "X-DIV", "Only the <x-div> element should be upgraded.");
diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp
index 0c5aad566..a7d3da259 100644
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -24,6 +24,7 @@
#include "nsIFormControl.h"
#include "nsIStyleSheetLinkingElement.h"
#include "nsIDOMDocumentType.h"
+#include "DocGroup.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "nsIMutationObserver.h"
@@ -74,6 +75,29 @@ class MOZ_STACK_CLASS nsHtml5OtherDocUpdate {
nsCOMPtr<nsIDocument> mDocument;
};
+/**
+ * Helper class to temporary break out of the document update batch. Use this
+ * with caution as this will cause blocked scripts to run.
+ */
+class MOZ_RAII mozAutoPauseContentUpdate final
+{
+public:
+ explicit mozAutoPauseContentUpdate(nsIDocument* aDocument)
+ : mDocument(aDocument)
+ {
+ MOZ_ASSERT(mDocument);
+ mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
+ }
+
+ ~mozAutoPauseContentUpdate()
+ {
+ mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
+ }
+
+private:
+ nsCOMPtr<nsIDocument> mDocument;
+};
+
nsHtml5TreeOperation::nsHtml5TreeOperation()
: mOpCode(eTreeOpUninitialized)
{
@@ -332,6 +356,41 @@ nsHtml5TreeOperation::AddAttributes(nsIContent* aNode,
return NS_OK;
}
+void
+nsHtml5TreeOperation::SetHTMLElementAttributes(dom::Element* aElement,
+ nsIAtom* aName,
+ nsHtml5HtmlAttributes* aAttributes)
+{
+ int32_t len = aAttributes->getLength();
+ for (int32_t i = 0; i < len; i++) {
+ // prefix doesn't need regetting. it is always null or a static atom
+ // local name is never null
+ nsCOMPtr<nsIAtom> localName =
+ Reget(aAttributes->getLocalNameNoBoundsCheck(i));
+ nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
+ int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
+
+ nsString value; // Not Auto, because using it to hold nsStringBuffer*
+ aAttributes->getValueNoBoundsCheck(i).ToString(value);
+ if (nsHtml5Atoms::a == aName && nsHtml5Atoms::name == localName) {
+ // This is an HTML5-incompliant Geckoism.
+ // Remove when fixing bug 582361
+ NS_ConvertUTF16toUTF8 cname(value);
+ NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
+ aElement->SetAttr(nsuri,
+ localName,
+ prefix,
+ uv,
+ false);
+ } else {
+ aElement->SetAttr(nsuri,
+ localName,
+ prefix,
+ value,
+ false);
+ }
+ }
+ }
nsIContent*
nsHtml5TreeOperation::CreateHTMLElement(
@@ -351,106 +410,133 @@ nsHtml5TreeOperation::CreateHTMLElement(
RefPtr<dom::NodeInfo> nodeInfo = aNodeInfoManager->GetNodeInfo(
aName, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
NS_ASSERTION(nodeInfo, "Got null nodeinfo.");
- nsCOMPtr<dom::Element> newElement = aCreator(nodeInfo.forget(), aFromParser);
- MOZ_ASSERT(newElement, "Element creation created null pointer.");
+ dom::Element* newContent = nullptr;
+ nsIDocument* document = nodeInfo->GetDocument();
+ bool willExecuteScript = false;
+ bool isCustomElement = false;
+ nsString isValue;
+ dom::CustomElementDefinition* definition = nullptr;
+
+ // Avoid overhead by checking if custom elements pref is enabled or not.
+ if (nsContentUtils::IsCustomElementsEnabled()) {
+ if (aAttributes) {
+ nsHtml5String is = aAttributes->getValue(nsHtml5AttributeName::ATTR_IS);
+ if (is) {
+ is.ToString(isValue);
+ }
+ }
- dom::Element* newContent = newElement;
- aBuilder->HoldElement(newElement.forget());
+ isCustomElement = (aCreator == NS_NewCustomElement || !isValue.IsEmpty());
+ if (isCustomElement && aFromParser != dom::FROM_PARSER_FRAGMENT) {
+ definition = nsContentUtils::LookupCustomElementDefinition(document,
+ nodeInfo->LocalName(), nodeInfo->NamespaceID(),
+ (isValue.IsEmpty() ? nullptr : &isValue));
- if (aCreator == NS_NewCustomElement) {
- // Not inlining the call below into NS_NewCustomElement itself, because
- // in the near future, the code here will need to break out of an update
- // batch here.
- nsContentUtils::SetupCustomElement(newContent);
+ if (definition) {
+ willExecuteScript = true;
+ }
+ }
}
- if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
- nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
- if (ssle) {
- ssle->InitStyleLinkElement(false);
- ssle->SetEnableUpdates(false);
+ if (willExecuteScript) { // This will cause custom element constructors to run
+ AutoSetThrowOnDynamicMarkupInsertionCounter
+ throwOnDynamicMarkupInsertionCounter(document);
+ mozAutoPauseContentUpdate autoPauseContentUpdate(document);
+ {
+ nsAutoMicroTask mt;
}
- } else if (MOZ_UNLIKELY(isKeygen)) {
- // Adapted from CNavDTD
- nsresult rv;
- nsCOMPtr<nsIFormProcessor> theFormProcessor =
- do_GetService(kFormProcessorCID, &rv);
- if (NS_FAILED(rv)) {
+ dom::AutoCEReaction
+ autoCEReaction(document->GetDocGroup()->CustomElementReactionsStack());
+
+ nsCOMPtr<dom::Element> newElement;
+ NS_NewHTMLElement(getter_AddRefs(newElement), nodeInfo.forget(),
+ aFromParser, (isValue.IsEmpty() ? nullptr : &isValue),
+ definition);
+
+ MOZ_ASSERT(newElement, "Element creation created null pointer.");
+ newContent = newElement;
+ aBuilder->HoldElement(newElement.forget());
+
+ if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
+ nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
+ if (ssle) {
+ ssle->InitStyleLinkElement(false);
+ ssle->SetEnableUpdates(false);
+ }
+ }
+
+ if (!aAttributes) {
return newContent;
}
- nsTArray<nsString> theContent;
- nsAutoString theAttribute;
-
- (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"),
- theContent,
- theAttribute);
-
- newContent->SetAttr(kNameSpaceID_None,
- nsGkAtoms::moztype,
- nullptr,
- theAttribute,
- false);
-
- RefPtr<dom::NodeInfo> optionNodeInfo =
- aNodeInfoManager->GetNodeInfo(nsHtml5Atoms::option,
- nullptr,
- kNameSpaceID_XHTML,
- nsIDOMNode::ELEMENT_NODE);
-
- for (uint32_t i = 0; i < theContent.Length(); ++i) {
- RefPtr<dom::NodeInfo> ni = optionNodeInfo;
- nsCOMPtr<dom::Element> optionElt =
- NS_NewHTMLOptionElement(ni.forget(), aFromParser);
- RefPtr<nsTextNode> optionText = new nsTextNode(aNodeInfoManager);
- (void) optionText->SetText(theContent[i], false);
- optionElt->AppendChildTo(optionText, false);
- newContent->AppendChildTo(optionElt, false);
+ SetHTMLElementAttributes(newContent, aName, aAttributes);
+ } else {
+ nsCOMPtr<dom::Element> newElement;
+
+ if (isCustomElement) {
+ NS_NewHTMLElement(getter_AddRefs(newElement), nodeInfo.forget(),
+ aFromParser, (isValue.IsEmpty() ? nullptr : &isValue),
+ definition);
+ } else {
+ newElement = aCreator(nodeInfo.forget(), aFromParser);
}
- newContent->DoneAddingChildren(false);
- }
- if (!aAttributes) {
- return newContent;
- }
+ MOZ_ASSERT(newElement, "Element creation created null pointer.");
- int32_t len = aAttributes->getLength();
- for (int32_t i = 0; i < len; i++) {
- // prefix doesn't need regetting. it is always null or a static atom
- // local name is never null
- nsCOMPtr<nsIAtom> localName =
- Reget(aAttributes->getLocalNameNoBoundsCheck(i));
- nsCOMPtr<nsIAtom> prefix = aAttributes->getPrefixNoBoundsCheck(i);
- int32_t nsuri = aAttributes->getURINoBoundsCheck(i);
+ newContent = newElement;
+ aBuilder->HoldElement(newElement.forget());
- nsString value; // Not Auto, because using it to hold nsStringBuffer*
- aAttributes->getValueNoBoundsCheck(i).ToString(value);
- if (nsHtml5Atoms::a == aName && nsHtml5Atoms::name == localName) {
- // This is an HTML5-incompliant Geckoism.
- // Remove when fixing bug 582361
- NS_ConvertUTF16toUTF8 cname(value);
- NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
- newContent->SetAttr(nsuri,
- localName,
- prefix,
- uv,
- false);
- } else {
- newContent->SetAttr(nsuri,
- localName,
- prefix,
- value,
+ if (MOZ_UNLIKELY(aName == nsHtml5Atoms::style || aName == nsHtml5Atoms::link)) {
+ nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(newContent));
+ if (ssle) {
+ ssle->InitStyleLinkElement(false);
+ ssle->SetEnableUpdates(false);
+ }
+ } else if (MOZ_UNLIKELY(isKeygen)) {
+ // Adapted from CNavDTD
+ nsresult rv;
+ nsCOMPtr<nsIFormProcessor> theFormProcessor =
+ do_GetService(kFormProcessorCID, &rv);
+ if (NS_FAILED(rv)) {
+ return newContent;
+ }
+
+ nsTArray<nsString> theContent;
+ nsAutoString theAttribute;
+
+ (void) theFormProcessor->ProvideContent(NS_LITERAL_STRING("select"),
+ theContent,
+ theAttribute);
+
+ newContent->SetAttr(kNameSpaceID_None,
+ nsGkAtoms::moztype,
+ nullptr,
+ theAttribute,
false);
- // Custom element setup may be needed if there is an "is" attribute.
- if (nsContentUtils::IsWebComponentsEnabled() &&
- kNameSpaceID_None == nsuri &&
- !prefix && nsGkAtoms::is == localName) {
- nsContentUtils::SetupCustomElement(newContent, &value);
+ RefPtr<dom::NodeInfo> optionNodeInfo = aNodeInfoManager->GetNodeInfo(
+ nsHtml5Atoms::option, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE);
+
+ for (uint32_t i = 0; i < theContent.Length(); ++i) {
+ RefPtr<dom::NodeInfo> ni = optionNodeInfo;
+ nsCOMPtr<dom::Element> optionElt =
+ NS_NewHTMLOptionElement(ni.forget(), aFromParser);
+ RefPtr<nsTextNode> optionText = new nsTextNode(aNodeInfoManager);
+ (void) optionText->SetText(theContent[i], false);
+ optionElt->AppendChildTo(optionText, false);
+ newContent->AppendChildTo(optionElt, false);
}
+ newContent->DoneAddingChildren(false);
}
+
+ if (!aAttributes) {
+ return newContent;
+ }
+
+ SetHTMLElementAttributes(newContent, aName, aAttributes);
}
+
return newContent;
}
diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h
index 0cdb08e2e..db6cc6397 100644
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -143,6 +143,10 @@ class nsHtml5TreeOperation {
nsIContent* aTable,
nsHtml5DocumentBuilder* aBuilder);
+ static void SetHTMLElementAttributes(mozilla::dom::Element* aElement,
+ nsIAtom* aName,
+ nsHtml5HtmlAttributes* aAttributes);
+
static nsresult AddAttributes(nsIContent* aNode,
nsHtml5HtmlAttributes* aAttributes,
nsHtml5DocumentBuilder* aBuilder);