diff options
Diffstat (limited to 'dom/bindings/BindingUtils.cpp')
-rw-r--r-- | dom/bindings/BindingUtils.cpp | 185 |
1 files changed, 184 insertions, 1 deletions
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 41ca6a629..a8884d6a4 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1,5 +1,4 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -16,11 +15,14 @@ #include "mozilla/SizePrintfMacros.h" #include "mozilla/Unused.h" #include "mozilla/UseCounter.h" +#include "mozilla/dom/DocGroup.h" #include "AccessCheck.h" #include "jsfriendapi.h" +#include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" +#include "nsHTMLTags.h" #include "nsIDocShell.h" #include "nsIDOMGlobalPropertyInitializer.h" #include "nsIPermissionManager.h" @@ -37,6 +39,7 @@ #include "nsGlobalWindow.h" #include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/CustomElementRegistry.h" #include "mozilla/dom/DOMError.h" #include "mozilla/dom/DOMErrorBinding.h" #include "mozilla/dom/DOMException.h" @@ -44,6 +47,7 @@ #include "mozilla/dom/HTMLObjectElement.h" #include "mozilla/dom/HTMLObjectElementBinding.h" #include "mozilla/dom/HTMLSharedObjectElement.h" +#include "mozilla/dom/HTMLElementBinding.h" #include "mozilla/dom/HTMLEmbedElementBinding.h" #include "mozilla/dom/HTMLAppletElementBinding.h" #include "mozilla/dom/Promise.h" @@ -62,6 +66,30 @@ namespace dom { using namespace workers; +// Forward declare GetConstructorObject methods. +#define HTML_TAG(_tag, _classname, _interfacename) \ +namespace HTML##_interfacename##ElementBinding { \ + JSObject* GetConstructorObject(JSContext*); \ +} +#define HTML_OTHER(_tag) +#include "nsHTMLTagList.h" +#undef HTML_TAG +#undef HTML_OTHER + +typedef JSObject* (*constructorGetterCallback)(JSContext*); + +// Mapping of html tag and GetConstructorObject methods. +#define HTML_TAG(_tag, _classname, _interfacename) HTML##_interfacename##ElementBinding::GetConstructorObject, +#define HTML_OTHER(_tag) nullptr, +// We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h +// to index into this array. +static const constructorGetterCallback sConstructorGetterCallback[] = { + HTMLUnknownElementBinding::GetConstructorObject, +#include "nsHTMLTagList.h" +#undef HTML_TAG +#undef HTML_OTHER +}; + const JSErrorFormatString ErrorFormatString[] = { #define MSG_DEF(_name, _argc, _exn, _str) \ { #_name, _str, _argc, _exn }, @@ -3382,6 +3410,161 @@ GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, return true; } +// https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor +already_AddRefed<nsGenericHTMLElement> +CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, + JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv) +{ + // Step 1. + nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + nsIDocument* doc = window->GetExtantDoc(); + if (!doc) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements()); + if (!registry) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + // Step 2 is in the code output by CGClassConstructor. + // Step 3. + JSContext* cx = aGlobal.Context(); + JS::Rooted<JSObject*> newTarget(cx, &aCallArgs.newTarget().toObject()); + CustomElementDefinition* definition = + registry->LookupCustomElementDefinition(cx, newTarget); + if (!definition) { + aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); + return nullptr; + } + + // The callee might be an Xray. Unwrap it to get actual callee. + JS::Rooted<JSObject*> callee(cx, js::CheckedUnwrap(&aCallArgs.callee())); + if (!callee) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + // And the actual callee might be in different compartment, so enter its + // compartment before getting the standard constructor object to compare to, + // so we get it from the same global as callee itself. + JSAutoCompartment ac(cx, callee); + int32_t tag = eHTMLTag_userdefined; + if (!definition->IsCustomBuiltIn()) { + // Step 4. + // If the definition is for an autonomous custom element, the active + // function should be HTMLElement. + JS::Rooted<JSObject*> constructor(cx, HTMLElementBinding::GetConstructorObject(cx)); + if (!constructor) { + aRv.NoteJSContextException(cx); + return nullptr; + } + + if (callee != constructor) { + aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); + return nullptr; + } + } else { + // Step 5. + // If the definition is for a customized built-in element, the localName + // should be defined in the specification. + tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName); + if (tag == eHTMLTag_userdefined) { + aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); + return nullptr; + } + + MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds"); + + // If the definition is for a customized built-in element, the active + // function should be the localname's element interface. + constructorGetterCallback cb = sConstructorGetterCallback[tag]; + if (!cb) { + aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); + return nullptr; + } + + JS::Rooted<JSObject*> constructor(cx, cb(cx)); + if (!constructor) { + aRv.NoteJSContextException(cx); + return nullptr; + } + + if (callee != constructor) { + aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); + return nullptr; + } + } + + RefPtr<mozilla::dom::NodeInfo> nodeInfo = + doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName, + nullptr, + kNameSpaceID_XHTML, + nsIDOMNode::ELEMENT_NODE); + if (!nodeInfo) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + // Step 6 and Step 7 are in the code output by CGClassConstructor. + // Step 8. + nsTArray<RefPtr<nsGenericHTMLElement>>& constructionStack = + definition->mConstructionStack; + if (constructionStack.IsEmpty()) { + RefPtr<nsGenericHTMLElement> newElement; + 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( + new CustomElementData(definition->mType, CustomElementData::State::eCustom)); + + newElement->SetCustomElementDefinition(definition); + + return newElement.forget(); + } + + // Step 9. + RefPtr<nsGenericHTMLElement>& element = constructionStack.LastElement(); + + // Step 10. + if (element == ALEADY_CONSTRUCTED_MARKER) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + + // Step 11. + // Do prototype swizzling for upgrading a custom element here, for cases when + // we have a reflector already. If we don't have one yet, our caller will + // create it with the right proto (by calling DoGetOrCreateDOMReflector with + // that proto). + JS::Rooted<JSObject*> reflector(cx, element->GetWrapper()); + if (reflector) { + // reflector might be in different compartment. + JSAutoCompartment ac(cx, reflector); + JS::Rooted<JSObject*> givenProto(cx, aGivenProto); + if (!JS_WrapObject(cx, &givenProto) || + !JS_SetPrototype(cx, reflector, givenProto)) { + aRv.NoteJSContextException(cx); + return nullptr; + } + } + + // Step 12 and Step 13. + return element.forget(); +} + #ifdef DEBUG namespace binding_detail { void |