summaryrefslogtreecommitdiffstats
path: root/dom/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings')
-rw-r--r--dom/bindings/BindingUtils.cpp212
-rw-r--r--dom/bindings/BindingUtils.h15
-rw-r--r--dom/bindings/Bindings.conf9
-rw-r--r--dom/bindings/CallbackObject.cpp11
-rw-r--r--dom/bindings/Codegen.py154
-rw-r--r--dom/bindings/parser/WebIDL.py47
-rw-r--r--dom/bindings/parser/tests/test_cereactions.py162
-rw-r--r--dom/bindings/parser/tests/test_constructor.py166
-rw-r--r--dom/bindings/parser/tests/test_constructor_no_interface_object.py33
-rw-r--r--dom/bindings/test/TestBindingHeader.h30
-rw-r--r--dom/bindings/test/TestCodeGen.webidl17
-rw-r--r--dom/bindings/test/TestExampleGen.webidl4
-rw-r--r--dom/bindings/test/TestJSImplGen.webidl4
-rw-r--r--dom/bindings/test/test_bug560072.html5
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>