mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
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:
parent
2f500c507d
commit
881cbd80bd
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,14 +1792,36 @@ 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)
|
||||
|
||||
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,
|
||||
@ -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(
|
||||
# If we're a constructor, the GlobalObject struct will be created in
|
||||
# CGClassConstructor.
|
||||
if not isConstructor:
|
||||
cgThings.append(CGGeneric(dedent(
|
||||
"""
|
||||
GlobalObject global(cx, ${obj});
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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.")
|
||||
|
@ -1425,6 +1425,12 @@ public:
|
||||
void SetNeedsCallerTypeAttr(bool, CallerType);
|
||||
};
|
||||
|
||||
class TestHTMLConstructorInterface : public nsGenericHTMLElement
|
||||
{
|
||||
public:
|
||||
virtual nsISupports* GetParentObject();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -1262,3 +1262,7 @@ interface TestWorkerExposedInterface {
|
||||
[NeedsCallerType] void needsCallerTypeMethod();
|
||||
[NeedsCallerType] attribute boolean needsCallerTypeAttr;
|
||||
};
|
||||
|
||||
[HTMLConstructor]
|
||||
interface TestHTMLConstructorInterface {
|
||||
};
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user