summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2020-01-02 21:25:47 -0500
committerGaming4JC <g4jc@hyperbola.info>2020-01-26 15:50:09 -0500
commit5222f6e9daa4cb74b404f769b23510b3d600efd9 (patch)
treee00a760b5b662dcfb35e2849ea1e9d83c85617e2
parent244277a3fa428b70ba46cf611dbe1905733515eb (diff)
downloadUXP-5222f6e9daa4cb74b404f769b23510b3d600efd9.tar
UXP-5222f6e9daa4cb74b404f769b23510b3d600efd9.tar.gz
UXP-5222f6e9daa4cb74b404f769b23510b3d600efd9.tar.lz
UXP-5222f6e9daa4cb74b404f769b23510b3d600efd9.tar.xz
UXP-5222f6e9daa4cb74b404f769b23510b3d600efd9.zip
Bug 1274159 - Part 2-2: Support HTMLConstructor WebIDL extended attribute for custom elements;
Tag UXP Issue #1344
-rw-r--r--dom/base/CustomElementRegistry.cpp17
-rw-r--r--dom/base/CustomElementRegistry.h7
-rw-r--r--dom/bindings/BindingUtils.cpp153
-rw-r--r--dom/bindings/BindingUtils.h8
-rw-r--r--dom/bindings/Bindings.conf5
-rw-r--r--dom/bindings/Codegen.py132
-rw-r--r--dom/bindings/parser/WebIDL.py29
-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.h6
-rw-r--r--dom/bindings/test/TestCodeGen.webidl4
-rw-r--r--parser/htmlparser/nsHTMLTags.h4
12 files changed, 534 insertions, 30 deletions
diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp
index 43e4e7e2a..6cd213210 100644
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -273,6 +273,23 @@ CustomElementRegistry::LookupCustomElementDefinition(const nsAString& aLocalName
return nullptr;
}
+CustomElementDefinition*
+CustomElementRegistry::LookupCustomElementDefinition(JSContext* aCx,
+ JSObject* aConstructor) const
+{
+ JS::Rooted<JSObject*> constructor(aCx, js::CheckedUnwrap(aConstructor));
+
+ const auto& ptr = mConstructors.lookup(constructor);
+ if (!ptr) {
+ return nullptr;
+ }
+
+ CustomElementDefinition* definition = mCustomDefinitions.Get(ptr->value());
+ MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions");
+
+ return definition;
+}
+
void
CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
{
diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h
index 9034dd7ea..8ef0785af 100644
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -128,6 +128,10 @@ struct CustomElementDefinition
// The document custom element order.
uint32_t mDocOrder;
+
+ bool IsCustomBuiltIn() {
+ return mType != mLocalName;
+ }
};
class CustomElementRegistry final : public nsISupports,
@@ -155,6 +159,9 @@ public:
CustomElementDefinition* LookupCustomElementDefinition(
const nsAString& aLocalName, const nsAString* aIs = nullptr) const;
+ CustomElementDefinition* LookupCustomElementDefinition(
+ JSContext* aCx, JSObject *aConstructor) const;
+
/**
* Enqueue created callback or register upgrade candidate for
* newly created custom elements, possibly extending an existing type.
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index b244d4d2a..6bc6b7143 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -19,10 +19,12 @@
#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 +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 },
@@ -3377,6 +3405,131 @@ 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,
+ 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.
+ // Construction stack will be implemented in bug 1287348. So we always run
+ // "construction stack is empty" case for now.
+ RefPtr<nsGenericHTMLElement> element;
+ if (tag == eHTMLTag_userdefined) {
+ // Autonomous custom element.
+ element = NS_NewHTMLElement(nodeInfo.forget());
+ } else {
+ // Customized built-in element.
+ element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
+ }
+
+ return element.forget();
+}
+
#ifdef DEBUG
namespace binding_detail {
void
diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h
index 5cab835b3..2563c5fee 100644
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -42,6 +42,7 @@
#include "nsWrapperCacheInlines.h"
+class nsGenericHTMLElement;
class nsIJSID;
namespace mozilla {
@@ -3420,6 +3421,13 @@ bool
GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
JS::MutableHandle<JSObject*> aDesiredProto);
+// 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,
+ ErrorResult& aRv);
+
void
SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
UseCounter aUseCounter);
diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
index b00af2085..6eebaf60c 100644
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1638,6 +1638,11 @@ DOMInterfaces = {
'register': False,
},
+'TestHTMLConstructorInterface' : {
+ 'headerFile': 'TestBindingHeader.h',
+ 'register': False,
+ },
+
}
# These are temporary, until they've been converted to use new DOM bindings
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py
index 6b23e8225..676d91793 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()
@@ -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.append("args")
+
# 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
diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py
index 4f602365b..a89620a9f 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
@@ -4537,7 +4547,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 +4577,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 +4681,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self.isStringifier() or
self.isJsonifier())
+ def isHTMLConstructor(self):
+ return self._htmlConstructor
+
def hasOverloads(self):
return self._hasOverloads
@@ -4722,6 +4739,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
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..9bd3e407f 100644
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -1425,6 +1425,12 @@ public:
void SetNeedsCallerTypeAttr(bool, CallerType);
};
+class TestHTMLConstructorInterface : public nsGenericHTMLElement
+{
+public:
+ virtual nsISupports* GetParentObject();
+};
+
} // namespace dom
} // namespace mozilla
diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl
index 4fb9be270..35777f6aa 100644
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -1262,3 +1262,7 @@ interface TestWorkerExposedInterface {
[NeedsCallerType] void needsCallerTypeMethod();
[NeedsCallerType] attribute boolean needsCallerTypeAttr;
};
+
+[HTMLConstructor]
+interface TestHTMLConstructorInterface {
+};
diff --git a/parser/htmlparser/nsHTMLTags.h b/parser/htmlparser/nsHTMLTags.h
index a2800df07..27a23b89f 100644
--- a/parser/htmlparser/nsHTMLTags.h
+++ b/parser/htmlparser/nsHTMLTags.h
@@ -17,6 +17,10 @@ class nsIAtom;
To change the list of tags, see nsHTMLTagList.h
+ These enum values are used as the index of array in various places.
+ If we change the structure of the enum by adding entries to it or removing
+ entries from it _directly_, not via nsHTMLTagList.h, don't forget to update
+ dom/bindings/BindingUtils.cpp and dom/html/nsHTMLContentSink.cpp as well.
*/
#define HTML_TAG(_tag, _classname, _interfacename) eHTMLTag_##_tag,
#define HTML_OTHER(_tag) eHTMLTag_##_tag,