diff options
Diffstat (limited to 'dom/bindings')
-rw-r--r-- | dom/bindings/BindingUtils.cpp | 212 | ||||
-rw-r--r-- | dom/bindings/BindingUtils.h | 15 | ||||
-rw-r--r-- | dom/bindings/Bindings.conf | 9 | ||||
-rw-r--r-- | dom/bindings/CallbackObject.cpp | 11 | ||||
-rw-r--r-- | dom/bindings/Codegen.py | 154 | ||||
-rw-r--r-- | dom/bindings/parser/WebIDL.py | 47 | ||||
-rw-r--r-- | dom/bindings/parser/tests/test_cereactions.py | 162 | ||||
-rw-r--r-- | dom/bindings/parser/tests/test_constructor.py | 166 | ||||
-rw-r--r-- | dom/bindings/parser/tests/test_constructor_no_interface_object.py | 33 | ||||
-rw-r--r-- | dom/bindings/test/TestBindingHeader.h | 30 | ||||
-rw-r--r-- | dom/bindings/test/TestCodeGen.webidl | 17 | ||||
-rw-r--r-- | dom/bindings/test/TestExampleGen.webidl | 4 | ||||
-rw-r--r-- | dom/bindings/test/TestJSImplGen.webidl | 4 | ||||
-rw-r--r-- | dom/bindings/test/test_bug560072.html | 5 |
14 files changed, 833 insertions, 36 deletions
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index b244d4d2a..f76f14d95 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -16,13 +16,16 @@ #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 "nsIDocShell.h" #include "nsIDOMGlobalPropertyInitializer.h" +#include "nsIParserService.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" #include "nsIXPConnect.h" @@ -37,6 +40,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 +48,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 +67,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 }, @@ -3377,6 +3406,189 @@ GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, return true; } +CustomElementReactionsStack* +GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj) +{ + // This might not be the right object, if there are wrappers. Unwrap if we can. + JSObject* obj = js::CheckedUnwrap(aObj); + if (!obj) { + return nullptr; + } + + nsGlobalWindow* window = xpc::WindowGlobalOrNull(obj); + if (!window) { + return nullptr; + } + + DocGroup* docGroup = window->AsInner()->GetDocGroup(); + if (!docGroup) { + return nullptr; + } + + return docGroup->CustomElementReactionsStack(); +} + +// 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. + nsIParserService* parserService = nsContentUtils::GetParserService(); + if (!parserService) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + tag = parserService->HTMLCaseSensitiveAtomTagToId(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 diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 5cab835b3..e583d0e06 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -42,6 +42,7 @@ #include "nsWrapperCacheInlines.h" +class nsGenericHTMLElement; class nsIJSID; namespace mozilla { @@ -49,6 +50,7 @@ namespace mozilla { enum UseCounter : int16_t; namespace dom { +class CustomElementReactionsStack; template<typename KeyType, typename ValueType> class Record; nsresult @@ -3420,6 +3422,19 @@ bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, JS::MutableHandle<JSObject*> aDesiredProto); +// Get the CustomElementReactionsStack for the docgroup of the global +// of the underlying object of aObj. This can be null if aObj can't +// be CheckUnwrapped, or if the global of the result has no docgroup +// (e.g. because it's not a Window global). +CustomElementReactionsStack* +GetCustomElementReactionsStack(JS::Handle<JSObject*> 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<nsGenericHTMLElement> +CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs, + JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv); + void SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject, UseCounter aUseCounter); diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index b00af2085..9428529f4 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1638,6 +1638,15 @@ DOMInterfaces = { 'register': False, }, +'TestHTMLConstructorInterface' : { + 'headerFile': 'TestBindingHeader.h', + 'register': False, + }, + +'TestCEReactionsInterface' : { + 'headerFile': 'TestBindingHeader.h', + 'register': False, + }, } # These are temporary, until they've been converted to use new DOM bindings diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp index bb01c804c..398acf9da 100644 --- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/CallbackObject.h" +#include "mozilla/CycleCollectedJSContext.h" #include "mozilla/dom/BindingUtils.h" #include "jsfriendapi.h" #include "nsIScriptGlobalObject.h" @@ -79,7 +80,10 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback, , mIsMainThread(NS_IsMainThread()) { if (mIsMainThread) { - nsContentUtils::EnterMicroTask(); + CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); + if (ccjs) { + ccjs->EnterMicroTask(); + } } // Compute the caller's subject principal (if necessary) early, before we @@ -288,7 +292,10 @@ CallbackObject::CallSetup::~CallSetup() // It is important that this is the last thing we do, after leaving the // compartment and undoing all our entry/incumbent script changes if (mIsMainThread) { - nsContentUtils::LeaveMicroTask(); + CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); + if (ccjs) { + ccjs->LeaveMicroTask(); + } } } diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 6b23e8225..8ee732cca 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1747,6 +1747,71 @@ class CGClassConstructor(CGAbstractStaticMethod): else: ctorName = self.descriptor.interface.identifier.name + # [HTMLConstructor] for custom element + # This needs to live in bindings code because it directly examines + # newtarget and the callee function to do HTMLConstructor specific things. + if self._ctor.isHTMLConstructor(): + htmlConstructorSanityCheck = dedent(""" + // The newTarget might be a cross-compartment wrapper. Get the underlying object + // so we can do the spec's object-identity checks. + JS::Rooted<JSObject*> newTarget(cx, js::CheckedUnwrap(&args.newTarget().toObject())); + if (!newTarget) { + return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR); + } + + // Step 2 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor. + // Enter the compartment of our underlying newTarget object, so we end + // up comparing to the constructor object for our interface from that global. + { + JSAutoCompartment ac(cx, newTarget); + JS::Handle<JSObject*> constructor(GetConstructorObjectHandle(cx)); + if (!constructor) { + return false; + } + if (newTarget == constructor) { + return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR); + } + } + + """) + + # If we are unable to get desired prototype from newTarget, then we + # fall back to the interface prototype object from newTarget's realm. + htmlConstructorFallback = dedent(""" + if (!desiredProto) { + // Step 7 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor. + // This fallback behavior is designed to match analogous behavior for the + // JavaScript built-ins. So we enter the compartment of our underlying + // newTarget object and fall back to the prototype object from that global. + // XXX The spec says to use GetFunctionRealm(), which is not actually + // the same thing as what we have here (e.g. in the case of scripted callable proxies + // whose target is not same-compartment with the proxy, or bound functions, etc). + // https://bugzilla.mozilla.org/show_bug.cgi?id=1317658 + { + JSAutoCompartment ac(cx, newTarget); + desiredProto = GetProtoObjectHandle(cx); + if (!desiredProto) { + return false; + } + } + + // desiredProto is in the compartment of the underlying newTarget object. + // Wrap it into the context compartment. + if (!JS_WrapObject(cx, &desiredProto)) { + return false; + } + } + """) + else: + htmlConstructorSanityCheck = "" + htmlConstructorFallback = "" + + + # If we're a constructor, "obj" may not be a function, so calling + # XrayAwareCalleeGlobal() on it is not safe. Of course in the + # constructor case either "obj" is an Xray or we're already in the + # content compartment, not the Xray compartment, so just + # constructing the GlobalObject from "obj" is fine. preamble = fill( """ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); @@ -1757,19 +1822,41 @@ class CGClassConstructor(CGAbstractStaticMethod): // Adding more relocations return ThrowConstructorWithoutNew(cx, "${ctorName}"); } + + GlobalObject global(cx, obj); + if (global.Failed()) { + return false; + } + + $*{htmlConstructorSanityCheck} JS::Rooted<JSObject*> desiredProto(cx); if (!GetDesiredProto(cx, args, &desiredProto)) { return false; } + $*{htmlConstructorFallback} """, chromeOnlyCheck=chromeOnlyCheck, - ctorName=ctorName) - - name = self._ctor.identifier.name - nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) - callGenerator = CGMethodCall(nativeName, True, self.descriptor, - self._ctor, isConstructor=True, - constructorName=ctorName) + ctorName=ctorName, + htmlConstructorSanityCheck=htmlConstructorSanityCheck, + htmlConstructorFallback=htmlConstructorFallback) + + if self._ctor.isHTMLConstructor(): + 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 + # 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, + self.descriptor, self._ctor, + isConstructor=True) + else: + name = self._ctor.identifier.name + nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) + callGenerator = CGMethodCall(nativeName, True, self.descriptor, + self._ctor, isConstructor=True, + constructorName=ctorName) return preamble + "\n" + callGenerator.define() @@ -7386,7 +7473,7 @@ class CGPerSignatureCall(CGThing): def __init__(self, returnType, arguments, nativeMethodName, static, descriptor, idlNode, argConversionStartsAt=0, getter=False, setter=False, isConstructor=False, useCounterName=None, - resultVar=None): + resultVar=None, objectName="obj"): assert idlNode.isMethod() == (not getter and not setter) assert idlNode.isAttr() == (getter or setter) # Constructors are always static @@ -7440,26 +7527,23 @@ class CGPerSignatureCall(CGThing): argsPre = [] if idlNode.isStatic(): - # If we're a constructor, "obj" may not be a function, so calling - # XrayAwareCalleeGlobal() on it is not safe. Of course in the - # constructor case either "obj" is an Xray or we're already in the - # content compartment, not the Xray compartment, so just - # constructing the GlobalObject from "obj" is fine. - if isConstructor: - objForGlobalObject = "obj" - else: - objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)" - cgThings.append(CGGeneric(fill( - """ - GlobalObject global(cx, ${obj}); - if (global.Failed()) { - return false; - } + # If we're a constructor, the GlobalObject struct will be created in + # CGClassConstructor. + if not isConstructor: + cgThings.append(CGGeneric(dedent( + """ + GlobalObject global(cx, xpc::XrayAwareCalleeGlobal(obj)); + if (global.Failed()) { + return false; + } + + """))) - """, - obj=objForGlobalObject))) argsPre.append("global") + if isConstructor and idlNode.isHTMLConstructor(): + argsPre.extend(["args", "desiredProto"]) + # For JS-implemented interfaces we do not want to base the # needsCx decision on the types involved, just on our extended # attributes. Also, JSContext is not needed for the static case @@ -7588,6 +7672,17 @@ class CGPerSignatureCall(CGThing): CGIfWrapper(CGList(xraySteps), "objIsXray")) + if (idlNode.getExtendedAttribute('CEReactions') is not None and + not getter): + cgThings.append(CGGeneric(fill( + """ + CustomElementReactionsStack* reactionsStack = GetCustomElementReactionsStack(${obj}); + Maybe<AutoCEReaction> ceReaction; + if (reactionsStack) { + ceReaction.emplace(reactionsStack, cx); + } + """, obj=objectName))) + # If this is a method that was generated by a maplike/setlike # interface, use the maplike/setlike generator to fill in the body. # Otherwise, use CGCallGenerator to call the native method. @@ -10985,7 +11080,8 @@ class CGProxySpecialOperation(CGPerSignatureCall): # CGPerSignatureCall won't do any argument conversion of its own. CGPerSignatureCall.__init__(self, returnType, arguments, nativeName, False, descriptor, operation, - len(arguments), resultVar=resultVar) + len(arguments), resultVar=resultVar, + objectName="proxy") if operation.isSetter() or operation.isCreator(): # arguments[0] is the index or name of the item that we're setting. @@ -13755,12 +13851,18 @@ class CGBindingRoot(CGThing): iface = desc.interface return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]) + def descriptorHasCEReactions(desc): + iface = desc.interface + return any(m.getExtendedAttribute("CEReactions") for m in iface.members + [iface]) + bindingHeaders["nsIDocument.h"] = any( descriptorDeprecated(d) for d in descriptors) bindingHeaders["mozilla/Preferences.h"] = any( descriptorRequiresPreferences(d) for d in descriptors) bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( d.concrete and d.proxy for d in descriptors) + bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = any( + descriptorHasCEReactions(d) for d in descriptors) def descriptorHasChromeOnly(desc): ctor = desc.interface.ctor() diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 4f602365b..81911996d 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -1582,7 +1582,7 @@ class IDLInterface(IDLInterfaceOrNamespace): [self.location]) self._noInterfaceObject = True - elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor": + elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor": if identifier == "Constructor" and not self.hasInterfaceObject(): raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible", [self.location]) @@ -1595,11 +1595,20 @@ class IDLInterface(IDLInterfaceOrNamespace): raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible", [self.location]) + if identifier == "HTMLConstructor": + if not self.hasInterfaceObject(): + raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible", + [self.location]) + + if not attr.noArguments(): + raise WebIDLError(str(identifier) + " must take no arguments", + [attr.location]) + args = attr.args() if attr.hasArgs() else [] retType = IDLWrapperType(self.location, self) - if identifier == "Constructor" or identifier == "ChromeConstructor": + if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor": name = "constructor" allowForbidden = True else: @@ -1610,7 +1619,8 @@ class IDLInterface(IDLInterfaceOrNamespace): allowForbidden=allowForbidden) method = IDLMethod(self.location, methodIdentifier, retType, - args, static=True) + args, static=True, + htmlConstructor=(identifier == "HTMLConstructor")) # Constructors are always NewObject and are always # assumed to be able to throw (since there's no way to # indicate otherwise) and never have any other @@ -1622,7 +1632,7 @@ class IDLInterface(IDLInterfaceOrNamespace): method.addExtendedAttributes( [IDLExtendedAttribute(self.location, ("ChromeOnly",))]) - if identifier == "Constructor" or identifier == "ChromeConstructor": + if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor": method.resolve(self) else: # We need to detect conflicts for NamedConstructors across @@ -4073,6 +4083,11 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("Attribute returns a type that is not exposed " "everywhere where the attribute is exposed", [self.location]) + if self.getExtendedAttribute("CEReactions"): + if self.readonly: + raise WebIDLError("[CEReactions] is not allowed on " + "readonly attributes", + [self.location]) def handleExtendedAttribute(self, attr): identifier = attr.identifier() @@ -4243,6 +4258,10 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("[Unscopable] is only allowed on non-static " "attributes and operations", [attr.location, self.location]) + elif identifier == "CEReactions": + if not attr.noArguments(): + raise WebIDLError("[CEReactions] must take no arguments", + [attr.location]) elif (identifier == "Pref" or identifier == "Deprecated" or identifier == "SetterThrows" or @@ -4537,7 +4556,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): static=False, getter=False, setter=False, creator=False, deleter=False, specialType=NamedOrIndexed.Neither, legacycaller=False, stringifier=False, jsonifier=False, - maplikeOrSetlikeOrIterable=None): + maplikeOrSetlikeOrIterable=None, htmlConstructor=False): # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up. IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.Tags.Method) @@ -4567,6 +4586,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope): self._jsonifier = jsonifier assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase) self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable + assert isinstance(htmlConstructor, bool) + # The identifier of a HTMLConstructor must be 'constructor'. + assert not htmlConstructor or identifier.name == "constructor" + self._htmlConstructor = htmlConstructor self._specialType = specialType self._unforgeable = False self.dependsOn = "Everything" @@ -4667,6 +4690,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope): self.isStringifier() or self.isJsonifier()) + def isHTMLConstructor(self): + return self._htmlConstructor + def hasOverloads(self): return self._hasOverloads @@ -4722,6 +4748,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert not method.isStringifier() assert not self.isJsonifier() assert not method.isJsonifier() + assert not self.isHTMLConstructor() + assert not method.isHTMLConstructor() return self @@ -4964,6 +4992,15 @@ class IDLMethod(IDLInterfaceMember, IDLScope): raise WebIDLError("[Unscopable] is only allowed on non-static " "attributes and operations", [attr.location, self.location]) + elif identifier == "CEReactions": + if not attr.noArguments(): + raise WebIDLError("[CEReactions] must take no arguments", + [attr.location]) + + if self.isSpecial() and not self.isSetter() and not self.isDeleter(): + raise WebIDLError("[CEReactions] is only allowed on operation, " + "attribute, setter, and deleter", + [attr.location, self.location]) elif (identifier == "Throws" or identifier == "NewObject" or identifier == "ChromeOnly" or diff --git a/dom/bindings/parser/tests/test_cereactions.py b/dom/bindings/parser/tests/test_cereactions.py new file mode 100644 index 000000000..2f9397d90 --- /dev/null +++ b/dom/bindings/parser/tests/test_cereactions.py @@ -0,0 +1,162 @@ +def WebIDLTest(parser, harness): + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions(DOMString a)] void foo(boolean arg2); + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] with an argument") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions(DOMString b)] readonly attribute boolean bar; + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] with an argument") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] attribute boolean bar; + }; + """) + + results = parser.finish() + except Exception, e: + harness.ok(False, "Shouldn't have thrown for [CEReactions] used on writable attribute. %s" % e) + threw = True + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] void foo(boolean arg2); + }; + """) + + results = parser.finish() + except Exception, e: + harness.ok(False, "Shouldn't have thrown for [CEReactions] used on regular operations. %s" % e) + threw = True + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] readonly attribute boolean A; + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] used on a readonly attribute") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [CEReactions] + interface Foo { + } + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] used on a interface") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] getter any(DOMString name); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown for [CEReactions] used on a named getter") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] creator boolean (DOMString name, boolean value); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown for [CEReactions] used on a named creator") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] legacycaller double compute(double x); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown for [CEReactions] used on a legacycaller") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] stringifier DOMString (); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown for [CEReactions] used on a stringifier") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] jsonifier; + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] used on a jsonifier") diff --git a/dom/bindings/parser/tests/test_constructor.py b/dom/bindings/parser/tests/test_constructor.py index 348204c7d..fb651c08d 100644 --- a/dom/bindings/parser/tests/test_constructor.py +++ b/dom/bindings/parser/tests/test_constructor.py @@ -13,7 +13,7 @@ def WebIDLTest(parser, harness): def checkMethod(method, QName, name, signatures, static=True, getter=False, setter=False, creator=False, deleter=False, legacycaller=False, stringifier=False, - chromeOnly=False): + chromeOnly=False, htmlConstructor=False): harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod") harness.ok(method.isMethod(), "Method is a method") @@ -29,6 +29,7 @@ def WebIDLTest(parser, harness): harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value") harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value") harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly") + harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value") harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures") sigpairs = zip(method.signatures(), signatures) @@ -80,6 +81,21 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" + [HTMLConstructor] + interface TestHTMLConstructor { + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestHTMLConstructor::constructor", + "constructor", [("TestHTMLConstructor (Wrapper)", [])], + htmlConstructor=True) + + parser = parser.reset() + parser.parse(""" [ChromeConstructor()] interface TestChromeConstructor { }; @@ -107,3 +123,151 @@ def WebIDLTest(parser, harness): threw = True harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor") + + # Test HTMLConstructor with argument + parser = parser.reset() + threw = False + try: + parser.parse(""" + [HTMLConstructor(DOMString a)] + interface TestHTMLConstructorWithArgs { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "HTMLConstructor should take no argument") + + # Test HTMLConstructor on a callback interface + parser = parser.reset() + threw = False + try: + parser.parse(""" + [HTMLConstructor] + callback interface TestHTMLConstructorOnCallbackInterface { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "HTMLConstructor can't be used on a callback interface") + + # Test HTMLConstructor and Constructor + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Constructor, + HTMLConstructor] + interface TestHTMLConstructorAndConstructor { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Can't have both a Constructor and a HTMLConstructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [HTMLConstructor, + Constructor] + interface TestHTMLConstructorAndConstructor { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [HTMLConstructor, + Constructor(DOMString a)] + interface TestHTMLConstructorAndConstructor { + }; + """) + except: + threw = True + + harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Constructor(DOMString a), + HTMLConstructor] + interface TestHTMLConstructorAndConstructor { + }; + """) + except: + threw = True + + harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor") + + # Test HTMLConstructor and ChromeConstructor + parser = parser.reset() + threw = False + try: + parser.parse(""" + [ChromeConstructor, + HTMLConstructor] + interface TestHTMLConstructorAndChromeConstructor { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [HTMLConstructor, + ChromeConstructor] + interface TestHTMLConstructorAndChromeConstructor { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [ChromeConstructor(DOMString a), + HTMLConstructor] + interface TestHTMLConstructorAndChromeConstructor { + }; + """) + results = parser.finish() + except: + threw = True + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [HTMLConstructor, + ChromeConstructor(DOMString a)] + interface TestHTMLConstructorAndChromeConstructor { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
\ No newline at end of file diff --git a/dom/bindings/parser/tests/test_constructor_no_interface_object.py b/dom/bindings/parser/tests/test_constructor_no_interface_object.py index 2b09ae71e..af7b73d67 100644 --- a/dom/bindings/parser/tests/test_constructor_no_interface_object.py +++ b/dom/bindings/parser/tests/test_constructor_no_interface_object.py @@ -34,3 +34,36 @@ def WebIDLTest(parser, harness): interface TestNamedConstructorNoInterfaceObject { }; """) + + # Test HTMLConstructor and NoInterfaceObject + parser = parser.reset() + + threw = False + try: + parser.parse(""" + [NoInterfaceObject, HTMLConstructor] + interface TestHTMLConstructorNoInterfaceObject { + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + + threw = False + try: + parser.parse(""" + [HTMLConstructor, NoInterfaceObject] + interface TestHTMLConstructorNoInterfaceObject { + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown.")
\ No newline at end of file diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index ca5aafdc5..7d9963700 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -939,6 +939,11 @@ public: void NeedsCallerTypeMethod(CallerType); bool NeedsCallerTypeAttr(CallerType); void SetNeedsCallerTypeAttr(bool, CallerType); + void CeReactionsMethod(); + void CeReactionsMethodOverload(); + void CeReactionsMethodOverload(const nsAString&); + bool CeReactionsAttr() const; + void SetCeReactionsAttr(bool); int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&); void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&, TestInterface*, const Dict&, double, @@ -1425,6 +1430,31 @@ public: void SetNeedsCallerTypeAttr(bool, CallerType); }; +class TestHTMLConstructorInterface : public nsGenericHTMLElement +{ +public: + virtual nsISupports* GetParentObject(); +}; + +class TestCEReactionsInterface : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_ISUPPORTS + + // We need a GetParentObject to make binding codegen happy + virtual nsISupports* GetParentObject(); + + int32_t Item(uint32_t); + uint32_t Length() const; + int32_t IndexedGetter(uint32_t, bool &); + void IndexedSetter(uint32_t, int32_t); + void NamedDeleter(const nsAString&, bool &); + void NamedGetter(const nsAString&, bool &, nsString&); + void NamedSetter(const nsAString&, const nsAString&); + void GetSupportedNames(nsTArray<nsString>&); +}; + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index 4fb9be270..3fce5e21b 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -947,6 +947,10 @@ interface TestInterface { [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr; [NeedsCallerType] void needsCallerTypeMethod(); [NeedsCallerType] attribute boolean needsCallerTypeAttr; + [CEReactions] void ceReactionsMethod(); + [CEReactions] void ceReactionsMethodOverload(); + [CEReactions] void ceReactionsMethodOverload(DOMString bar); + [CEReactions] attribute boolean ceReactionsAttr; legacycaller short(unsigned long arg1, TestInterface arg2); void passArgsWithDefaults(optional long arg1, optional TestInterface? arg2 = null, @@ -1262,3 +1266,16 @@ interface TestWorkerExposedInterface { [NeedsCallerType] void needsCallerTypeMethod(); [NeedsCallerType] attribute boolean needsCallerTypeAttr; }; + +[HTMLConstructor] +interface TestHTMLConstructorInterface { +}; + +interface TestCEReactionsInterface { + [CEReactions] setter creator void (unsigned long index, long item); + [CEReactions] setter creator void (DOMString name, DOMString item); + [CEReactions] deleter void (DOMString name); + getter long item(unsigned long index); + getter DOMString (DOMString name); + readonly attribute unsigned long length; +}; diff --git a/dom/bindings/test/TestExampleGen.webidl b/dom/bindings/test/TestExampleGen.webidl index ea6387a84..a2183c002 100644 --- a/dom/bindings/test/TestExampleGen.webidl +++ b/dom/bindings/test/TestExampleGen.webidl @@ -777,6 +777,10 @@ interface TestExampleInterface { [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr; [NeedsCallerType] void needsCallerTypeMethod(); [NeedsCallerType] attribute boolean needsCallerTypeAttr; + [CEReactions] void ceReactionsMethod(); + [CEReactions] void ceReactionsMethodOverload(); + [CEReactions] void ceReactionsMethodOverload(DOMString bar); + [CEReactions] attribute boolean ceReactionsAttr; legacycaller short(unsigned long arg1, TestInterface arg2); void passArgsWithDefaults(optional long arg1, optional TestInterface? arg2 = null, diff --git a/dom/bindings/test/TestJSImplGen.webidl b/dom/bindings/test/TestJSImplGen.webidl index a131dcdfe..a133b9981 100644 --- a/dom/bindings/test/TestJSImplGen.webidl +++ b/dom/bindings/test/TestJSImplGen.webidl @@ -794,6 +794,10 @@ interface TestJSImplInterface { [Throws] attribute boolean throwingAttr; [GetterThrows] attribute boolean throwingGetterAttr; [SetterThrows] attribute boolean throwingSetterAttr; + [CEReactions] void ceReactionsMethod(); + [CEReactions] void ceReactionsMethodOverload(); + [CEReactions] void ceReactionsMethodOverload(DOMString bar); + [CEReactions] attribute boolean ceReactionsAttr; // NeedsSubjectPrincipal not supported on JS-implemented things for // now, because we always pass in the caller principal anyway. // [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod(); diff --git a/dom/bindings/test/test_bug560072.html b/dom/bindings/test/test_bug560072.html index 82bb1c2c6..0eebff116 100644 --- a/dom/bindings/test/test_bug560072.html +++ b/dom/bindings/test/test_bug560072.html @@ -20,11 +20,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=560072 /** Test for Bug 560072 **/ is(document.body, - Object.getOwnPropertyDescriptor(HTMLDocument.prototype, "body").get.call(document), + Object.getOwnPropertyDescriptor(Document.prototype, "body").get.call(document), "Should get body out of property descriptor"); is(document.body, - Object.getOwnPropertyDescriptor(Object.getPrototypeOf(document), "body").get.call(document), + Object.getOwnPropertyDescriptor( + Object.getPrototypeOf(Object.getPrototypeOf(document)), "body").get.call(document), "Should get body out of property descriptor this way too"); </script> |