diff options
Diffstat (limited to 'dom/bindings/Codegen.py')
-rw-r--r-- | dom/bindings/Codegen.py | 176 |
1 files changed, 149 insertions, 27 deletions
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index e3cca63b6..c9a5e9f41 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1753,6 +1753,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); @@ -1763,19 +1828,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() @@ -7392,7 +7479,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 @@ -7446,26 +7533,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 @@ -7594,6 +7678,17 @@ class CGPerSignatureCall(CGThing): CGIfWrapper(CGList(xraySteps), "objIsXray")) + if (idlNode.getExtendedAttribute('CEReactions') is not None and + not getter): + cgThings.append(CGGeneric(dedent( + """ + Maybe<AutoCEReaction> ceReaction; + DocGroup* docGroup = self->GetDocGroup(); + if (docGroup) { + ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx); + } + """))) + # 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. @@ -10991,7 +11086,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. @@ -13690,6 +13786,8 @@ class CGForwardDeclarations(CGWrapper): builder.add(d.nativeType + "Atoms", isStruct=True) for t in getTypesFromDescriptor(d): builder.forwardDeclareForType(t, config) + if d.hasCEReactions(): + builder.addInMozillaDom("DocGroup") for d in dictionaries: if len(d.members) > 0: @@ -13767,6 +13865,9 @@ class CGBindingRoot(CGThing): descriptorRequiresPreferences(d) for d in descriptors) bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( d.concrete and d.proxy for d in descriptors) + hasCEReactions = any(d.hasCEReactions() for d in descriptors) + bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions + bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions def descriptorHasChromeOnly(desc): ctor = desc.interface.ctor() @@ -14602,6 +14703,12 @@ class CGBindingImplClass(CGClass): breakAfterReturnDecl=" ", override=descriptor.wrapperCache, body=self.getWrapObjectBody())) + if descriptor.hasCEReactions(): + self.methodDecls.insert(0, + ClassMethod("GetDocGroup", "DocGroup*", [], + const=True, + breakAfterReturnDecl=" ", + body=self.getGetDocGroupBody())) if wantGetParent: self.methodDecls.insert(0, ClassMethod("GetParentObject", @@ -14622,6 +14729,9 @@ class CGBindingImplClass(CGClass): def getGetParentObjectBody(self): return None + def getGetDocGroupBody(self): + return None + def deps(self): return self._deps @@ -14761,6 +14871,8 @@ class CGExampleRoot(CGThing): self.root = CGNamespace.build(["mozilla", "dom"], self.root) builder = ForwardDeclarationBuilder() + if descriptor.hasCEReactions(): + builder.addInMozillaDom("DocGroup") for member in descriptor.interface.members: if not member.isAttr() and not member.isMethod(): continue @@ -15072,7 +15184,7 @@ class CGJSImplClass(CGBindingImplClass): private: RefPtr<${jsImplName}> mImpl; - nsCOMPtr<nsISupports> mParent; + nsCOMPtr<nsIGlobalObject> mParent; """, isupportsDecl=isupportsDecl, @@ -15151,6 +15263,16 @@ class CGJSImplClass(CGBindingImplClass): def getGetParentObjectBody(self): return "return mParent;\n" + def getGetDocGroupBody(self): + return dedent( + """ + nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent); + if (!window) { + return nullptr; + } + return window->GetDocGroup(); + """) + def getCreateFromExistingBody(self): # XXXbz we could try to get parts of this (e.g. the argument # conversions) auto-generated by somehow creating an IDLMethod and |