diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index 33c8ea920e56..fb0a9759a43c 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -158,10 +158,12 @@ namespace { class MOZ_RAII AutoConstructionStackEntry final { public: - AutoConstructionStackEntry(nsTArray>& aStack, - nsGenericHTMLElement* aElement) + AutoConstructionStackEntry(nsTArray>& aStack, + Element* aElement) : mStack(aStack) { + MOZ_ASSERT(aElement->IsHTMLElement() || aElement->IsXULElement()); + mIndex = mStack.Length(); mStack.AppendElement(aElement); } @@ -174,7 +176,7 @@ public: } private: - nsTArray>& mStack; + nsTArray>& mStack; uint32_t mIndex; }; @@ -917,8 +919,7 @@ CustomElementRegistry::Upgrade(Element* aElement, } // Step 5. - AutoConstructionStackEntry acs(aDefinition->mConstructionStack, - nsGenericHTMLElement::FromContent(aElement)); + AutoConstructionStackEntry acs(aDefinition->mConstructionStack, aElement); // Step 6 and step 7. DoUpgrade(aElement, aDefinition->mConstructor, aRv); diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h index 37da2aee2f96..01d1a1c280b3 100644 --- a/dom/base/CustomElementRegistry.h +++ b/dom/base/CustomElementRegistry.h @@ -174,7 +174,7 @@ struct CustomElementDefinition UniquePtr mCallbacks; // A construction stack. Use nullptr to represent an "already constructed marker". - nsTArray> mConstructionStack; + nsTArray> mConstructionStack; // The document custom element order. uint32_t mDocOrder; diff --git a/dom/base/nsContentCreatorFunctions.h b/dom/base/nsContentCreatorFunctions.h index a5c210500b01..61f08fcfd714 100644 --- a/dom/base/nsContentCreatorFunctions.h +++ b/dom/base/nsContentCreatorFunctions.h @@ -59,7 +59,8 @@ NS_NewMathMLElement(mozilla::dom::Element** aResult, #ifdef MOZ_XUL nsresult NS_NewXULElement(mozilla::dom::Element** aResult, - already_AddRefed&& aNodeInfo); + already_AddRefed&& aNodeInfo, + mozilla::dom::FromParser aFromParser); void NS_TrustedNewXULElement(nsIContent** aResult, diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 1e2d61fb7bc5..817938af0440 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -226,6 +226,7 @@ #include "nsPluginHost.h" #include "mozilla/HangAnnotations.h" #include "mozilla/Encoding.h" +#include "nsXULElement.h" #include "nsIBidiKeyboard.h" @@ -10067,6 +10068,185 @@ nsContentUtils::TryToUpgradeElement(Element* aElement) } } +static void +DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, NodeInfo* aNodeInfo, + CustomElementConstructor* aConstructor, ErrorResult& aRv) +{ + RefPtr element = + aConstructor->Construct("Custom Element Create", aRv); + if (aRv.Failed()) { + return; + } + + if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { + if (!element || !element->IsHTMLElement()) { + aRv.ThrowTypeError(NS_LITERAL_STRING("HTMLElement")); + return; + } + } else { + if (!element || !element->IsXULElement()) { + aRv.ThrowTypeError(NS_LITERAL_STRING("XULElement")); + return; + } + } + + nsAtom* localName = aNodeInfo->NameAtom(); + + if (aDoc != element->OwnerDoc() || element->GetParentNode() || + element->HasChildren() || element->GetAttrCount() || + element->NodeInfo()->NameAtom() != localName) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + + element.forget(aElement); +} + +/* static */ nsresult +nsContentUtils::NewXULOrHTMLElement(Element** aResult, mozilla::dom::NodeInfo* aNodeInfo, + FromParser aFromParser, const nsAString* aIs, + mozilla::dom::CustomElementDefinition* aDefinition) +{ + RefPtr nodeInfo = aNodeInfo; + MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) || + nodeInfo->NamespaceEquals(kNameSpaceID_XUL), + "Can only create XUL or XHTML elements."); + + nsAtom *name = nodeInfo->NameAtom(); + RefPtr tagAtom = nodeInfo->NameAtom(); + RefPtr typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom; + + int32_t tag = eHTMLTag_unknown; + bool isCustomElementName = false; + if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { + tag = nsHTMLTags::CaseSensitiveAtomTagToId(name); + isCustomElementName = (tag == eHTMLTag_userdefined && + nsContentUtils::IsCustomElementName(name)); + } else { + isCustomElementName = nsContentUtils::IsCustomElementName(name); + } + bool isCustomElement = isCustomElementName || aIs; + MOZ_ASSERT_IF(aDefinition, isCustomElement); + + // https://dom.spec.whatwg.org/#concept-create-element + // 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 = aDefinition; + if (CustomElementRegistry::IsCustomElementEnabled() && isCustomElement && + !definition) { + definition = + nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(), + nodeInfo->LocalName(), + nodeInfo->NamespaceID(), + typeAtom); + } + + // It might be a problem that parser synchronously calls constructor, so filed + // bug 1378079 to figure out what we should do for parser case. + if (definition) { + /* + * Synchronous custom elements flag is determined by 3 places in spec, + * 1) create an element for a token, the flag is determined by + * "will execute script" which is not originally created + * for the HTML fragment parsing algorithm. + * 2) createElement and createElementNS, the flag is the same as + * NOT_FROM_PARSER. + * 3) clone a node, our implementation will not go into this function. + * For the unset case which is non-synchronous only applied for + * inner/outerHTML. + */ + 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 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; + + // Step 5. + if (definition->IsCustomBuiltIn()) { + // SetupCustomElement() should be called with an element that don't have + // CustomElementData setup, if not we will hit the assertion in + // SetCustomElementData(). + // Built-in element + MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), + "Custom built-in XUL elements are not supported yet."); + *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take(); + (*aResult)->SetCustomElementData(new CustomElementData(typeAtom)); + if (synchronousCustomElements) { + CustomElementRegistry::Upgrade(*aResult, definition, rv); + if (rv.MaybeSetPendingException(cx)) { + aes.ReportException(); + } + } else { + nsContentUtils::EnqueueUpgradeReaction(*aResult, definition); + } + + return NS_OK; + } + + // Step 6.1. + if (synchronousCustomElements) { + DoCustomElementCreate(aResult, nodeInfo->GetDocument(), nodeInfo, + definition->mConstructor, rv); + if (rv.MaybeSetPendingException(cx)) { + if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { + NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser)); + } else { + NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget())); + } + } + return NS_OK; + } + + // Step 6.2. + if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { + NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser)); + } else { + NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget())); + } + (*aResult)->SetCustomElementData(new CustomElementData(definition->mType)); + nsContentUtils::EnqueueUpgradeReaction(*aResult, definition); + return NS_OK; + } + + if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) { + // Per the Custom Element specification, unknown tags that are valid custom + // element names should be HTMLElement instead of HTMLUnknownElement. + if (isCustomElementName) { + NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser)); + } else { + *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take(); + } + } else { + NS_IF_ADDREF(*aResult = new nsXULElement(nodeInfo.forget())); + } + + if (!*aResult) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (CustomElementRegistry::IsCustomElementEnabled() && isCustomElement) { + (*aResult)->SetCustomElementData(new CustomElementData(typeAtom)); + } + + return NS_OK; +} + /* static */ CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc, const nsAString& aLocalName, @@ -10075,7 +10255,8 @@ nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc, { MOZ_ASSERT(aDoc); - if (aNameSpaceID != kNameSpaceID_XHTML || + if ((aNameSpaceID != kNameSpaceID_XUL && + aNameSpaceID != kNameSpaceID_XHTML) || !aDoc->GetDocShell()) { return nullptr; } @@ -10176,7 +10357,8 @@ nsContentUtils::GetCustomPrototype(nsIDocument* aDoc, { MOZ_ASSERT(aDoc); - if (aNamespaceID != kNameSpaceID_XHTML || + if ((aNamespaceID != kNameSpaceID_XHTML && + aNamespaceID != kNameSpaceID_XUL) || !aDoc->GetDocShell()) { return; } diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 956d5caf1230..8889df63333d 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -20,6 +20,7 @@ #include "js/TypeDecls.h" #include "js/Value.h" #include "js/RootingAPI.h" +#include "mozilla/dom/FromParser.h" #include "mozilla/BasicEvents.h" #include "mozilla/EventForwards.h" #include "mozilla/GuardObjects.h" @@ -3028,6 +3029,14 @@ public: */ static void TryToUpgradeElement(Element* aElement); + /** + * Creates a new XUL or XHTML element applying any appropriate custom element + * definition. + */ + static nsresult NewXULOrHTMLElement(Element** aResult, mozilla::dom::NodeInfo* aNodeInfo, + mozilla::dom::FromParser aFromParser, const nsAString* aIs, + mozilla::dom::CustomElementDefinition* aDefinition); + /** * Looking up a custom element definition. * https://html.spec.whatwg.org/#look-up-a-custom-element-definition diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index ef962c5d6549..e6c5bba2b39a 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -6398,7 +6398,7 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* // that old upgrade behavior could also share the new upgrade steps. // And this old upgrade will be remove at some point (when everything is // switched to latest custom element spec). - nsTArray>& constructionStack = + nsTArray>& constructionStack = definition->mConstructionStack; if (constructionStack.Length()) { element = constructionStack.LastElement(); diff --git a/dom/base/nsNameSpaceManager.cpp b/dom/base/nsNameSpaceManager.cpp index 3b9a13224560..9759bb6ba469 100644 --- a/dom/base/nsNameSpaceManager.cpp +++ b/dom/base/nsNameSpaceManager.cpp @@ -183,7 +183,7 @@ NS_NewElement(Element** aResult, } #ifdef MOZ_XUL if (ns == kNameSpaceID_XUL) { - return NS_NewXULElement(aResult, ni.forget()); + return NS_NewXULElement(aResult, ni.forget(), aFromParser); } #endif if (ns == kNameSpaceID_MathML) { diff --git a/dom/base/nsNodeUtils.cpp b/dom/base/nsNodeUtils.cpp index d9acdc21c0ed..ef5b8dd3298a 100644 --- a/dom/base/nsNodeUtils.cpp +++ b/dom/base/nsNodeUtils.cpp @@ -458,7 +458,7 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, } if (CustomElementRegistry::IsCustomElementEnabled() && - clone->IsHTMLElement()) { + (clone->IsHTMLElement() || clone->IsXULElement())) { // The cloned node may be a custom element that may require // enqueing upgrade reaction. Element* cloneElem = clone->AsElement(); diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 96ad690d280f..570dc9c08a34 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -46,6 +46,7 @@ #include "mozilla/dom/HTMLEmbedElement.h" #include "mozilla/dom/HTMLElementBinding.h" #include "mozilla/dom/HTMLEmbedElementBinding.h" +#include "mozilla/dom/XULElementBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ResolveSystemBinding.h" #include "mozilla/dom/WebIDLGlobalNameHash.h" @@ -57,6 +58,7 @@ #include "ipc/ErrorIPCUtils.h" #include "mozilla/UseCounter.h" #include "mozilla/dom/DocGroup.h" +#include "nsXULElement.h" namespace mozilla { namespace dom { @@ -3548,9 +3550,9 @@ GetCustomElementReactionsStack(JS::Handle aObj) } // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor -already_AddRefed -CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, - JS::Handle aGivenProto, ErrorResult& aRv) +already_AddRefed +CreateXULOrHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, + JS::Handle aGivenProto, ErrorResult& aRv) { // Step 1. nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); @@ -3565,6 +3567,11 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, return nullptr; } + int32_t ns = doc->GetDefaultNamespaceID(); + if (ns != kNameSpaceID_XUL) { + ns = kNameSpaceID_XHTML; + } + RefPtr registry(window->CustomElements()); if (!registry) { aRv.Throw(NS_ERROR_UNEXPECTED); @@ -3597,8 +3604,14 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, if (!definition->IsCustomBuiltIn()) { // Step 4. // If the definition is for an autonomous custom element, the active - // function should be HTMLElement. - JS::Rooted constructor(cx, HTMLElementBinding::GetConstructorObject(cx)); + // function should be HTMLElement or XULElement + JS::Rooted constructor(cx); + if (ns == kNameSpaceID_XUL) { + constructor = XULElementBinding::GetConstructorObject(cx); + } else { + constructor = HTMLElementBinding::GetConstructorObject(cx); + } + if (!constructor) { aRv.NoteJSContextException(cx); return nullptr; @@ -3612,6 +3625,13 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, // Step 5. // If the definition is for a customized built-in element, the localName // should be defined in the specification. + + // Customized built-in elements are not supported for XUL yet. + if (ns == kNameSpaceID_XUL) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName); if (tag == eHTMLTag_userdefined) { aRv.ThrowTypeError(); @@ -3643,7 +3663,7 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, RefPtr nodeInfo = doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName, nullptr, - kNameSpaceID_XHTML, + ns, nsIDOMNode::ELEMENT_NODE); if (!nodeInfo) { aRv.Throw(NS_ERROR_UNEXPECTED); @@ -3652,16 +3672,20 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, // Step 6 and Step 7 are in the code output by CGClassConstructor. // Step 8. - nsTArray>& constructionStack = + nsTArray>& constructionStack = definition->mConstructionStack; if (constructionStack.IsEmpty()) { - RefPtr newElement; - if (tag == eHTMLTag_userdefined) { - // Autonomous custom element. - newElement = NS_NewHTMLElement(nodeInfo.forget()); + RefPtr newElement; + if (ns == kNameSpaceID_XUL) { + newElement = new nsXULElement(nodeInfo.forget()); } else { - // Customized built-in element. - newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER); + if (tag == eHTMLTag_userdefined) { + // Autonomous custom element. + newElement = NS_NewHTMLElement(nodeInfo.forget()); + } else { + // Customized built-in element. + newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER); + } } newElement->SetCustomElementData( @@ -3673,7 +3697,7 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, } // Step 9. - RefPtr& element = constructionStack.LastElement(); + RefPtr& element = constructionStack.LastElement(); // Step 10. if (element == ALEADY_CONSTRUCTED_MARKER) { diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index d763d19a710e..faac177d5c0a 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3391,11 +3391,11 @@ GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, CustomElementReactionsStack* GetCustomElementReactionsStack(JS::Handle aObj); // This function is expected to be called from the constructor function for an -// HTML element interface; the global/callargs need to be whatever was passed to -// that constructor function. -already_AddRefed -CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, - JS::Handle aGivenProto, ErrorResult& aRv); +// HTML or XUL element interface; the global/callargs need to be whatever was +// passed to that constructor function. +already_AddRefed +CreateXULOrHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, + JS::Handle aGivenProto, ErrorResult& aRv); void SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter); diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index b2330c179d7f..6f0c770757ed 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1907,11 +1907,11 @@ class CGClassConstructor(CGAbstractStaticMethod): signatures = self._ctor.signatures() assert len(signatures) == 1 # Given that HTMLConstructor takes no args, we can just codegen a - # call to CreateHTMLElement() in BindingUtils which reuses the + # call to CreateXULOrHTMLElement() in BindingUtils which reuses the # factory thing in HTMLContentSink. Then we don't have to implement # Constructor on all the HTML elements. callGenerator = CGPerSignatureCall(signatures[0][0], signatures[0][1], - "CreateHTMLElement", True, + "CreateXULOrHTMLElement", True, self.descriptor, self._ctor, isConstructor=True) else: diff --git a/dom/html/nsHTMLContentSink.cpp b/dom/html/nsHTMLContentSink.cpp index 60628e37c1c7..238559af5ada 100644 --- a/dom/html/nsHTMLContentSink.cpp +++ b/dom/html/nsHTMLContentSink.cpp @@ -224,157 +224,17 @@ public: int32_t mStackPos; }; -static void -DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, nsAtom* aLocalName, - CustomElementConstructor* aConstructor, ErrorResult& aRv) -{ - RefPtr element = - aConstructor->Construct("Custom Element Create", aRv); - if (aRv.Failed()) { - return; - } - - if (!element || !element->IsHTMLElement()) { - aRv.ThrowTypeError(NS_LITERAL_STRING("HTMLElement")); - return; - } - - if (aDoc != element->OwnerDoc() || element->GetParentNode() || - element->HasChildren() || element->GetAttrCount() || - element->NodeInfo()->NameAtom() != aLocalName) { - aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); - return; - } - - element.forget(aElement); -} - nsresult NS_NewHTMLElement(Element** aResult, already_AddRefed&& aNodeInfo, FromParser aFromParser, const nsAString* aIs, mozilla::dom::CustomElementDefinition* aDefinition) { - *aResult = nullptr; - RefPtr nodeInfo = aNodeInfo; - nsAtom *name = nodeInfo->NameAtom(); - RefPtr tagAtom = nodeInfo->NameAtom(); - RefPtr typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom; - NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), - "Trying to HTML elements that don't have the XHTML namespace"); + "Trying to create HTML elements that don't have the XHTML namespace"); - int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(name); - bool isCustomElementName = (tag == eHTMLTag_userdefined && - nsContentUtils::IsCustomElementName(name)); - bool isCustomElement = isCustomElementName || aIs; - MOZ_ASSERT_IF(aDefinition, isCustomElement); - - // https://dom.spec.whatwg.org/#concept-create-element - // 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 = aDefinition; - if (CustomElementRegistry::IsCustomElementEnabled() && isCustomElement && - !definition) { - definition = - nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(), - nodeInfo->LocalName(), - nodeInfo->NamespaceID(), - typeAtom); - } - - // It might be a problem that parser synchronously calls constructor, so filed - // bug 1378079 to figure out what we should do for parser case. - if (definition) { - /* - * Synchronous custom elements flag is determined by 3 places in spec, - * 1) create an element for a token, the flag is determined by - * "will execute script" which is not originally created - * for the HTML fragment parsing algorithm. - * 2) createElement and createElementNS, the flag is the same as - * NOT_FROM_PARSER. - * 3) clone a node, our implementation will not go into this function. - * For the unset case which is non-synchronous only applied for - * inner/outerHTML. - */ - 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 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; - - // Step 5. - if (definition->IsCustomBuiltIn()) { - // SetupCustomElement() should be called with an element that don't have - // CustomElementData setup, if not we will hit the assertion in - // SetCustomElementData(). - // Built-in element - *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take(); - (*aResult)->SetCustomElementData(new CustomElementData(typeAtom)); - if (synchronousCustomElements) { - CustomElementRegistry::Upgrade(*aResult, definition, rv); - if (rv.MaybeSetPendingException(cx)) { - aes.ReportException(); - } - } else { - nsContentUtils::EnqueueUpgradeReaction(*aResult, definition); - } - - return NS_OK; - } - - // Step 6.1. - if (synchronousCustomElements) { - DoCustomElementCreate(aResult, nodeInfo->GetDocument(), - nodeInfo->NameAtom(), - definition->mConstructor, rv); - if (rv.MaybeSetPendingException(cx)) { - NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser)); - } - return NS_OK; - } - - // 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; - } - - // Per the Custom Element specification, unknown tags that are valid custom - // element names should be HTMLElement instead of HTMLUnknownElement. - if (isCustomElementName) { - NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser)); - } else { - *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take(); - } - - if (!*aResult) { - return NS_ERROR_OUT_OF_MEMORY; - } - - if (CustomElementRegistry::IsCustomElementEnabled() && isCustomElement) { - (*aResult)->SetCustomElementData(new CustomElementData(typeAtom)); - } - - return NS_OK; + return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIs, aDefinition); } already_AddRefed diff --git a/dom/tests/mochitest/webcomponents/chrome.ini b/dom/tests/mochitest/webcomponents/chrome.ini index 0f9110ab683d..9e2ff37cce7b 100644 --- a/dom/tests/mochitest/webcomponents/chrome.ini +++ b/dom/tests/mochitest/webcomponents/chrome.ini @@ -10,4 +10,5 @@ support-files = [test_custom_element_upgrade_chrome.html] support-files = test_upgrade_page.html - upgrade_tests.js \ No newline at end of file + upgrade_tests.js +[test_xul_custom_element.xul] diff --git a/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul b/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul new file mode 100644 index 000000000000..5b33116f7f4c --- /dev/null +++ b/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul @@ -0,0 +1,68 @@ + + + + + + XUL Custom Elements + + + + +

