Bug 1274159 - Part 2-2: Support HTMLConstructor WebIDL extended attribute for custom elements; r=bz,wchen

MozReview-Commit-ID: 6egmavfTmFT
This commit is contained in:
Edgar Chen 2016-11-17 23:31:50 +08:00
parent 2f500c507d
commit 881cbd80bd
12 changed files with 533 additions and 29 deletions

View File

@ -275,6 +275,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)
{

View File

@ -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.

View File

@ -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 },
@ -3328,6 +3356,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

View File

@ -42,6 +42,7 @@
#include "nsWrapperCacheInlines.h"
class nsGenericHTMLElement;
class nsIJSID;
namespace mozilla {
@ -3180,6 +3181,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);

View File

@ -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

View File

@ -1717,6 +1717,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);
@ -1727,19 +1792,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)
ctorName=ctorName,
htmlConstructorSanityCheck=htmlConstructorSanityCheck,
htmlConstructorFallback=htmlConstructorFallback)
name = self._ctor.identifier.name
nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
callGenerator = CGMethodCall(nativeName, True, self.descriptor,
self._ctor, isConstructor=True,
constructorName=ctorName)
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()
@ -7272,26 +7359,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

View File

@ -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,6 +1595,15 @@ 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 []
if self.identifier.name == "Promise":
@ -1603,7 +1612,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
promiseType = None
retType = IDLWrapperType(self.location, self, promiseType)
if identifier == "Constructor" or identifier == "ChromeConstructor":
if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
name = "constructor"
allowForbidden = True
else:
@ -1614,7 +1623,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
@ -1626,7 +1636,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
@ -4508,7 +4518,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)
@ -4538,6 +4548,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"
@ -4638,6 +4652,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self.isStringifier() or
self.isJsonifier())
def isHTMLConstructor(self):
return self._htmlConstructor
def hasOverloads(self):
return self._hasOverloads
@ -4693,6 +4710,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

View File

@ -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)
@ -93,6 +94,21 @@ def WebIDLTest(parser, harness):
"constructor", [("TestChromeConstructor (Wrapper)", [])],
chromeOnly=True)
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()
threw = False
try:
@ -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")

View File

@ -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.")

View File

@ -1425,6 +1425,12 @@ public:
void SetNeedsCallerTypeAttr(bool, CallerType);
};
class TestHTMLConstructorInterface : public nsGenericHTMLElement
{
public:
virtual nsISupports* GetParentObject();
};
} // namespace dom
} // namespace mozilla

View File

@ -1262,3 +1262,7 @@ interface TestWorkerExposedInterface {
[NeedsCallerType] void needsCallerTypeMethod();
[NeedsCallerType] attribute boolean needsCallerTypeAttr;
};
[HTMLConstructor]
interface TestHTMLConstructorInterface {
};

View File

@ -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,