+ +

+  
+
diff --git a/dom/webidl/XULElement.webidl b/dom/webidl/XULElement.webidl index a9cb6a637f0d..dfb63711a5cf 100644 --- a/dom/webidl/XULElement.webidl +++ b/dom/webidl/XULElement.webidl @@ -8,7 +8,7 @@ interface XULControllers; interface MozRDFCompositeDataSource; interface MozRDFResource; -[Func="IsChromeOrXBL"] +[HTMLConstructor, Func="IsChromeOrXBL"] interface XULElement : Element { // Layout properties [SetterThrows] diff --git a/dom/xbl/nsXBLService.cpp b/dom/xbl/nsXBLService.cpp index 4b5d2c86f358..e809ac1c5d9f 100644 --- a/dom/xbl/nsXBLService.cpp +++ b/dom/xbl/nsXBLService.cpp @@ -1034,6 +1034,11 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoun rv = NS_NewXMLDocument(getter_AddRefs(doc)); NS_ENSURE_SUCCESS(rv, rv); + // XBL documents must allow XUL and XBL elements in them but the usual check + // only checks if the document is loaded in the system principal which is + // sometimes not the case. + doc->ForceEnableXULXBL(); + // Set the style backend type before loading the XBL document. Assume // gecko if there's no bound document. doc->SetStyleBackendType(aBoundDocument ? aBoundDocument->GetStyleBackendType() diff --git a/dom/xml/XMLDocument.cpp b/dom/xml/XMLDocument.cpp index 27afb3790c58..236b00030b4f 100644 --- a/dom/xml/XMLDocument.cpp +++ b/dom/xml/XMLDocument.cpp @@ -236,6 +236,12 @@ NS_NewXBLDocument(nsIDOMDocument** aInstancePtrResult, NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr idoc = do_QueryInterface(*aInstancePtrResult); + + // XBL documents must allow XUL and XBL elements in them but the usual check + // only checks if the document is loaded in the system principal which is + // sometimes not the case. + idoc->ForceEnableXULXBL(); + nsDocument* doc = static_cast(idoc.get()); doc->SetLoadedAsInteractiveData(true); doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE); diff --git a/dom/xul/moz.build b/dom/xul/moz.build index 60cfc1d7f436..8fb521034128 100644 --- a/dom/xul/moz.build +++ b/dom/xul/moz.build @@ -20,6 +20,7 @@ if CONFIG['MOZ_XUL']: EXPORTS += [ 'nsIXULDocument.h', + 'nsXULElement.h', ] UNIFIED_SOURCES += [ diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 3185b63b80a1..36f809c947d3 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -186,8 +186,12 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo * bool aIsScriptable, bool aIsRoot) { RefPtr ni = aNodeInfo; - RefPtr element = new nsXULElement(ni.forget()); - if (element) { + nsCOMPtr baseElement; + NS_NewXULElement(getter_AddRefs(baseElement), ni.forget(), dom::FROM_PARSER_NETWORK); + + if (baseElement) { + nsXULElement* element = FromContent(baseElement); + if (aPrototype->mHasIdAttribute) { element->SetHasID(); } @@ -216,9 +220,11 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo * } } } + + return baseElement.forget().downcast(); } - return element.forget(); + return nullptr; } nsresult @@ -255,20 +261,22 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype, } nsresult -NS_NewXULElement(Element** aResult, already_AddRefed&& aNodeInfo) +NS_NewXULElement(Element** aResult, already_AddRefed&& aNodeInfo, + FromParser aFromParser) { - RefPtr ni = aNodeInfo; + RefPtr nodeInfo = aNodeInfo; - NS_PRECONDITION(ni, "need nodeinfo for non-proto Create"); + NS_PRECONDITION(nodeInfo, "need nodeinfo for non-proto Create"); - nsIDocument* doc = ni->GetDocument(); + NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XUL), + "Trying to create XUL elements that don't have the XUL namespace"); + + nsIDocument* doc = nodeInfo->GetDocument(); if (doc && !doc->AllowXULXBL()) { return NS_ERROR_NOT_AVAILABLE; } - NS_ADDREF(*aResult = new nsXULElement(ni.forget())); - - return NS_OK; + return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, nullptr, nullptr); } void diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index b65fe0ddad64..2c70ffaaae81 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -40,6 +40,7 @@ #include "mozilla/dom/DOMRect.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/DOMString.h" +#include "mozilla/dom/FromParser.h" class nsIDocument; class nsXULPrototypeDocument; @@ -799,7 +800,8 @@ protected: bool BoolAttrIsTrue(nsAtom* aName) const; friend nsresult - NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo); + NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo, + mozilla::dom::FromParser aFromParser, const nsAString* aIs); friend void NS_TrustedNewXULElement(nsIContent** aResult, mozilla::dom::NodeInfo *aNodeInfo); diff --git a/layout/xul/nsDocElementBoxFrame.cpp b/layout/xul/nsDocElementBoxFrame.cpp index c2fe491aa8ca..159f7300348b 100644 --- a/layout/xul/nsDocElementBoxFrame.cpp +++ b/layout/xul/nsDocElementBoxFrame.cpp @@ -21,6 +21,7 @@ #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/FromParser.h" //#define DEBUG_REFLOW @@ -98,7 +99,7 @@ nsDocElementBoxFrame::CreateAnonymousContent(nsTArray& aElements) NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); nsresult rv = NS_NewXULElement(getter_AddRefs(mPopupgroupContent), - nodeInfo.forget()); + nodeInfo.forget(), dom::NOT_FROM_PARSER); NS_ENSURE_SUCCESS(rv, rv); if (!aElements.AppendElement(mPopupgroupContent)) @@ -110,7 +111,8 @@ nsDocElementBoxFrame::CreateAnonymousContent(nsTArray& aElements) nsIDOMNode::ELEMENT_NODE); NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); - rv = NS_NewXULElement(getter_AddRefs(mTooltipContent), nodeInfo.forget()); + rv = NS_NewXULElement(getter_AddRefs(mTooltipContent), nodeInfo.forget(), + dom::NOT_FROM_PARSER); NS_ENSURE_SUCCESS(rv, rv); mTooltipContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_default,