From 13f425b97cdfddf4ba7c5ed7b8dba41618d1014d Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 22 May 2012 15:46:20 +0200 Subject: [PATCH] Fix for bug 768692 (Move DOM list binding generation to the new DOM binding codegen). r=bzbarsky. --HG-- extra : rebase_source : 651a0fac4c9a87ef1c0a9cd91588c6421fd050c4 --- caps/src/nsScriptSecurityManager.cpp | 5 +- content/base/src/FragmentOrElement.cpp | 2 +- content/base/src/nsContentList.cpp | 4 +- content/base/src/nsDOMFile.cpp | 2 +- content/base/src/nsDOMSettableTokenList.cpp | 2 +- content/base/src/nsDOMTokenList.cpp | 2 +- content/events/src/nsPaintRequest.h | 2 +- .../content/src/HTMLPropertiesCollection.cpp | 4 +- content/html/content/src/nsClientRect.cpp | 2 +- .../html/content/src/nsHTMLFormElement.cpp | 2 +- .../html/content/src/nsHTMLSelectElement.cpp | 2 +- .../html/content/src/nsHTMLTableElement.cpp | 2 +- content/svg/content/src/DOMSVGLengthList.cpp | 2 +- content/svg/content/src/DOMSVGNumberList.cpp | 2 +- content/svg/content/src/DOMSVGPathSegList.cpp | 2 +- content/svg/content/src/DOMSVGPointList.cpp | 2 +- .../svg/content/src/DOMSVGTransformList.cpp | 2 +- content/xbl/src/nsBindingManager.cpp | 2 +- dom/base/nsDOMClassInfo.cpp | 16 +- dom/base/nsScriptNameSpaceManager.cpp | 2 +- dom/base/nsScriptNameSpaceManager.h | 4 +- dom/bindings/BindingUtils.cpp | 62 +- dom/bindings/BindingUtils.h | 124 +- dom/bindings/Bindings.conf | 46 + dom/bindings/Codegen.py | 1023 ++++++++++++++--- dom/bindings/Configuration.py | 62 +- dom/bindings/DOMJSClass.h | 26 +- dom/bindings/DOMJSProxyHandler.cpp | 228 ++++ dom/bindings/DOMJSProxyHandler.h | 109 ++ dom/bindings/Errors.msg | 1 + dom/bindings/Makefile.in | 2 + dom/bindings/parser/WebIDL.py | 3 + dom/bindings/test/TestBindingHeader.h | 96 ++ dom/bindings/test/TestCodeGen.webidl | 37 + dom/workers/Worker.cpp | 36 +- dom/workers/WorkerScope.cpp | 19 +- js/xpconnect/src/XPCConvert.cpp | 7 +- js/xpconnect/src/XPCJSID.cpp | 9 +- js/xpconnect/src/XPCJSRuntime.cpp | 14 +- js/xpconnect/src/XPCQuickStubs.cpp | 22 +- js/xpconnect/src/XPCWrappedNative.cpp | 7 +- js/xpconnect/src/XPCWrappedNativeScope.cpp | 21 +- js/xpconnect/src/codegen.py | 2 + js/xpconnect/src/dombindings.cpp | 2 +- js/xpconnect/src/dombindings.h | 2 +- js/xpconnect/src/dombindingsgen.py | 6 +- js/xpconnect/src/nsXPConnect.cpp | 19 +- js/xpconnect/src/xpcpublic.h | 22 +- js/xpconnect/wrappers/WrapperFactory.cpp | 4 +- js/xpconnect/wrappers/XrayWrapper.cpp | 6 +- 50 files changed, 1768 insertions(+), 314 deletions(-) create mode 100644 dom/bindings/DOMJSProxyHandler.cpp create mode 100644 dom/bindings/DOMJSProxyHandler.h diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index d99545a0b80..0a58a6f2306 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -2399,10 +2399,7 @@ nsScriptSecurityManager::old_doGetObjectPrincipal(JSObject *aObj, if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { priv = (nsISupports *) js::GetObjectPrivate(aObj); - } else if (IsDOMClass(jsClass) && - DOMJSClass::FromJSClass(jsClass)->mDOMObjectIsISupports) { - priv = UnwrapDOMObject(aObj); - } else { + } else if (!UnwrapDOMObjectToISupports(aObj, priv)) { priv = nullptr; } diff --git a/content/base/src/FragmentOrElement.cpp b/content/base/src/FragmentOrElement.cpp index c86b6a32151..621ca47f16a 100644 --- a/content/base/src/FragmentOrElement.cpp +++ b/content/base/src/FragmentOrElement.cpp @@ -391,7 +391,7 @@ JSObject* nsChildContentList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::NodeList::create(cx, scope, this, triedToWrap); + return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this, triedToWrap); } NS_IMETHODIMP diff --git a/content/base/src/nsContentList.cpp b/content/base/src/nsContentList.cpp index 3cd20b6386c..eaa6451c74d 100644 --- a/content/base/src/nsContentList.cpp +++ b/content/base/src/nsContentList.cpp @@ -162,7 +162,7 @@ JSObject* nsSimpleContentList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::NodeList::create(cx, scope, this, triedToWrap); + return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this, triedToWrap); } // nsFormContentList @@ -478,7 +478,7 @@ nsContentList::~nsContentList() JSObject* nsContentList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::HTMLCollection::create(cx, scope, this, + return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this, triedToWrap); } diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index 29be038e588..27188432513 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -688,7 +688,7 @@ JSObject* nsDOMFileList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::FileList::create(cx, scope, this, triedToWrap); + return mozilla::dom::oldproxybindings::FileList::create(cx, scope, this, triedToWrap); } nsIDOMFile* diff --git a/content/base/src/nsDOMSettableTokenList.cpp b/content/base/src/nsDOMSettableTokenList.cpp index 0239455ff0b..0f6d607444a 100644 --- a/content/base/src/nsDOMSettableTokenList.cpp +++ b/content/base/src/nsDOMSettableTokenList.cpp @@ -51,6 +51,6 @@ JSObject* nsDOMSettableTokenList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::DOMSettableTokenList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::DOMSettableTokenList::create(cx, scope, this, triedToWrap); } diff --git a/content/base/src/nsDOMTokenList.cpp b/content/base/src/nsDOMTokenList.cpp index ba3ce56f565..b85ad8ce268 100644 --- a/content/base/src/nsDOMTokenList.cpp +++ b/content/base/src/nsDOMTokenList.cpp @@ -272,7 +272,7 @@ nsDOMTokenList::ToString(nsAString& aResult) JSObject* nsDOMTokenList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::DOMTokenList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::DOMTokenList::create(cx, scope, this, triedToWrap); } diff --git a/content/events/src/nsPaintRequest.h b/content/events/src/nsPaintRequest.h index 3cf9f4f70f9..d6e3584220d 100644 --- a/content/events/src/nsPaintRequest.h +++ b/content/events/src/nsPaintRequest.h @@ -46,7 +46,7 @@ public: virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::PaintRequestList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::PaintRequestList::create(cx, scope, this, triedToWrap); } diff --git a/content/html/content/src/HTMLPropertiesCollection.cpp b/content/html/content/src/HTMLPropertiesCollection.cpp index 1b43a9b970c..fbc03edee6d 100644 --- a/content/html/content/src/HTMLPropertiesCollection.cpp +++ b/content/html/content/src/HTMLPropertiesCollection.cpp @@ -108,7 +108,7 @@ JSObject* HTMLPropertiesCollection::WrapObject(JSContext* cx, JSObject* scope, bool* triedToWrap) { - return mozilla::dom::binding::HTMLPropertiesCollection::create(cx, scope, this, + return mozilla::dom::oldproxybindings::HTMLPropertiesCollection::create(cx, scope, this, triedToWrap); } @@ -426,7 +426,7 @@ JSObject* PropertyNodeList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::PropertyNodeList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::PropertyNodeList::create(cx, scope, this, triedToWrap); } diff --git a/content/html/content/src/nsClientRect.cpp b/content/html/content/src/nsClientRect.cpp index 2495df5bbbc..2414e903355 100644 --- a/content/html/content/src/nsClientRect.cpp +++ b/content/html/content/src/nsClientRect.cpp @@ -106,7 +106,7 @@ nsClientRectList::GetItemAt(uint32_t aIndex) JSObject* nsClientRectList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::ClientRectList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::ClientRectList::create(cx, scope, this, triedToWrap); } diff --git a/content/html/content/src/nsHTMLFormElement.cpp b/content/html/content/src/nsHTMLFormElement.cpp index 0e60c4c341a..304fff91cae 100644 --- a/content/html/content/src/nsHTMLFormElement.cpp +++ b/content/html/content/src/nsHTMLFormElement.cpp @@ -121,7 +121,7 @@ public: virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::HTMLCollection::create(cx, scope, this, + return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this, triedToWrap); } diff --git a/content/html/content/src/nsHTMLSelectElement.cpp b/content/html/content/src/nsHTMLSelectElement.cpp index bfd7be37630..91391aa7493 100644 --- a/content/html/content/src/nsHTMLSelectElement.cpp +++ b/content/html/content/src/nsHTMLSelectElement.cpp @@ -2015,7 +2015,7 @@ JSObject* nsHTMLOptionCollection::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::HTMLOptionsCollection::create(cx, scope, this, + return mozilla::dom::oldproxybindings::HTMLOptionsCollection::create(cx, scope, this, triedToWrap); } diff --git a/content/html/content/src/nsHTMLTableElement.cpp b/content/html/content/src/nsHTMLTableElement.cpp index e79ad54ef41..5b4113605d3 100644 --- a/content/html/content/src/nsHTMLTableElement.cpp +++ b/content/html/content/src/nsHTMLTableElement.cpp @@ -56,7 +56,7 @@ public: virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::HTMLCollection::create(cx, scope, this, + return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this, triedToWrap); } diff --git a/content/svg/content/src/DOMSVGLengthList.cpp b/content/svg/content/src/DOMSVGLengthList.cpp index 38e0a621753..f7e7402052e 100644 --- a/content/svg/content/src/DOMSVGLengthList.cpp +++ b/content/svg/content/src/DOMSVGLengthList.cpp @@ -76,7 +76,7 @@ NS_INTERFACE_MAP_END JSObject* DOMSVGLengthList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::SVGLengthList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::SVGLengthList::create(cx, scope, this, triedToWrap); } diff --git a/content/svg/content/src/DOMSVGNumberList.cpp b/content/svg/content/src/DOMSVGNumberList.cpp index 23791bc4b75..8aa05973702 100644 --- a/content/svg/content/src/DOMSVGNumberList.cpp +++ b/content/svg/content/src/DOMSVGNumberList.cpp @@ -77,7 +77,7 @@ NS_INTERFACE_MAP_END JSObject* DOMSVGNumberList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::SVGNumberList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::SVGNumberList::create(cx, scope, this, triedToWrap); } diff --git a/content/svg/content/src/DOMSVGPathSegList.cpp b/content/svg/content/src/DOMSVGPathSegList.cpp index 33ffba003cd..3dfd378a3d0 100644 --- a/content/svg/content/src/DOMSVGPathSegList.cpp +++ b/content/svg/content/src/DOMSVGPathSegList.cpp @@ -84,7 +84,7 @@ DOMSVGPathSegList::~DOMSVGPathSegList() JSObject* DOMSVGPathSegList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::SVGPathSegList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::SVGPathSegList::create(cx, scope, this, triedToWrap); } diff --git a/content/svg/content/src/DOMSVGPointList.cpp b/content/svg/content/src/DOMSVGPointList.cpp index 1e4cf3a5540..5e5db231b77 100644 --- a/content/svg/content/src/DOMSVGPointList.cpp +++ b/content/svg/content/src/DOMSVGPointList.cpp @@ -103,7 +103,7 @@ DOMSVGPointList::~DOMSVGPointList() JSObject* DOMSVGPointList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::SVGPointList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::SVGPointList::create(cx, scope, this, triedToWrap); } diff --git a/content/svg/content/src/DOMSVGTransformList.cpp b/content/svg/content/src/DOMSVGTransformList.cpp index 5e38d323454..d3bd1a611c7 100644 --- a/content/svg/content/src/DOMSVGTransformList.cpp +++ b/content/svg/content/src/DOMSVGTransformList.cpp @@ -77,7 +77,7 @@ JSObject* DOMSVGTransformList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::SVGTransformList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::SVGTransformList::create(cx, scope, this, triedToWrap); } diff --git a/content/xbl/src/nsBindingManager.cpp b/content/xbl/src/nsBindingManager.cpp index 5700df7d426..664c0279fe5 100644 --- a/content/xbl/src/nsBindingManager.cpp +++ b/content/xbl/src/nsBindingManager.cpp @@ -80,7 +80,7 @@ public: virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap) { - return mozilla::dom::binding::NodeList::create(cx, scope, this, + return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this, triedToWrap); } diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index a449ca419c2..96aee42ad6f 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -543,6 +543,7 @@ using mozilla::dom::indexedDB::IDBWrapperCache; #undef None // something included above defines this preprocessor symbol, maybe Xlib headers #include "WebGLContext.h" #include "nsICanvasRenderingContextInternal.h" +#include "mozilla/dom/BindingUtils.h" using namespace mozilla; using namespace mozilla::dom; @@ -4575,12 +4576,13 @@ nsDOMClassInfo::Init() sDisableGlobalScopePollutionSupport = Preferences::GetBool("browser.dom.global_scope_pollution.disabled"); - // Proxy bindings - mozilla::dom::binding::Register(nameSpaceManager); - // Non-proxy bindings mozilla::dom::Register(nameSpaceManager); + // This needs to happen after the call to mozilla::dom::Register, because we + // overwrite some values. + mozilla::dom::oldproxybindings::Register(nameSpaceManager); + if (!AzureCanvasEnabled()) { nameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING("CanvasRenderingContext2D"), NULL); } @@ -6746,7 +6748,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, name_struct->mType == nsGlobalNameStruct::eTypeClassProto || name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { // Lookup new DOM bindings. - mozilla::dom::binding::DefineInterface define = + mozilla::dom::DefineInterface define = name_struct->mDefineDOMInterface; if (define) { if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor && @@ -6754,7 +6756,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, return NS_OK; } - if (mozilla::dom::binding::DefineConstructor(cx, obj, define, &rv)) { + if (mozilla::dom::DefineConstructor(cx, obj, define, &rv)) { *did_resolve = NS_SUCCEEDED(rv); return rv; @@ -8791,9 +8793,9 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx, JSObject *obj, if (!JSVAL_IS_PRIMITIVE(collection)) { // We already have a node list in our reserved slot, use it. JSObject *obj = JSVAL_TO_OBJECT(collection); - if (mozilla::dom::binding::HTMLCollection::objIsWrapper(obj)) { + if (mozilla::dom::oldproxybindings::HTMLCollection::objIsWrapper(obj)) { nsIHTMLCollection *native = - mozilla::dom::binding::HTMLCollection::getNative(obj); + mozilla::dom::oldproxybindings::HTMLCollection::getNative(obj); NS_ADDREF(*nodeList = static_cast(native)); } else { diff --git a/dom/base/nsScriptNameSpaceManager.cpp b/dom/base/nsScriptNameSpaceManager.cpp index b63d35e73c9..6d0a8d6a1ef 100644 --- a/dom/base/nsScriptNameSpaceManager.cpp +++ b/dom/base/nsScriptNameSpaceManager.cpp @@ -781,7 +781,7 @@ nsScriptNameSpaceManager::Observe(nsISupports* aSubject, const char* aTopic, void nsScriptNameSpaceManager::RegisterDefineDOMInterface(const nsAFlatString& aName, - mozilla::dom::binding::DefineInterface aDefineDOMInterface) + mozilla::dom::DefineInterface aDefineDOMInterface) { nsGlobalNameStruct *s = AddToHash(&mGlobalNames, &aName); if (s) { diff --git a/dom/base/nsScriptNameSpaceManager.h b/dom/base/nsScriptNameSpaceManager.h index 2b3af056919..a054cebe866 100644 --- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -67,7 +67,7 @@ struct nsGlobalNameStruct }; // For new style DOM bindings. - mozilla::dom::binding::DefineInterface mDefineDOMInterface; + mozilla::dom::DefineInterface mDefineDOMInterface; private: @@ -139,7 +139,7 @@ public: nsGlobalNameStruct* GetConstructorProto(const nsGlobalNameStruct* aStruct); void RegisterDefineDOMInterface(const nsAFlatString& aName, - mozilla::dom::binding::DefineInterface aDefineDOMInterface); + mozilla::dom::DefineInterface aDefineDOMInterface); private: // Adds a new entry to the hash and returns the nsGlobalNameStruct diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 4a1fec3cbe2..2695b365db2 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -8,6 +8,7 @@ #include "BindingUtils.h" +#include "WrapperFactory.h" #include "xpcprivate.h" #include "XPCQuickStubs.h" @@ -242,7 +243,7 @@ JSObject* CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver, JSObject* protoProto, JSClass* protoClass, JSClass* constructorClass, JSNative constructor, - unsigned ctorNargs, JSClass* instanceClass, + unsigned ctorNargs, const DOMClass* domClass, Prefable* methods, Prefable* properties, Prefable* constants, @@ -267,7 +268,7 @@ CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver, } js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT, - JS::PrivateValue(instanceClass)); + JS::PrivateValue(const_cast(domClass))); } else { proto = NULL; @@ -333,10 +334,8 @@ JSBool InstanceClassHasProtoAtDepth(JSHandleObject protoObject, uint32_t protoID, uint32_t depth) { - JSClass* instanceClass = static_cast( + const DOMClass* domClass = static_cast( js::GetReservedSlot(protoObject, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate()); - MOZ_ASSERT(IsDOMClass(instanceClass)); - DOMJSClass* domClass = DOMJSClass::FromJSClass(instanceClass); return (uint32_t)domClass->mInterfaceChain[depth] == protoID; } @@ -377,14 +376,11 @@ QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) if (!obj) return false; - JSClass* clasp = js::GetObjectJSClass(obj); - if (!IsDOMClass(clasp) || - !DOMJSClass::FromJSClass(clasp)->mDOMObjectIsISupports) { + nsISupports* native; + if (!UnwrapDOMObjectToISupports(obj, native)) { return Throw(cx, NS_ERROR_FAILURE); } - nsISupports* native = UnwrapDOMObject(obj); - if (argc < 1) { return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); } @@ -451,8 +447,9 @@ XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id, JSFunction *fun = JS_NewFunctionById(cx, methodSpecs[i].call.op, methodSpecs[i].nargs, 0, wrapper, id); - if (!fun) - return false; + if (!fun) { + return false; + } SET_JITINFO(fun, methodSpecs[i].call.info); JSObject *funobj = JS_GetFunctionObject(fun); desc->value.setObject(*funobj); @@ -590,5 +587,46 @@ XrayEnumerateProperties(JS::AutoIdVector& props, return true; } +bool +GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found, + JS::Value* vp) +{ + JSObject* proto = js::GetObjectProto(proxy); + if (!proto) { + *found = false; + return true; + } + + JSBool hasProp; + if (!JS_HasPropertyById(cx, proto, id, &hasProp)) { + return false; + } + + *found = hasProp; + if (!hasProp || !vp) { + return true; + } + + return JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp); +} + +bool +HasPropertyOnPrototype(JSContext* cx, JSObject* proxy, DOMProxyHandler* handler, + jsid id) +{ + JSAutoEnterCompartment ac; + if (xpc::WrapperFactory::IsXrayWrapper(proxy)) { + proxy = js::UnwrapObject(proxy); + if (!ac.enter(cx, proxy)) { + return false; + } + } + MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler); + + bool found; + // We ignore an error from GetPropertyOnPrototype. + return !GetPropertyOnPrototype(cx, proxy, id, &found, NULL) || found; +} + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 7509e0805ee..50e7b2b0e87 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -8,6 +8,7 @@ #define mozilla_dom_BindingUtils_h__ #include "mozilla/dom/DOMJSClass.h" +#include "mozilla/dom/DOMJSProxyHandler.h" #include "mozilla/dom/workers/Workers.h" #include "mozilla/ErrorResult.h" @@ -78,13 +79,34 @@ IsDOMClass(const js::Class* clasp) return IsDOMClass(Jsvalify(clasp)); } +// It's ok for eRegularDOMObject and eProxyDOMObject to be the same, but +// eNonDOMObject should always be different from the other two. This enum +// shouldn't be used to differentiate between non-proxy and proxy bindings. +enum DOMObjectSlot { + eNonDOMObject = -1, + eRegularDOMObject = DOM_OBJECT_SLOT, + eProxyDOMObject = DOM_PROXY_OBJECT_SLOT +}; + template inline T* -UnwrapDOMObject(JSObject* obj) +UnwrapDOMObject(JSObject* obj, DOMObjectSlot slot) { - MOZ_ASSERT(IsDOMClass(JS_GetClass(obj))); + MOZ_ASSERT(slot != eNonDOMObject, + "Don't pass non-DOM objects to this function"); - JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT); +#ifdef DEBUG + if (IsDOMClass(js::GetObjectClass(obj))) { + MOZ_ASSERT(slot == eRegularDOMObject); + } else { + MOZ_ASSERT(js::IsObjectProxyClass(js::GetObjectClass(obj)) || + js::IsFunctionProxyClass(js::GetObjectClass(obj))); + MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily()); + MOZ_ASSERT(slot == eProxyDOMObject); + } +#endif + + JS::Value val = js::GetReservedSlot(obj, slot); // XXXbz/khuey worker code tries to unwrap interface objects (which have // nothing here). That needs to stop. // XXX We don't null-check UnwrapObject's result; aren't we going to crash @@ -96,6 +118,62 @@ UnwrapDOMObject(JSObject* obj) return static_cast(val.toPrivate()); } +// Only use this with a new DOM binding object (either proxy or regular). +inline const DOMClass* +GetDOMClass(JSObject* obj) +{ + js::Class* clasp = js::GetObjectClass(obj); + if (IsDOMClass(clasp)) { + return &DOMJSClass::FromJSClass(clasp)->mClass; + } + + js::BaseProxyHandler* handler = js::GetProxyHandler(obj); + MOZ_ASSERT(handler->family() == ProxyFamily()); + return &static_cast(handler)->mClass; +} + +inline DOMObjectSlot +GetDOMClass(JSObject* obj, const DOMClass*& result) +{ + js::Class* clasp = js::GetObjectClass(obj); + if (IsDOMClass(clasp)) { + result = &DOMJSClass::FromJSClass(clasp)->mClass; + return eRegularDOMObject; + } + + if (js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) { + js::BaseProxyHandler* handler = js::GetProxyHandler(obj); + if (handler->family() == ProxyFamily()) { + result = &static_cast(handler)->mClass; + return eProxyDOMObject; + } + } + + return eNonDOMObject; +} + +inline bool +UnwrapDOMObjectToISupports(JSObject* obj, nsISupports*& result) +{ + const DOMClass* clasp; + DOMObjectSlot slot = GetDOMClass(obj, clasp); + if (slot == eNonDOMObject || !clasp->mDOMObjectIsISupports) { + return false; + } + + result = UnwrapDOMObject(obj, slot); + return true; +} + +inline bool +IsDOMObject(JSObject* obj) +{ + js::Class* clasp = js::GetObjectClass(obj); + return IsDOMClass(clasp) || + ((js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) && + js::GetProxyHandler(obj)->family() == ProxyFamily()); +} + // Some callers don't want to set an exception when unwrappin fails // (for example, overload resolution uses unwrapping to tell what sort // of thing it's looking at). @@ -105,8 +183,9 @@ inline nsresult UnwrapObject(JSContext* cx, JSObject* obj, U& value) { /* First check to see whether we have a DOM object */ - JSClass* clasp = js::GetObjectJSClass(obj); - if (!IsDOMClass(clasp)) { + const DOMClass* domClass; + DOMObjectSlot slot = GetDOMClass(obj, domClass); + if (slot == eNonDOMObject) { /* Maybe we have a security wrapper or outer window? */ if (!js::IsWrapper(obj)) { /* Not a DOM object, not a wrapper, just bail */ @@ -118,22 +197,19 @@ UnwrapObject(JSContext* cx, JSObject* obj, U& value) return NS_ERROR_XPC_SECURITY_MANAGER_VETO; } MOZ_ASSERT(!js::IsWrapper(obj)); - clasp = js::GetObjectJSClass(obj); - if (!IsDOMClass(clasp)) { + slot = GetDOMClass(obj, domClass); + if (slot == eNonDOMObject) { /* We don't have a DOM object */ return NS_ERROR_XPC_BAD_CONVERT_JS; } } - MOZ_ASSERT(IsDOMClass(clasp)); - /* This object is a DOM object. Double-check that it is safely castable to T by checking whether it claims to inherit from the class identified by protoID. */ - DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp); if (domClass->mInterfaceChain[PrototypeTraits::Depth] == PrototypeID) { - value = UnwrapDOMObject(obj); + value = UnwrapDOMObject(obj, slot); return NS_OK; } @@ -309,7 +385,7 @@ JSObject* CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* receiver, JSObject* protoProto, JSClass* protoClass, JSClass* constructorClass, JSNative constructor, - unsigned ctorNargs, JSClass* instanceClass, + unsigned ctorNargs, const DOMClass* domClass, Prefable* methods, Prefable* properties, Prefable* constants, @@ -555,6 +631,22 @@ GetParentPointer(const ParentObject& aObject) return ToSupports(aObject.mObject); } +template +inline void +ClearWrapper(T* p, nsWrapperCache* cache) +{ + cache->ClearWrapper(); +} + +template +inline void +ClearWrapper(T* p, void*) +{ + nsWrapperCache* cache; + CallQueryInterface(p, &cache); + ClearWrapper(p, cache); +} + // Can only be called with the immediate prototype of the instance object. Can // only be called on the prototype of an object known to be a DOM instance. JSBool @@ -700,6 +792,14 @@ QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp); JSBool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp); +bool +GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found, + JS::Value* vp); + +bool +HasPropertyOnPrototype(JSContext* cx, JSObject* proxy, DOMProxyHandler* handler, + jsid id); + template class NonNull { diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 97bcb16fff0..d0ca851debc 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -301,6 +301,52 @@ DOMInterfaces = { 'register': False }, + +'TestIndexedGetterInterface' : { + 'nativeType': 'mozilla::dom::TestIndexedGetterInterface', + 'headerFile': 'TestBindingHeader.h', + 'register': False, + 'infallible': [ 'length' ] + }, + +'TestNamedGetterInterface' : { + 'nativeType': 'mozilla::dom::TestNamedGetterInterface', + 'headerFile': 'TestBindingHeader.h', + 'register': False + }, + +'TestIndexedAndNamedGetterInterface' : { + 'nativeType': 'mozilla::dom::TestIndexedAndNamedGetterInterface', + 'headerFile': 'TestBindingHeader.h', + 'register': False, + 'infallible': [ 'length' ] + }, + +'TestIndexedSetterInterface' : { + 'nativeType': 'mozilla::dom::TestIndexedSetterInterface', + 'headerFile': 'TestBindingHeader.h', + 'register': False + }, + +'TestNamedSetterInterface' : { + 'nativeType': 'mozilla::dom::TestNamedSetterInterface', + 'headerFile': 'TestBindingHeader.h', + 'register': False + }, + +'TestIndexedAndNamedSetterInterface' : { + 'nativeType': 'mozilla::dom::TestIndexedAndNamedSetterInterface', + 'headerFile': 'TestBindingHeader.h', + 'register': False + }, + +'TestIndexedAndNamedGetterAndSetterInterface' : { + 'nativeType': 'mozilla::dom::TestIndexedAndNamedGetterAndSetterInterface', + 'headerFile': 'TestBindingHeader.h', + 'register': False, + 'infallible': [ 'length', '__stringifier' ], + 'binaryNames': { '__stringifier': 'Stringify' } + }, } # 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 8d38fd1a22b..8a7a312970c 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -68,7 +68,7 @@ class CGNativePropertyHooks(CGThing): def declare(self): if self.descriptor.workers: return "" - return " extern const NativePropertyHooks NativeHooks;\n" + return "extern const NativePropertyHooks NativeHooks;\n" def define(self): if self.descriptor.workers: return "" @@ -79,6 +79,21 @@ class CGNativePropertyHooks(CGThing): const NativePropertyHooks NativeHooks = { ResolveProperty, EnumerateProperties, %s }; """ % parentHooks +def DOMClass(descriptor): + protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain] + # Pad out the list to the right length with _ID_Count so we + # guarantee that all the lists are the same length. _ID_Count + # is never the ID of any prototype, so it's safe to use as + # padding. + protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList))) + prototypeChainString = ', '.join(protoList) + nativeHooks = "NULL" if descriptor.workers else "&NativeHooks" + return """{ + { %s }, + %s, %s +}""" % (prototypeChainString, toStringBool(descriptor.nativeIsISupports), + nativeHooks) + class CGDOMJSClass(CGThing): """ Generate a DOMJSClass for a given descriptor @@ -87,18 +102,9 @@ class CGDOMJSClass(CGThing): CGThing.__init__(self) self.descriptor = descriptor def declare(self): - return " extern DOMJSClass Class;\n" + return "extern DOMJSClass Class;\n" def define(self): traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'NULL' - protoList = ['prototypes::id::' + proto for proto in self.descriptor.prototypeChain] - # Pad out the list to the right length with _ID_Count so we - # guarantee that all the lists are the same length. _ID_Count - # is never the ID of any prototype, so it's safe to use as - # padding. - while len(protoList) < self.descriptor.config.maxProtoChainLength: - protoList.append('prototypes::id::_ID_Count') - prototypeChainString = ', '.join(protoList) - nativeHooks = "NULL" if self.descriptor.workers else "&NativeHooks" return """ DOMJSClass Class = { { "%s", @@ -118,15 +124,13 @@ DOMJSClass Class = { %s, /* trace */ JSCLASS_NO_INTERNAL_MEMBERS }, - { %s }, - -1, %s, - %s + %s, + -1 }; """ % (self.descriptor.interface.identifier.name, ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'JS_PropertyStub', - FINALIZE_HOOK_NAME, traceHook, prototypeChainString, - str(self.descriptor.nativeIsISupports).lower(), - nativeHooks) + FINALIZE_HOOK_NAME, traceHook, + CGIndenter(CGGeneric(DOMClass(self.descriptor))).define()) class CGPrototypeJSClass(CGThing): def __init__(self, descriptor): @@ -136,8 +140,7 @@ class CGPrototypeJSClass(CGThing): # We're purely for internal consumption return "" def define(self): - return """ -static JSClass PrototypeClass = { + return """static JSClass PrototypeClass = { "%sPrototype", JSCLASS_HAS_RESERVED_SLOTS(1), JS_PropertyStub, /* addProperty */ @@ -202,12 +205,12 @@ class CGList(CGThing): self.children.append(child) def prepend(self, child): self.children.insert(0, child) + def join(self, generator): + return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator))) def declare(self): - return self.joiner.join([child.declare() for child in self.children - if child is not None]) + return self.join(child.declare() for child in self.children if child is not None) def define(self): - return self.joiner.join([child.define() for child in self.children - if child is not None]) + return self.join(child.define() for child in self.children if child is not None) class CGGeneric(CGThing): """ @@ -231,10 +234,11 @@ class CGIndenter(CGThing): A class that takes another CGThing and generates code that indents that CGThing by some number of spaces. The default indent is two spaces. """ - def __init__(self, child, indentLevel=2): + def __init__(self, child, indentLevel=2, declareOnly=False): CGThing.__init__(self) self.child = child self.indent = " " * indentLevel + self.declareOnly = declareOnly def declare(self): decl = self.child.declare() if decl is not "": @@ -243,10 +247,10 @@ class CGIndenter(CGThing): return "" def define(self): defn = self.child.define() - if defn is not "": + if defn is not "" and not self.declareOnly: return re.sub(lineStartDetector, self.indent, defn) else: - return "" + return defn class CGWrapper(CGThing): """ @@ -285,6 +289,13 @@ class CGWrapper(CGThing): defn.replace("\n", "\n" + (" " * len(self.definePre)))) return self.definePre + defn + self.definePost +class CGIfWrapper(CGWrapper): + def __init__(self, child, condition): + pre = CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", + reindent=True) + CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(), + post="\n}") + class CGNamespace(CGWrapper): def __init__(self, namespace, child, declareOnly=False): pre = "namespace %s {\n" % namespace @@ -519,39 +530,54 @@ class CGAbstractMethod(CGThing): inline should be True to generate an inline method, whose body is part of the declaration. + alwaysInline should be True to generate an inline method annotated with + MOZ_ALWAYS_INLINE. + static should be True to generate a static method, which only has a definition. + + If templateArgs is not None it should be a list of strings containing + template arguments, and the function will be templatized using those + arguments. """ - def __init__(self, descriptor, name, returnType, args, inline=False, static=False): + def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None): CGThing.__init__(self) self.descriptor = descriptor self.name = name self.returnType = returnType self.args = args self.inline = inline + self.alwaysInline = alwaysInline self.static = static + self.templateArgs = templateArgs def _argstring(self): return ', '.join([str(a) for a in self.args]) + def _template(self): + if self.templateArgs is None: + return '' + return 'template <%s>\n' % ', '.join(self.templateArgs) def _decorators(self): decorators = [] - if self.inline: + if self.alwaysInline: + decorators.append('MOZ_ALWAYS_INLINE') + elif self.inline: decorators.append('inline') if self.static: decorators.append('static') decorators.append(self.returnType) - return ' '.join(decorators) + maybeNewline = " " if self.inline else "\n" + return ' '.join(decorators) + maybeNewline def declare(self): if self.inline: return self._define() - return "\n %s %s(%s);\n" % (self._decorators(), self.name, self._argstring()) + return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring()) def _define(self): return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue() def define(self): return "" if self.inline else self._define() def definition_prologue(self): - maybeNewline = " " if self.inline else "\n" - return "\n%s%s%s(%s)\n{" % (self._decorators(), maybeNewline, - self.name, self._argstring()) + return "%s%s%s(%s)\n{" % (self._template(), self._decorators(), + self.name, self._argstring()) def definition_epilogue(self): return "\n}\n" def definition_body(self): @@ -580,7 +606,7 @@ class CGAbstractClassHook(CGAbstractStaticMethod): def definition_body_prologue(self): return """ - %s* self = UnwrapDOMObject<%s>(obj); + %s* self = UnwrapDOMObject<%s>(obj, eRegularDOMObject); """ % (self.descriptor.nativeType, self.descriptor.nativeType) def definition_body(self): @@ -608,6 +634,24 @@ class CGAddPropertyHook(CGAbstractClassHook): return """ nsContentUtils::PreserveWrapper(reinterpret_cast(self), self); return true;""" +def finalizeHook(descriptor, hookName, context): + if descriptor.customFinalize: + return """if (self) { + self->%s(%s); +}""" % (hookName, context) + clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else "" + if descriptor.workers: + release = "self->Release();" + else: + assert descriptor.nativeIsISupports + release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); +if (rt) { + rt->DeferredRelease(reinterpret_cast(self)); +} else { + NS_RELEASE(self); +}""" + return clearWrapper + release + class CGClassFinalizeHook(CGAbstractClassHook): """ A hook for finalize, used to release our native object. @@ -618,23 +662,7 @@ class CGClassFinalizeHook(CGAbstractClassHook): 'void', args) def generate_code(self): - if self.descriptor.customFinalize: - return """ if (self) { - self->%s(%s); - }""" % (self.name, self.args[0].name) - clearWrapper = "self->ClearWrapper();\n " if self.descriptor.wrapperCache else "" - if self.descriptor.workers: - release = "self->Release();" - else: - assert self.descriptor.nativeIsISupports - release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); - if (rt) { - rt->DeferredRelease(reinterpret_cast(self)); - } else { - NS_RELEASE(self); - }""" - return """ - %s%s""" % (clearWrapper, release) + return CGIndenter(CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))).define() class CGClassTraceHook(CGAbstractClassHook): """ @@ -894,8 +922,12 @@ class MethodDefiner(PropertyDefiner): def __init__(self, descriptor, name, static): PropertyDefiner.__init__(self, descriptor, name) + # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822 + # We should be able to check for special operations without an + # identifier. For now we check if the name starts with __ methods = [m for m in descriptor.interface.members if - m.isMethod() and m.isStatic() == static] + m.isMethod() and m.isStatic() == static and + not m.isIdentifierLess()] self.chrome = [{"name": m.identifier.name, "length": methodLength(m), "flags": "JSPROP_ENUMERATE", @@ -906,12 +938,30 @@ class MethodDefiner(PropertyDefiner): "flags": "JSPROP_ENUMERATE", "pref": PropertyDefiner.getControllingPref(m) } for m in methods if not isChromeOnly(m)] + + # FIXME Check for an existing iterator on the interface first. + if any(m.isGetter() and m.isIndexed() for m in methods): + self.chrome.append({"name": 'iterator', + "methodInfo": False, + "nativeName": "JS_ArrayIterator", + "length": 0, + "flags": "JSPROP_ENUMERATE", + "pref": None }) + self.regular.append({"name": 'iterator', + "methodInfo": False, + "nativeName": "JS_ArrayIterator", + "length": 0, + "flags": "JSPROP_ENUMERATE", + "pref": None }) + if not descriptor.interface.parent and not static and not descriptor.workers: self.chrome.append({"name": 'QueryInterface', + "methodInfo": False, "length": 1, "flags": "0", "pref": None }) self.regular.append({"name": 'QueryInterface', + "methodInfo": False, "length": 1, "flags": "0", "pref": None }) @@ -933,9 +983,12 @@ class MethodDefiner(PropertyDefiner): return m["pref"] def specData(m): - isQI = (m["name"] == 'QueryInterface') - jitinfo = ("&%s_methodinfo" % m["name"]) if not isQI else "NULL" - accessor = "genericMethod" if not isQI else "QueryInterface" + if m.get("methodInfo", True): + jitinfo = ("&%s_methodinfo" % m["name"]) + accessor = "genericMethod" + else: + jitinfo = "nullptr" + accessor = m.get("nativeName", m["name"]) return (m["name"], accessor, jitinfo, m["length"], m["flags"]) return self.generatePrefableArray( @@ -1106,10 +1159,10 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): else: prefCache = None - getParentProto = ("JSObject* parentProto = %s;\n" - "if (!parentProto) {\n" - " return NULL;\n" - "}") % getParentProto + getParentProto = ("JSObject* parentProto = %s;\n" + + "if (!parentProto) {\n" + + " return NULL;\n" + + "}\n") % getParentProto needInterfaceObjectClass = (needInterfaceObject and self.descriptor.hasInstanceInterface) @@ -1122,32 +1175,40 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): constructHook = "ThrowingConstructor" constructArgs = 0 - call = CGGeneric(("return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,\n" - " %s, %s, %s, %d,\n" - " %s,\n" - " %%(methods)s, %%(attrs)s, %%(consts)s, %%(staticMethods)s,\n" - " %s);") % ( + if self.descriptor.concrete: + if self.descriptor.proxy: + domClass = "&Class" + else: + domClass = "&Class.mClass" + else: + domClass = "nullptr" + + call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto, + %s, %s, %s, %d, + %s, + %%(methods)s, %%(attrs)s, + %%(consts)s, %%(staticMethods)s, + %s);""" % ( "&PrototypeClass" if needInterfacePrototypeObject else "NULL", "&InterfaceObjectClass" if needInterfaceObjectClass else "NULL", constructHook if needConstructor else "NULL", constructArgs, - "&Class.mBase" if self.descriptor.concrete else "NULL", - '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")) - + domClass, + '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL") if self.properties.hasChromeOnly(): if self.descriptor.workers: accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()" else: accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))" - accessCheck = "if (" + accessCheck + ") {\n" - chrome = CGWrapper(CGGeneric((CGIndenter(call).define() % self.properties.variableNames(True))), - pre=accessCheck, post="\n}") + chrome = CGIfWrapper(CGGeneric(call % self.properties.variableNames(True)), + accessCheck) + chrome = CGWrapper(chrome, pre="\n\n") else: chrome = None functionBody = CGList( [CGGeneric(getParentProto), initIds, prefCache, chrome, - CGGeneric(call.define() % self.properties.variableNames(False))], + CGGeneric(call % self.properties.variableNames(False))], "\n\n") return CGIndenter(functionBody).define() @@ -1272,23 +1333,48 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): *aEnabled = true; return !!%s(aCx, global, aReceiver);""" % (getter)) -class CGWrapMethod(CGAbstractMethod): +class CGIsMethod(CGAbstractMethod): + def __init__(self, descriptor): + args = [Argument('JSObject*', 'obj')] + CGAbstractMethod.__init__(self, descriptor, 'Is', 'bool', args) + + def definition_body(self): + # Non-proxy implementation would check + # js::GetObjectJSClass(obj) == &Class.mBase + return """ return IsProxy(obj);""" + +class CGWrapWithCacheMethod(CGAbstractMethod): def __init__(self, descriptor): - # XXX can we wrap if we don't have an interface prototype object? assert descriptor.interface.hasInterfacePrototypeObject() args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'), Argument(descriptor.nativeType + '*', 'aObject'), + Argument('nsWrapperCache*', 'aCache'), Argument('bool*', 'aTriedToWrap')] CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) def definition_body(self): if self.descriptor.workers: - return """ - *aTriedToWrap = true; + return """ *aTriedToWrap = true; return aObject->GetJSObject();""" - return """ - *aTriedToWrap = true; + if self.descriptor.proxy: + create = """ JSObject *obj = NewProxyObject(aCx, &DOMProxyHandler::instance, + JS::PrivateValue(aObject), proto, parent); + if (!obj) { + return NULL; + } + +""" + else: + create = """ JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, parent); + if (!obj) { + return NULL; + } + + js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject)); +""" + + return """ *aTriedToWrap = true; JSObject* parent = WrapNativeParent(aCx, aScope, aObject->GetParentObject()); if (!parent) { @@ -1309,17 +1395,23 @@ class CGWrapMethod(CGAbstractMethod): return NULL; } - JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, parent); - if (!obj) { - return NULL; - } - - js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject)); +%s NS_ADDREF(aObject); - aObject->SetWrapper(obj); + aCache->SetWrapper(obj); - return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aObject")) + return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"), create) + +class CGWrapMethod(CGAbstractMethod): + def __init__(self, descriptor): + # XXX can we wrap if we don't have an interface prototype object? + assert descriptor.interface.hasInterfacePrototypeObject() + args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'), + Argument('T*', 'aObject'), Argument('bool*', 'aTriedToWrap')] + CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, inline=True, templateArgs=["class T"]) + + def definition_body(self): + return " return Wrap(aCx, aScope, aObject, aObject, aTriedToWrap);" class CGWrapNonWrapperCacheMethod(CGAbstractMethod): def __init__(self, descriptor): @@ -2205,7 +2297,7 @@ for (uint32_t i = 0; i < length; ++i) { return (template, declType, None, False) if not type.isPrimitive(): - raise TypeError("Need conversion for argument type '%s'" % type) + raise TypeError("Need conversion for argument type '%s'" % str(type)) # XXXbz need to add support for [EnforceRange] and [Clamp] typeName = builtinNames[type.tag()] @@ -2658,24 +2750,25 @@ def typeNeedsCx(type): (type.isUnion() and any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)))) -# Returns a CGThing containing the type of the return value, or None -# if there is no need for a return value. +# Returns a tuple consisting of a CGThing containing the type of the return +# value, or None if there is no need for a return value, and a boolean signaling +# whether the return value is passed in an out parameter. def getRetvalDeclarationForType(returnType, descriptorProvider, resultAlreadyAddRefed): if returnType is None or returnType.isVoid(): # Nothing to declare - return None + return None, False if returnType.isPrimitive() and returnType.tag() in builtinNames: result = CGGeneric(builtinNames[returnType.tag()]) if returnType.nullable(): result = CGWrapper(result, pre="Nullable<", post=">") - return result + return result, False if returnType.isString(): - return CGGeneric("nsString") + return CGGeneric("nsString"), True if returnType.isEnum(): if returnType.nullable(): raise TypeError("We don't support nullable enum return values") - return CGGeneric(returnType.inner.identifier.name) + return CGGeneric(returnType.inner.identifier.name), False if returnType.isGeckoInterface(): result = CGGeneric(descriptorProvider.getDescriptor( returnType.unroll().inner.identifier.name).nativeType) @@ -2683,28 +2776,28 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, result = CGWrapper(result, pre="nsRefPtr<", post=">") else: result = CGWrapper(result, post="*") - return result + return result, False if returnType.isCallback(): # XXXbz we're going to assume that callback types are always # nullable for now. - return CGGeneric("JSObject*") - if returnType.isAny(): - return CGGeneric("JS::Value") + return CGGeneric("JSObject*"), False + if returnType.tag() is IDLType.Tags.any: + return CGGeneric("JS::Value"), False if returnType.isObject(): - return CGGeneric("JSObject*") + return CGGeneric("JSObject*"), False if returnType.isSequence(): nullable = returnType.nullable() if nullable: returnType = returnType.inner # If our result is already addrefed, use the right type in the # sequence argument here. - result = getRetvalDeclarationForType(returnType.inner, - descriptorProvider, - resultAlreadyAddRefed) + (result, _) = getRetvalDeclarationForType(returnType.inner, + descriptorProvider, + resultAlreadyAddRefed) result = CGWrapper(result, pre="nsTArray< ", post=" >") if nullable: result = CGWrapper(result, pre="Nullable< ", post=" >") - return result + return result, True raise TypeError("Don't know how to declare return value for %s" % returnType) @@ -2715,36 +2808,36 @@ def isResultAlreadyAddRefed(descriptor, extendedAttributes): class CGCallGenerator(CGThing): """ A class to generate an actual call to a C++ object. Assumes that the C++ - object is stored in a variable named "self". + object is stored in a variable whose name is given by the |object| argument. """ def __init__(self, errorReport, arguments, argsPre, returnType, - extendedAttributes, descriptorProvider, nativeMethodName, static): + extendedAttributes, descriptorProvider, nativeMethodName, + static, object="self", declareResult=True): CGThing.__init__(self) isFallible = errorReport is not None + resultAlreadyAddRefed = isResultAlreadyAddRefed(descriptorProvider, + extendedAttributes) + (result, resultOutParam) = getRetvalDeclarationForType(returnType, + descriptorProvider, + resultAlreadyAddRefed) + args = CGList([CGGeneric(arg) for arg in argsPre], ", ") - for (i, a) in enumerate(arguments): - arg = "arg" + str(i) + for (a, name) in arguments: # This is a workaround for a bug in Apple's clang. if a.type.isObject() and not a.type.nullable() and not a.optional: - arg = "(JSObject&)" + arg - args.append(CGGeneric(arg)) - resultOutParam = (returnType is not None and - (returnType.isString() or returnType.isSequence())) + name = "(JSObject&)" + name + args.append(CGGeneric(name)) + # Return values that go in outparams go here if resultOutParam: args.append(CGGeneric("result")) if isFallible: args.append(CGGeneric("rv")) - resultAlreadyAddRefed = isResultAlreadyAddRefed(descriptorProvider, - extendedAttributes) - result = getRetvalDeclarationForType(returnType, - descriptorProvider, - resultAlreadyAddRefed) needsCx = (typeNeedsCx(returnType) or - any(typeNeedsCx(a.type) for a in arguments) or + any(typeNeedsCx(a.type) for (a, _) in arguments) or 'implicitJSContext' in extendedAttributes) if not "cx" in argsPre and needsCx: @@ -2755,14 +2848,14 @@ class CGCallGenerator(CGThing): call = CGGeneric(nativeMethodName) if static: - call = CGWrapper(call, pre="%s::" % (descriptorProvider.getDescriptor( - returnType.unroll().inner.identifier.name).nativeType)) + call = CGWrapper(call, pre="%s::" % descriptorProvider.nativeType) else: - call = CGWrapper(call, pre="self->") + call = CGWrapper(call, pre="%s->" % object) call = CGList([call, CGWrapper(args, pre="(", post=");")]) if result is not None: - result = CGWrapper(result, post=" result;") - self.cgRoot.prepend(result) + if declareResult: + result = CGWrapper(result, post=" result;") + self.cgRoot.prepend(result) if not resultOutParam: call = CGWrapper(call, pre="result = ") @@ -2772,7 +2865,7 @@ class CGCallGenerator(CGThing): if isFallible: self.cgRoot.prepend(CGGeneric("ErrorResult rv;")) self.cgRoot.append(CGGeneric("if (rv.Failed()) {")) - self.cgRoot.append(CGIndenter(CGGeneric(errorReport))) + self.cgRoot.append(CGIndenter(errorReport)) self.cgRoot.append(CGGeneric("}")) def define(self): @@ -2830,7 +2923,7 @@ class CGPerSignatureCall(CGThing): cgThings.append(CGCallGenerator( self.getErrorReport() if self.isFallible() else None, - self.arguments, self.argsPre, returnType, + self.getArguments(), self.argsPre, returnType, self.extendedAttributes, descriptor, nativeMethodName, static)) self.cgRoot = CGList(cgThings, "\n") @@ -2841,6 +2934,8 @@ class CGPerSignatureCall(CGThing): return "\nJS::Value* argv = JS_ARGV(cx, vp);\n" def getArgc(self): return "argc" + def getArguments(self): + return [(a, "arg" + str(i)) for (i, a) in enumerate(self.arguments)] def isFallible(self): return not 'infallible' in self.extendedAttributes @@ -2865,10 +2960,10 @@ class CGPerSignatureCall(CGThing): self.idlNode.identifier.name)) def getErrorReport(self): - return 'return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'\ - % (toStringBool(not self.descriptor.workers), - self.descriptor.interface.identifier.name, - self.idlNode.identifier.name) + return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");' + % (toStringBool(not self.descriptor.workers), + self.descriptor.interface.identifier.name, + self.idlNode.identifier.name)) def define(self): return (self.cgRoot.define() + "\n" + self.wrap_return_value()) @@ -3846,6 +3941,96 @@ ${body} 'const': ' const' if self.const else '', 'body': body }) +class ClassConstructor(ClassItem): + """ + Used for adding a constructor to a CGClass. + + args is a list of Argument objects that are the arguments taken by the + constructor. + + inline should be True if the constructor should be marked inline. + + bodyInHeader should be True if the body should be placed in the class + declaration in the header. + + visibility determines the visibility of the constructor (public, + protected, private), defaults to private. + + baseConstructors is a list of strings containing calls to base constructors, + defaults to None. + + body contains a string with the code for the constructor, defaults to None. + """ + def __init__(self, args, inline=False, bodyInHeader=False, + visibility="private", baseConstructors=None, body=None): + self.args = args + self.inline = inline or bodyInHeader + self.bodyInHeader = bodyInHeader + self.baseConstructors = baseConstructors + self.body = body + ClassItem.__init__(self, None, visibility) + + def getDecorators(self, declaring): + decorators = [] + if self.inline and declaring: + decorators.append('inline') + if decorators: + return ' '.join(decorators) + ' ' + return '' + + def getInitializationList(self, cgClass): + items = [str(c) for c in self.baseConstructors] + for m in cgClass.members: + if not m.static: + initialize = m.getBody() + if initialize: + items.append(m.name + "(" + initialize + ")") + + if len(items) > 0: + return '\n : ' + ',\n '.join(items) + return '' + + def getBody(self): + assert self.body is not None + return self.body + + def declare(self, cgClass): + args = ', '.join([str(a) for a in self.args]) + if self.bodyInHeader: + body = ' ' + self.getBody(); + body = stripTrailingWhitespace(body.replace('\n', '\n ')) + if len(body) > 0: + body += '\n' + body = self.getInitializationList(cgClass) + '\n{\n' + body + '}' + else: + body = ';' + + return string.Template("""${decorators}${className}(${args})${body} +""").substitute({ 'decorators': self.getDecorators(True), + 'className': cgClass.getNameString(), + 'args': args, + 'body': body }) + + def define(self, cgClass): + if self.bodyInHeader: + return '' + + args = ', '.join([str(a) for a in self.args]) + + body = ' ' + self.getBody() + body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n ')) + if len(body) > 0: + body += '\n' + + return string.Template("""${decorators} +${className}::${className}(${args})${initializationList} +{${body}}\n +""").substitute({ 'decorators': self.getDecorators(False), + 'className': cgClass.getNameString(), + 'args': args, + 'initializationList': self.getInitializationList(cgClass), + 'body': body }) + class ClassMember(ClassItem): def __init__(self, name, type, visibility="private", static=False, body=None): @@ -3854,10 +4039,6 @@ class ClassMember(ClassItem): self.body = body ClassItem.__init__(self, name, visibility) - def getBody(self): - assert self.body is not None - return self.body - def declare(self, cgClass): return '%s%s %s;\n' % ('static ' if self.static else '', self.type, self.name) @@ -3865,8 +4046,12 @@ class ClassMember(ClassItem): def define(self, cgClass): if not self.static: return '' - return '%s %s::%s = %s;\n' % (self.type, cgClass.getNameString(), - self.name, self.getBody()) + if self.body: + body = " = " + self.body + else: + body = "" + return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(), + self.name, body) class ClassTypedef(ClassItem): def __init__(self, name, type, visibility="public"): @@ -3902,13 +4087,14 @@ class ClassEnum(ClassItem): return '' class CGClass(CGThing): - def __init__(self, name, bases=[], members=[], methods=[], typedefs = [], - enums=[], templateArgs=[], templateSpecialization=[], - isStruct=False, indent=''): + def __init__(self, name, bases=[], members=[], constructors=[], methods=[], + typedefs = [], enums=[], templateArgs=[], + templateSpecialization=[], isStruct=False, indent=''): CGThing.__init__(self) self.name = name self.bases = bases self.members = members + self.constructors = constructors self.methods = methods self.typedefs = typedefs self.enums = enums @@ -3985,7 +4171,7 @@ class CGClass(CGThing): return (result, lastVisibility, itemCount) order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''), - (self.methods, '\n')] + (self.constructors, '\n'), (self.methods, '\n')] lastVisibility = self.defaultVisibility itemCount = 0 @@ -3998,7 +4184,7 @@ class CGClass(CGThing): len(self.indent)).define() result = result + memberString - result = result + self.indent + '};\n\n' + result = result + self.indent + '};\n' return result def define(self): @@ -4011,7 +4197,8 @@ class CGClass(CGThing): itemCount = itemCount + 1 return (result, itemCount) - order = [(self.members, '\n'), (self.methods, '\n')] + order = [(self.members, '\n'), (self.constructors, '\n'), + (self.methods, '\n')] result = '' itemCount = 0 @@ -4117,6 +4304,532 @@ class CGClassForwardDeclare(CGThing): # Header only return '' +class CGProxySpecialOperation(CGPerSignatureCall): + """ + Base class for classes for calling an indexed or named special operation + (don't use this directly, use the derived classes below). + """ + def __init__(self, descriptor, operation): + nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation)) + operation = descriptor.operations[operation] + assert len(operation.signatures()) == 1 + signature = operation.signatures()[0] + extendedAttributes = descriptor.getExtendedAttributes(operation) + + (returnType, arguments) = signature + + # We pass len(arguments) as the final argument so that the + # CGPerSignatureCall won't do any argument conversion of its own. + CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName, + False, descriptor, operation, + len(arguments)) + + if operation.isSetter() or operation.isCreator(): + # arguments[0] is the index or name of the item that we're setting. + argument = arguments[1] + template = getJSToNativeConversionTemplate(argument.type, descriptor) + templateValues = { + "declName": argument.identifier.name, + "holderName": argument.identifier.name + "_holder", + "val": "desc->value", + "valPtr": "&desc->value" + } + self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues)) + elif operation.isGetter(): + self.cgRoot.prepend(CGGeneric("bool found;")) + + def getArguments(self): + args = [(a, a.identifier.name) for a in self.arguments] + if self.idlNode.isGetter(): + args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean]), "found")) + return args + + def wrap_return_value(self): + if not self.idlNode.isGetter() or self.templateValues is None: + return "" + + wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues)) + wrap = CGIfWrapper(wrap, "found") + return "\n" + wrap.define() + +class CGProxyIndexedGetter(CGProxySpecialOperation): + """ + Class to generate a call to an indexed getter. If templateValues is not None + the returned value will be wrapped with wrapForType using templateValues. + """ + def __init__(self, descriptor, templateValues=None): + self.templateValues = templateValues + CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter') + +class CGProxyIndexedSetter(CGProxySpecialOperation): + """ + Class to generate a call to an indexed setter. + """ + def __init__(self, descriptor): + CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter') + +class CGProxyNamedGetter(CGProxySpecialOperation): + """ + Class to generate a call to an named getter. If templateValues is not None + the returned value will be wrapped with wrapForType using templateValues. + """ + def __init__(self, descriptor, templateValues=None): + self.templateValues = templateValues + CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter') + +class CGProxyNamedSetter(CGProxySpecialOperation): + """ + Class to generate a call to a named setter. + """ + def __init__(self, descriptor): + CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter') + +class CGProxyIsProxy(CGAbstractMethod): + def __init__(self, descriptor): + args = [Argument('JSObject*', 'obj')] + CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True) + def declare(self): + return "" + def definition_body(self): + return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == &DOMProxyHandler::instance;" + +class CGProxyUnwrap(CGAbstractMethod): + def __init__(self, descriptor): + args = [Argument('JSObject*', 'obj')] + CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True) + def declare(self): + return "" + def definition_body(self): + return """ if (xpc::WrapperFactory::IsXrayWrapper(obj)) { + obj = js::UnwrapObject(obj); + } + MOZ_ASSERT(IsProxy(obj)); + return static_cast<%s*>(js::GetProxyPrivate(obj).toPrivate());""" % (self.descriptor.nativeType) + +class CGDOMJSProxyHandlerDOMClass(CGThing): + def __init__(self, descriptor): + CGThing.__init__(self) + self.descriptor = descriptor + def declare(self): + return "extern const DOMClass Class;\n" + def define(self): + return """ +const DOMClass Class = """ + DOMClass(self.descriptor) + """; + +""" + +class CGDOMJSProxyHandler_CGDOMJSProxyHandler(ClassConstructor): + def __init__(self): + ClassConstructor.__init__(self, [], inline=True, visibility="public", + baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"], + body="") + +class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'), + Argument('jsid', 'id'), Argument('bool', 'set'), + Argument('JSPropertyDescriptor*', 'desc')] + ClassMethod.__init__(self, "getOwnPropertyDescriptor", "bool", args) + self.descriptor = descriptor + def getBody(self): + indexedGetter = self.descriptor.operations['IndexedGetter'] + indexedSetter = self.descriptor.operations['IndexedSetter'] + + setOrIndexedGet = "" + if indexedGetter or indexedSetter: + setOrIndexedGet += "int32_t index = GetArrayIndexFromId(cx, id);\n" + + if indexedGetter: + readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None) + fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly + templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value', + 'obj': 'proxy', 'successCode': fillDescriptor} + get = ("if (index >= 0) {\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" + + "}\n") % (self.descriptor.nativeType) + + if indexedSetter or self.descriptor.operations['NamedSetter']: + setOrIndexedGet += "if (set) {\n" + if indexedSetter: + setOrIndexedGet += (" if (index >= 0) {\n") + if not 'IndexedCreator' in self.descriptor.operations: + # FIXME need to check that this is a 'supported property index' + assert False + setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" + + " return true;\n" + + " }\n") + if self.descriptor.operations['NamedSetter']: + setOrIndexedGet += " if (JSID_IS_STRING(id)) {\n" + if not 'NamedCreator' in self.descriptor.operations: + # FIXME need to check that this is a 'supported property name' + assert False + setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" + + " return true;\n" + + " }\n") + setOrIndexedGet += "}" + if indexedGetter: + setOrIndexedGet += (" else {\n" + + CGIndenter(CGGeneric(get)).define() + + "}") + setOrIndexedGet += "\n\n" + elif indexedGetter: + setOrIndexedGet += ("if (!set) {\n" + + CGIndenter(CGGeneric(get)).define() + + "}\n\n") + + namedGetter = self.descriptor.operations['NamedGetter'] + if namedGetter: + readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None) + fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly + templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value', + 'obj': 'proxy', 'successCode': fillDescriptor} + namedGet = ("\n" + + "if (!set && JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" + + " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" + + " FakeDependentString name;\n" + " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" + + " eStringify, eStringify, name)) {\n" + + " return false;\n" + + " }\n" + + "\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" + + "}\n") % (self.descriptor.nativeType) + else: + namedGet = "" + + return setOrIndexedGet + """JSObject* expando; +if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) { + unsigned flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED; + if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) { + return false; + } + if (desc->obj) { + // Pretend the property lives on the wrapper. + desc->obj = proxy; + return true; + } +} +""" + namedGet + """ +desc->obj = NULL; +return true;""" + +class CGDOMJSProxyHandler_defineProperty(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'), + Argument('jsid', 'id'), + Argument('JSPropertyDescriptor*', 'desc')] + ClassMethod.__init__(self, "defineProperty", "bool", args) + self.descriptor = descriptor + def getBody(self): + set = "" + + indexedSetter = self.descriptor.operations['IndexedSetter'] + if indexedSetter: + if not (self.descriptor.operations['IndexedCreator'] is indexedSetter): + raise TypeError("Can't handle creator that's different from the setter") + set += ("int32_t index = GetArrayIndexFromId(cx, id);\n" + + "if (index >= 0) {\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() + + " return true;\n" + + "}\n") % (self.descriptor.nativeType) + elif self.descriptor.operations['IndexedGetter']: + set += ("if (GetArrayIndexFromId(cx, id)) {\n" + + " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" + + "}\n") % self.descriptor.name + namedSetter = self.descriptor.operations['NamedSetter'] + if namedSetter: + if not self.descriptor.operations['NamedCreator'] is namedSetter: + raise TypeError("Can't handle creator that's different from the setter") + set += ("if (JSID_IS_STRING(id)) {\n" + + " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" + + " FakeDependentString name;\n" + " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" + + " eStringify, eStringify, name)) {\n" + + " return false;\n" + + " }\n" + + "\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" + + "}\n") % (self.descriptor.nativeType) + elif self.descriptor.operations['NamedGetter']: + set += ("if (JSID_IS_STRING(id)) {\n" + + " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" + + " FakeDependentString name;\n" + " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" + + " eStringify, eStringify, name)) {\n" + + " return false;\n" + + " }\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + + " if (found) {\n" + " return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" + + " }\n" + + "}\n") % (self.descriptor.nativeType, self.descriptor.name) + return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args) + +class CGDOMJSProxyHandler_getOwnPropertyNames(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'), + Argument('JS::AutoIdVector&', 'props')] + ClassMethod.__init__(self, "getOwnPropertyNames", "bool", args) + self.descriptor = descriptor + def getBody(self): + indexedGetter = self.descriptor.operations['IndexedGetter'] + if indexedGetter: + addIndices = """uint32_t length = UnwrapProxy(proxy)->GetLength(); +MOZ_ASSERT(int32_t(length) >= 0); +for (int32_t i = 0; i < int32_t(length); ++i) { + if (!props.append(INT_TO_JSID(i))) { + return false; + } +} + +""" + else: + addIndices = "" + + return addIndices + """JSObject* expando; +if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = DOMProxyHandler::GetExpandoObject(proxy)) && + !js::GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props)) { + return false; +} + +// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=772869 Add named items +return true;""" + +class CGDOMJSProxyHandler_hasOwn(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'), + Argument('jsid', 'id'), Argument('bool*', 'bp')] + ClassMethod.__init__(self, "hasOwn", "bool", args) + self.descriptor = descriptor + def getBody(self): + indexedGetter = self.descriptor.operations['IndexedGetter'] + if indexedGetter: + indexed = ("int32_t index = GetArrayIndexFromId(cx, id);\n" + + "if (index >= 0) {\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" + + " *bp = found;\n" + + " return true;\n" + + "}\n\n") % (self.descriptor.nativeType) + else: + indexed = "" + + namedGetter = self.descriptor.operations['NamedGetter'] + if namedGetter: + named = ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" + + " jsval nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" + + " FakeDependentString name;\n" + " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" + + " eStringify, eStringify, name)) {\n" + + " return false;\n" + + " }\n" + + "\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" + + " *bp = found;\n" + " return true;\n" + "}\n" + + "\n") % (self.descriptor.nativeType) + else: + named = "" + + return indexed + """JSObject* expando = GetExpandoObject(proxy); +if (expando) { + JSBool b = true; + JSBool ok = JS_HasPropertyById(cx, expando, id, &b); + *bp = !!b; + if (!ok || *bp) { + return ok; + } +} + +""" + named + """*bp = false; +return true;""" + +class CGDOMJSProxyHandler_get(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'), + Argument('JSObject*', 'receiver'), Argument('jsid', 'id'), + Argument('JS::Value*', 'vp')] + ClassMethod.__init__(self, "get", "bool", args) + self.descriptor = descriptor + def getBody(self): + getFromExpando = """JSObject* expando = DOMProxyHandler::GetExpandoObject(proxy); +if (expando) { + JSBool hasProp; + if (!JS_HasPropertyById(cx, expando, id, &hasProp)) { + return false; + } + + if (hasProp) { + return JS_GetPropertyById(cx, expando, id, vp); + } +}""" + + templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'} + + indexedGetter = self.descriptor.operations['IndexedGetter'] + if indexedGetter: + getIndexedOrExpando = ("int32_t index = GetArrayIndexFromId(cx, id);\n" + + "if (index >= 0) {\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) % (self.descriptor.nativeType) + getIndexedOrExpando += """ + // Even if we don't have this index, we don't forward the + // get on to our expando object. +} else { + %s +} +""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n '))) + else: + getIndexedOrExpando = getFromExpando + "\n" + + namedGetter = self.descriptor.operations['NamedGetter'] + if namedGetter: + getNamed = ("if (JSID_IS_STRING(id)) {\n" + + " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" + + " FakeDependentString name;\n" + " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" + + " eStringify, eStringify, name)) {\n" + + " return false;\n" + + " }\n" + + "\n" + + " %s* self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + + "}\n") % (self.descriptor.nativeType) + else: + getNamed = "" + + return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), + "Should not have a XrayWrapper here"); + +%s +bool found; +if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp)) { + return false; +} + +if (found) { + return true; +} +%s +vp->setUndefined(); +return true;""" % (getIndexedOrExpando, getNamed) + +class CGDOMJSProxyHandler_obj_toString(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy')] + ClassMethod.__init__(self, "obj_toString", "JSString*", args) + self.descriptor = descriptor + def getBody(self): + stringifier = self.descriptor.operations['Stringifier'] + if stringifier: + name = stringifier.identifier.name + nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) + signature = stringifier.signatures()[0] + returnType = signature[0] + extendedAttributes = self.descriptor.getExtendedAttributes(stringifier) + infallible = 'infallible' in extendedAttributes + if not infallible: + error = ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString")\n' + + "return NULL;") % self.descriptor.interface.identifier.name + else: + error = None + call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)") + return call.define() + """ + +JSString* jsresult; +return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;""" + + return "return mozilla::dom::DOMProxyHandler::obj_toString(cx, \"%s\");" % self.descriptor.name + +class CGDOMJSProxyHandler_finalize(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')] + ClassMethod.__init__(self, "finalize", "void", args) + self.descriptor = descriptor + def getBody(self): + return ("%s self = UnwrapProxy(proxy);\n\n" % (self.descriptor.nativeType + "*") + + finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name)) + +class CGDOMJSProxyHandler_getElementIfPresent(ClassMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'), + Argument('JSObject*', 'receiver'), + Argument('uint32_t', 'index'), + Argument('JS::Value*', 'vp'), Argument('bool*', 'present')] + ClassMethod.__init__(self, "getElementIfPresent", "bool", args) + self.descriptor = descriptor + def getBody(self): + indexedGetter = self.descriptor.operations['IndexedGetter'] + if indexedGetter: + successCode = """*present = found; +return true;""" + templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', + 'obj': 'proxy', 'successCode': successCode} + get = ("%s* self = UnwrapProxy(proxy);\n" + + CGProxyIndexedGetter(self.descriptor, templateValues).define() + "\n" + "// We skip the expando object if there is an indexed getter.\n" + + "\n") % (self.descriptor.nativeType) + else: + get = """ + +JSObject* expando = GetExpandoObject(proxy); +if (expando) { + JSBool isPresent; + if (!JS_GetElementIfPresent(cx, expando, index, expando, vp, &isPresent)) { + return false; + } + if (isPresent) { + *present = true; + return true; + } +} +""" + + return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), + "Should not have a XrayWrapper here"); + +""" + get + """ +// No need to worry about name getters here, so just check the proto. + +JSObject *proto = js::GetObjectProto(proxy); +if (proto) { + JSBool isPresent; + if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp, &isPresent)) { + return false; + } + *present = isPresent; + return true; +} + +*present = false; +// Can't Debug_SetValueRangeToCrashOnTouch because it's not public +return true;""" + +class CGDOMJSProxyHandler(CGClass): + def __init__(self, descriptor): + constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()] + methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)] + if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']: + methods.append(CGDOMJSProxyHandler_defineProperty(descriptor)) + methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor), + CGDOMJSProxyHandler_hasOwn(descriptor), + CGDOMJSProxyHandler_get(descriptor), + CGDOMJSProxyHandler_obj_toString(descriptor), + CGDOMJSProxyHandler_finalize(descriptor), + CGDOMJSProxyHandler_getElementIfPresent(descriptor)]) + CGClass.__init__(self, 'DOMProxyHandler', + bases=[ClassBase('mozilla::dom::DOMProxyHandler')], + members=[ClassMember('instance', 'DOMProxyHandler', visibility='public', static=True)], + constructors=constructors, + methods=methods) + def stripTrailingWhitespace(text): lines = text.splitlines() for i in range(len(lines)): @@ -4133,7 +4846,7 @@ class CGDescriptor(CGThing): if descriptor.interface.hasInterfacePrototypeObject(): hasMethod, hasGetter, hasSetter = False, False, False for m in descriptor.interface.members: - if m.isMethod() and not m.isStatic(): + if m.isMethod() and not m.isStatic() and not m.isIdentifierLess(): cgThings.append(CGSpecializedMethod(descriptor, m)) cgThings.append(CGMemberJITInfo(descriptor, m)) hasMethod = True @@ -4148,8 +4861,7 @@ class CGDescriptor(CGThing): if hasGetter: cgThings.append(CGGenericGetter(descriptor)) if hasSetter: cgThings.append(CGGenericSetter(descriptor)) - - if descriptor.concrete: + if descriptor.concrete and not descriptor.proxy: if not descriptor.workers and descriptor.wrapperCache: cgThings.append(CGAddPropertyHook(descriptor)) @@ -4161,11 +4873,6 @@ class CGDescriptor(CGThing): if (descriptor.customTrace): cgThings.append(CGClassTraceHook(descriptor)) - if descriptor.interface.hasInterfacePrototypeObject(): - cgThings.append(CGNativePropertyHooks(descriptor)) - if descriptor.concrete: - cgThings.append(CGDOMJSClass(descriptor)) - if descriptor.interface.hasInterfaceObject(): cgThings.append(CGClassConstructHook(descriptor)) cgThings.append(CGClassHasInstanceHook(descriptor)) @@ -4178,9 +4885,9 @@ class CGDescriptor(CGThing): cgThings.append(CGGeneric(define=str(properties))) cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties)) if descriptor.interface.hasInterfacePrototypeObject(): - cgThings.append(CGIndenter(CGGetProtoObjectMethod(descriptor))) + cgThings.append(CGGetProtoObjectMethod(descriptor)) else: - cgThings.append(CGIndenter(CGGetConstructorObjectMethod(descriptor))) + cgThings.append(CGGetConstructorObjectMethod(descriptor)) # Set up our Xray callbacks as needed. Note that we don't need to do # it in workers. @@ -4192,14 +4899,27 @@ class CGDescriptor(CGThing): if descriptor.interface.hasInterfaceObject(): cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) + if descriptor.interface.hasInterfacePrototypeObject(): + cgThings.append(CGNativePropertyHooks(descriptor)) + if descriptor.concrete: + if descriptor.proxy: + cgThings.append(CGProxyIsProxy(descriptor)) + cgThings.append(CGProxyUnwrap(descriptor)) + cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor)) + cgThings.append(CGDOMJSProxyHandler(descriptor)) + cgThings.append(CGIsMethod(descriptor)) + else: + cgThings.append(CGDOMJSClass(descriptor)) + if descriptor.wrapperCache: + cgThings.append(CGWrapWithCacheMethod(descriptor)) cgThings.append(CGWrapMethod(descriptor)) else: cgThings.append(CGWrapNonWrapperCacheMethod(descriptor)) - cgThings = CGList(cgThings) - cgThings = CGWrapper(cgThings, post='\n') + cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n") + cgThings = CGWrapper(cgThings, pre='\n', post='\n') self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), cgThings), post='\n') @@ -4612,7 +5332,8 @@ class CGBindingRoot(CGThing): curr = CGHeaders(descriptors, dictionaries, ['mozilla/dom/BindingUtils.h', - 'mozilla/dom/DOMJSClass.h'], + 'mozilla/dom/DOMJSClass.h', + 'mozilla/dom/DOMJSProxyHandler.h'], ['mozilla/dom/Nullable.h', 'PrimitiveConversions.h', 'XPCQuickStubs.h', diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index dc935231157..880614eb5c4 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -147,13 +147,63 @@ class Descriptor(DescriptorProvider): # If we're concrete, we need to crawl our ancestor interfaces and mark # them as having a concrete descendant. - self.concrete = desc.get('concrete', True) + self.concrete = desc.get('concrete', not self.interface.isExternal()) if self.concrete: + self.proxy = False + operations = { + 'IndexedGetter': None, + 'IndexedSetter': None, + 'IndexedCreator': None, + 'IndexedDeleter': None, + 'NamedGetter': None, + 'NamedSetter': None, + 'NamedCreator': None, + 'NamedDeleter': None, + 'Stringifier': None + } iface = self.interface while iface: + for m in iface.members: + if not m.isMethod(): + continue + + def addOperation(operation, m): + if not operations[operation]: + operations[operation] = m + def addIndexedOrNamedOperation(operation, m): + self.proxy = True + if m.isIndexed(): + operation = 'Indexed' + operation + else: + assert m.isNamed() + operation = 'Named' + operation + addOperation(operation, m) + + if m.isStringifier(): + addOperation('Stringifier', m) + else: + if m.isGetter(): + addIndexedOrNamedOperation('Getter', m) + if m.isSetter(): + addIndexedOrNamedOperation('Setter', m) + if m.isCreator(): + addIndexedOrNamedOperation('Creator', m) + if m.isDeleter(): + addIndexedOrNamedOperation('Deleter', m) + raise TypeError("deleter specified on %s but we " + "don't support deleters yet" % + self.interface.identifier.name) + iface.setUserData('hasConcreteDescendant', True) iface = iface.parent + if self.proxy: + self.operations = operations + iface = self.interface + while iface: + iface.setUserData('hasProxyDescendant', True) + iface = iface.parent + if self.interface.isExternal() and 'prefable' in desc: raise TypeError("%s is external but has a prefable setting" % self.interface.identifier.name) @@ -188,8 +238,14 @@ class Descriptor(DescriptorProvider): elif isinstance(config, list): add('all', config, attribute) else: - assert isinstance(config, string) - add('all', [config], attribute) + assert isinstance(config, str) + if config == '*': + iface = self.interface + while iface: + add('all', map(lambda m: m.name, iface.members), attribute) + iface = iface.parent + else: + add('all', [config], attribute) for attribute in ['infallible', 'implicitJSContext', 'resultNotAddRefed']: addExtendedAttribute(attribute, desc.get(attribute, {})) diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index 12579cee249..bbbf54732c9 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -45,6 +45,21 @@ struct NativePropertyHooks const NativePropertyHooks *mProtoHooks; }; +struct DOMClass +{ + // A list of interfaces that this object implements, in order of decreasing + // derivedness. + const prototypes::ID mInterfaceChain[prototypes::id::_ID_Count]; + + // We store the DOM object in reserved slot with index DOM_OBJECT_SLOT or in + // the proxy private if we use a proxy object. + // Sometimes it's an nsISupports and sometimes it's not; this class tells + // us which it is. + const bool mDOMObjectIsISupports; + + const NativePropertyHooks* mNativeHooks; +}; + // Special JSClass for reflected DOM objects. struct DOMJSClass { @@ -53,9 +68,7 @@ struct DOMJSClass // only allows brace initialization for aggregate/POD types. JSClass mBase; - // A list of interfaces that this object implements, in order of decreasing - // derivedness. - const prototypes::ID mInterfaceChain[prototypes::id::_ID_Count]; + DOMClass mClass; // We cache the VTable index of GetWrapperCache for objects that support it. // @@ -63,13 +76,6 @@ struct DOMJSClass // XXXkhuey this is unused and needs to die. const int16_t mGetWrapperCacheVTableOffset; - // We store the DOM object in a reserved slot whose index is mNativeSlot. - // Sometimes it's an nsISupports and sometimes it's not; this class tells - // us which it is. - const bool mDOMObjectIsISupports; - - const NativePropertyHooks* mNativeHooks; - static DOMJSClass* FromJSClass(JSClass* base) { MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS); return reinterpret_cast(base); diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp new file mode 100644 index 00000000000..9df3c55ec92 --- /dev/null +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -0,0 +1,228 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=2 sw=2 et tw=99 ft=cpp: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Util.h" + +#include "DOMJSProxyHandler.h" +#include "xpcpublic.h" +#include "xpcprivate.h" +#include "XPCQuickStubs.h" +#include "XPCWrapper.h" +#include "WrapperFactory.h" +#include "nsDOMClassInfo.h" +#include "nsGlobalWindow.h" +#include "nsWrapperCacheInlines.h" +#include "mozilla/dom/BindingUtils.h" + +#include "jsapi.h" +#include "jsatom.h" + +using namespace JS; + +namespace mozilla { +namespace dom { + +jsid s_length_id = JSID_VOID; + +bool +DefineStaticJSVals(JSContext* cx) +{ + JSAutoRequest ar(cx); + + return InternJSString(cx, s_length_id, "length"); +} + + +int HandlerFamily; + +bool +DefineConstructor(JSContext* cx, JSObject* obj, DefineInterface aDefine, nsresult* aResult) +{ + bool enabled; + bool defined = aDefine(cx, obj, &enabled); + MOZ_ASSERT(!defined || enabled, + "We defined a constructor but the new bindings are disabled?"); + *aResult = defined ? NS_OK : NS_ERROR_FAILURE; + return enabled; +} + +// static +JSObject* +DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JSObject* obj) +{ + NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object"); + JSObject* expando = GetExpandoObject(obj); + if (!expando) { + expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr, + js::GetObjectParent(obj)); + if (!expando) { + return NULL; + } + + xpc::CompartmentPrivate* priv = xpc::GetCompartmentPrivate(obj); + if (!priv->RegisterDOMExpandoObject(obj)) { + return NULL; + } + + nsWrapperCache* cache; + CallQueryInterface(UnwrapDOMObject(obj, eProxyDOMObject), &cache); + cache->SetPreservingWrapper(true); + + js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando)); + } + return expando; +} + +bool +DOMProxyHandler::getPropertyDescriptor(JSContext* cx, JSObject* proxy, jsid id, bool set, + JSPropertyDescriptor* desc) +{ + if (!getOwnPropertyDescriptor(cx, proxy, id, set, desc)) { + return false; + } + if (desc->obj) { + return true; + } + + JSObject* proto = js::GetObjectProto(proxy); + if (!proto) { + desc->obj = NULL; + return true; + } + + return JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc); +} + +bool +DOMProxyHandler::defineProperty(JSContext* cx, JSObject* proxy, jsid id, + JSPropertyDescriptor* desc) +{ + if ((desc->attrs & JSPROP_GETTER) && desc->setter == JS_StrictPropertyStub) { + return JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT | + JSREPORT_STRICT_MODE_ERROR, + js_GetErrorMessage, NULL, + JSMSG_GETTER_ONLY); + } + + if (xpc::WrapperFactory::IsXrayWrapper(proxy)) { + return true; + } + + JSObject* expando = EnsureExpandoObject(cx, proxy); + if (!expando) { + return false; + } + + return JS_DefinePropertyById(cx, expando, id, desc->value, desc->getter, desc->setter, + desc->attrs); +} + +bool +DOMProxyHandler::delete_(JSContext* cx, JSObject* proxy, jsid id, bool* bp) +{ + JSBool b = true; + + JSObject* expando; + if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) { + Value v; + if (!JS_DeletePropertyById2(cx, expando, id, &v) || !JS_ValueToBoolean(cx, v, &b)) { + return false; + } + } + + *bp = !!b; + return true; +} + +bool +DOMProxyHandler::enumerate(JSContext* cx, JSObject* proxy, AutoIdVector& props) +{ + JSObject* proto = JS_GetPrototype(proxy); + return getOwnPropertyNames(cx, proxy, props) && + (!proto || js::GetPropertyNames(cx, proto, 0, &props)); +} + +bool +DOMProxyHandler::fix(JSContext* cx, JSObject* proxy, Value* vp) +{ + vp->setUndefined(); + return true; +} + +bool +DOMProxyHandler::has(JSContext* cx, JSObject* proxy, jsid id, bool* bp) +{ + if (!hasOwn(cx, proxy, id, bp)) { + return false; + } + + if (*bp) { + // We have the property ourselves; no need to worry about our prototype + // chain. + return true; + } + + // OK, now we have to look at the proto + JSObject *proto = js::GetObjectProto(proxy); + if (!proto) { + return true; + } + JSBool protoHasProp; + bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp); + if (ok) { + *bp = protoHasProp; + } + return ok; +} + +// static +JSString* +DOMProxyHandler::obj_toString(JSContext* cx, const char* className) +{ + size_t nchars = sizeof("[object ]") - 1 + strlen(className); + jschar* chars = static_cast(JS_malloc(cx, (nchars + 1) * sizeof(jschar))); + if (!chars) { + return NULL; + } + + const char* prefix = "[object "; + nchars = 0; + while ((chars[nchars] = (jschar)*prefix) != 0) { + nchars++, prefix++; + } + while ((chars[nchars] = (jschar)*className) != 0) { + nchars++, className++; + } + chars[nchars++] = ']'; + chars[nchars] = 0; + + JSString* str = JS_NewUCString(cx, chars, nchars); + if (!str) { + JS_free(cx, chars); + } + return str; +} + +int32_t +IdToInt32(JSContext* cx, jsid id) +{ + JSAutoRequest ar(cx); + + jsval idval; + double array_index; + int32_t i; + if (!::JS_IdToValue(cx, id, &idval) || + !::JS_ValueToNumber(cx, idval, &array_index) || + !::JS_DoubleIsInt32(array_index, &i)) { + return -1; + } + + return i; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h new file mode 100644 index 00000000000..4d5237d80dd --- /dev/null +++ b/dom/bindings/DOMJSProxyHandler.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_DOMJSProxyHandler_h +#define mozilla_dom_DOMJSProxyHandler_h + +#include "jsapi.h" +#include "jsatom.h" +#include "jsproxy.h" +#include "xpcpublic.h" +#include "nsString.h" +#include "mozilla/Likely.h" + +#define DOM_PROXY_OBJECT_SLOT js::JSSLOT_PROXY_PRIVATE + +namespace mozilla { +namespace dom { + +enum { + JSPROXYSLOT_EXPANDO = 0 +}; + +template struct Prefable; + +class DOMProxyHandler : public js::BaseProxyHandler +{ +public: + DOMProxyHandler(const DOMClass& aClass) + : js::BaseProxyHandler(ProxyFamily()), + mClass(aClass) + { + } + + bool getPropertyDescriptor(JSContext* cx, JSObject* proxy, jsid id, bool set, + JSPropertyDescriptor* desc); + bool defineProperty(JSContext* cx, JSObject* proxy, jsid id, + JSPropertyDescriptor* desc); + bool delete_(JSContext* cx, JSObject* proxy, jsid id, bool* bp); + bool enumerate(JSContext* cx, JSObject* proxy, JS::AutoIdVector& props); + bool fix(JSContext* cx, JSObject* proxy, JS::Value* vp); + bool has(JSContext* cx, JSObject* proxy, jsid id, bool* bp); + using js::BaseProxyHandler::obj_toString; + + static JSObject* GetExpandoObject(JSObject* obj) + { + MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object"); + JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); + return v.isUndefined() ? NULL : v.toObjectOrNull(); + } + static JSObject* EnsureExpandoObject(JSContext* cx, JSObject* obj); + + const DOMClass& mClass; + +protected: + static JSString* obj_toString(JSContext* cx, const char* className); +}; + +extern jsid s_length_id; + +int32_t IdToInt32(JSContext* cx, jsid id); + +inline int32_t +GetArrayIndexFromId(JSContext* cx, jsid id) +{ + if (MOZ_LIKELY(JSID_IS_INT(id))) { + return JSID_TO_INT(id); + } + if (MOZ_LIKELY(id == s_length_id)) { + return -1; + } + if (MOZ_LIKELY(JSID_IS_ATOM(id))) { + JSAtom* atom = JSID_TO_ATOM(id); + jschar s = *js::GetAtomChars(atom); + if (MOZ_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z')) + return -1; + + uint32_t i; + JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id)); + return js::StringIsArrayIndex(str, &i) ? i : -1; + } + return IdToInt32(cx, id); +} + +inline void +FillPropertyDescriptor(JSPropertyDescriptor* desc, JSObject* obj, bool readonly) +{ + desc->obj = obj; + desc->attrs = (readonly ? JSPROP_READONLY : 0) | JSPROP_ENUMERATE; + desc->getter = NULL; + desc->setter = NULL; + desc->shortid = 0; +} + +inline void +FillPropertyDescriptor(JSPropertyDescriptor* desc, JSObject* obj, jsval v, bool readonly) +{ + desc->value = v; + FillPropertyDescriptor(desc, obj, readonly); +} + +JSObject* +EnsureExpandoObject(JSContext* cx, JSObject* obj); + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_DOMProxyHandler_h */ diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index 20e66ee86cc..20d420fa9df 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -25,3 +25,4 @@ MSG_DEF(MSG_NOT_OBJECT, 0, "Value not an object.") MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 1, "Value does not implement interface {0}.") MSG_DEF(MSG_NOT_IN_UNION, 1, "Value could not be converted to any of: {0}.") MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, "Illegal constructor.") +MSG_DEF(MSG_NO_PROPERTY_SETTER, 1, "{0} doesn't have an indexed property setter.") \ No newline at end of file diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index f091dd768d3..700864569de 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -45,6 +45,7 @@ CPPSRCS = \ $(linked_binding_cpp_files) \ $(filter %.cpp, $(globalgen_targets)) \ BindingUtils.cpp \ + DOMJSProxyHandler.cpp \ $(NULL) EXPORTS_NAMESPACES = $(binding_include_path) mozilla @@ -56,6 +57,7 @@ EXPORTS_mozilla = \ EXPORTS_$(binding_include_path) = \ BindingUtils.h \ DOMJSClass.h \ + DOMJSProxyHandler.h \ Errors.msg \ Nullable.h \ PrimitiveConversions.h \ diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 06881d412ee..3a1c9c4e1af 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -2133,6 +2133,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def hasOverloads(self): return self._hasOverloads + def isIdentifierLess(self): + return self.identifier.name[:2] == "__" + def resolve(self, parentScope): assert isinstance(parentScope, IDLScope) IDLObjectWithIdentifier.resolve(self, parentScope) diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 12964129137..99cb2a6ff1b 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -558,6 +558,102 @@ private: }; +class TestIndexedGetterInterface : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_ISUPPORTS + + // We need a GetParentObject to make binding codegen happy + virtual nsISupports* GetParentObject(); + + uint32_t IndexedGetter(uint32_t, bool&, ErrorResult&); + uint32_t IndexedGetter(uint32_t, ErrorResult&) MOZ_DELETE; + uint32_t Item(uint32_t, ErrorResult&); + uint32_t Item(uint32_t, bool&, ErrorResult&) MOZ_DELETE; + uint32_t GetLength(); +}; + +class TestNamedGetterInterface : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_ISUPPORTS + + // We need a GetParentObject to make binding codegen happy + virtual nsISupports* GetParentObject(); + + void NamedGetter(const nsAString&, bool&, nsAString&, ErrorResult&); +}; + +class TestIndexedAndNamedGetterInterface : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_ISUPPORTS + + // We need a GetParentObject to make binding codegen happy + virtual nsISupports* GetParentObject(); + + uint32_t IndexedGetter(uint32_t, bool&, ErrorResult&); + void NamedGetter(const nsAString&, bool&, nsAString&, ErrorResult&); + void NamedItem(const nsAString&, nsAString&, ErrorResult&); + uint32_t GetLength(); +}; + +class TestIndexedSetterInterface : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_ISUPPORTS + + // We need a GetParentObject to make binding codegen happy + virtual nsISupports* GetParentObject(); + + void IndexedSetter(uint32_t, const nsAString&, ErrorResult&); + void SetItem(uint32_t, const nsAString&, ErrorResult&); +}; + +class TestNamedSetterInterface : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_ISUPPORTS + + // We need a GetParentObject to make binding codegen happy + virtual nsISupports* GetParentObject(); + + void NamedSetter(const nsAString&, TestIndexedSetterInterface&, ErrorResult&); +}; + +class TestIndexedAndNamedSetterInterface : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_ISUPPORTS + + // We need a GetParentObject to make binding codegen happy + virtual nsISupports* GetParentObject(); + + void IndexedSetter(uint32_t, TestIndexedSetterInterface&, ErrorResult&); + void NamedSetter(const nsAString&, TestIndexedSetterInterface&, ErrorResult&); + void SetNamedItem(const nsAString&, TestIndexedSetterInterface&, ErrorResult&); +}; + +class TestIndexedAndNamedGetterAndSetterInterface : public TestIndexedSetterInterface +{ +public: + uint32_t IndexedGetter(uint32_t, bool&, ErrorResult&); + uint32_t Item(uint32_t, ErrorResult&); + void NamedGetter(const nsAString&, bool&, nsAString&, ErrorResult&); + void NamedItem(const nsAString&, nsAString&, ErrorResult&); + void IndexedSetter(uint32_t, int32_t&, ErrorResult&); + void IndexedSetter(uint32_t, const nsAString&, ErrorResult&) MOZ_DELETE; + void NamedSetter(const nsAString&, const nsAString&, ErrorResult&); + void Stringify(nsAString&); + uint32_t GetLength(); +}; + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index cf9c7105521..f3334514cd6 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -372,3 +372,40 @@ dictionary ParentDict : GrandparentDict { dictionary DictContainingDict { Dict memberDict; }; + +interface TestIndexedGetterInterface { + getter long item(unsigned long index); + readonly attribute unsigned long length; +}; + +interface TestNamedGetterInterface { + getter DOMString (DOMString name); +}; + +interface TestIndexedAndNamedGetterInterface { + getter long (unsigned long index); + getter DOMString namedItem(DOMString name); + readonly attribute unsigned long length; +}; + +interface TestIndexedSetterInterface { + setter creator void setItem(unsigned long index, DOMString item); +}; + +interface TestNamedSetterInterface { + setter creator void (DOMString name, TestIndexedSetterInterface item); +}; + +interface TestIndexedAndNamedSetterInterface { + setter creator void (unsigned long index, TestIndexedSetterInterface item); + setter creator void setNamedItem(DOMString name, TestIndexedSetterInterface item); +}; + +interface TestIndexedAndNamedGetterAndSetterInterface : TestIndexedSetterInterface { + getter long item(unsigned long index); + getter DOMString namedItem(DOMString name); + setter creator void (unsigned long index, long item); + setter creator void (DOMString name, DOMString item); + stringifier DOMString (); + readonly attribute unsigned long length; +}; diff --git a/dom/workers/Worker.cpp b/dom/workers/Worker.cpp index 0ff9d763482..5f434cfe909 100644 --- a/dom/workers/Worker.cpp +++ b/dom/workers/Worker.cpp @@ -233,7 +233,8 @@ private: Finalize(JSFreeOp* aFop, JSObject* aObj) { JS_ASSERT(JS_GetClass(aObj) == Class()); - WorkerPrivate* worker = UnwrapDOMObject(aObj); + WorkerPrivate* worker = + UnwrapDOMObject(aObj, eRegularDOMObject); if (worker) { worker->_finalize(aFop); } @@ -243,7 +244,8 @@ private: Trace(JSTracer* aTrc, JSObject* aObj) { JS_ASSERT(JS_GetClass(aObj) == Class()); - WorkerPrivate* worker = UnwrapDOMObject(aObj); + WorkerPrivate* worker = + UnwrapDOMObject(aObj, eRegularDOMObject); if (worker) { worker->_trace(aTrc); } @@ -303,9 +305,13 @@ DOMJSClass Worker::sClass = { JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, Trace }, - { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count, - prototypes::id::_ID_Count }, - -1, false, &sNativePropertyHooks + { + { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count, + prototypes::id::_ID_Count }, + false, + &sNativePropertyHooks + }, + -1 }; JSPropertySpec Worker::sProperties[] = { @@ -376,7 +382,7 @@ private: if (aObj) { JSClass* classPtr = JS_GetClass(aObj); if (classPtr == Class()) { - return UnwrapDOMObject(aObj); + return UnwrapDOMObject(aObj, eRegularDOMObject); } } @@ -393,7 +399,8 @@ private: Finalize(JSFreeOp* aFop, JSObject* aObj) { JS_ASSERT(JS_GetClass(aObj) == Class()); - WorkerPrivate* worker = UnwrapDOMObject(aObj); + WorkerPrivate* worker = + UnwrapDOMObject(aObj, eRegularDOMObject); if (worker) { worker->_finalize(aFop); } @@ -403,7 +410,8 @@ private: Trace(JSTracer* aTrc, JSObject* aObj) { JS_ASSERT(JS_GetClass(aObj) == Class()); - WorkerPrivate* worker = UnwrapDOMObject(aObj); + WorkerPrivate* worker = + UnwrapDOMObject(aObj, eRegularDOMObject); if (worker) { worker->_trace(aTrc); } @@ -423,9 +431,13 @@ DOMJSClass ChromeWorker::sClass = { JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, Trace, }, - { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count, - prototypes::id::_ID_Count }, - -1, false, &sNativePropertyHooks + { + { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count, + prototypes::id::_ID_Count }, + false, + &sNativePropertyHooks + }, + -1 }; WorkerPrivate* @@ -434,7 +446,7 @@ Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj, { JSClass* classPtr = JS_GetClass(aObj); if (classPtr == Class() || classPtr == ChromeWorker::Class()) { - return UnwrapDOMObject(aObj); + return UnwrapDOMObject(aObj, eRegularDOMObject); } JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index cf61a1a2bda..0f1bf7ae62c 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -768,7 +768,8 @@ private: { JSClass* classPtr = JS_GetClass(aObj); if (classPtr == Class()) { - return UnwrapDOMObject(aObj); + return UnwrapDOMObject(aObj, + eRegularDOMObject); } JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, @@ -803,7 +804,7 @@ private: { JS_ASSERT(JS_GetClass(aObj) == Class()); DedicatedWorkerGlobalScope* scope = - UnwrapDOMObject(aObj); + UnwrapDOMObject(aObj, eRegularDOMObject); if (scope) { DestroyProtoOrIfaceCache(aObj); scope->_finalize(aFop); @@ -815,7 +816,7 @@ private: { JS_ASSERT(JS_GetClass(aObj) == Class()); DedicatedWorkerGlobalScope* scope = - UnwrapDOMObject(aObj); + UnwrapDOMObject(aObj, eRegularDOMObject); if (scope) { mozilla::dom::TraceProtoOrIfaceCache(aTrc, aObj); scope->_trace(aTrc); @@ -859,9 +860,13 @@ DOMJSClass DedicatedWorkerGlobalScope::sClass = { JS_EnumerateStub, reinterpret_cast(Resolve), JS_ConvertStub, Finalize, NULL, NULL, NULL, NULL, Trace }, - { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count, - prototypes::id::_ID_Count }, - -1, false, &sNativePropertyHooks + { + { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count, + prototypes::id::_ID_Count }, + false, + &sNativePropertyHooks + }, + -1 }; JSPropertySpec DedicatedWorkerGlobalScope::sProperties[] = { @@ -890,7 +895,7 @@ WorkerGlobalScope::GetInstancePrivate(JSContext* aCx, JSObject* aObj, JS_ASSERT(classPtr != Class()); if (classPtr == DedicatedWorkerGlobalScope::Class()) { - return UnwrapDOMObject(aObj); + return UnwrapDOMObject(aObj, eRegularDOMObject); } JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index 2837847a9e0..c0153174b50 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -75,12 +75,7 @@ XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface) *iface = (nsISupports*) xpc_GetJSPrivate(obj); return true; } - if (jsclass && IsDOMClass(jsclass) && - DOMJSClass::FromJSClass(jsclass)->mDOMObjectIsISupports) { - *iface = UnwrapDOMObject(obj); - return true; - } - return false; + return UnwrapDOMObjectToISupports(obj, *iface); } /***************************************************************************/ diff --git a/js/xpconnect/src/XPCJSID.cpp b/js/xpconnect/src/XPCJSID.cpp index 8e9d31ab077..276b9e41ac1 100644 --- a/js/xpconnect/src/XPCJSID.cpp +++ b/js/xpconnect/src/XPCJSID.cpp @@ -482,15 +482,10 @@ nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper, } nsISupports *identity; - if (mozilla::dom::binding::instanceIsProxy(obj)) { + if (mozilla::dom::oldproxybindings::instanceIsProxy(obj)) { identity = static_cast(js::GetProxyPrivate(obj).toPrivate()); - } else if (mozilla::dom::IsDOMClass(js::GetObjectJSClass(obj)) && - mozilla::dom::DOMJSClass::FromJSClass( - js::GetObjectJSClass(obj))->mDOMObjectIsISupports) { - identity = - mozilla::dom::UnwrapDOMObject(obj); - } else { + } else if (!mozilla::dom::UnwrapDOMObjectToISupports(obj, identity)) { identity = nullptr; } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index b4fcbe24003..f363ebc1ebd 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -447,15 +447,14 @@ SuspectDOMExpandos(nsPtrHashKey *key, void *arg) Closure *closure = static_cast(arg); JSObject* obj = key->GetKey(); nsISupports* native = nullptr; - if (js::IsProxy(obj)) { - NS_ASSERTION(dom::binding::instanceIsProxy(obj), - "Not a DOM proxy?"); + if (dom::oldproxybindings::instanceIsProxy(obj)) { native = static_cast(js::GetProxyPrivate(obj).toPrivate()); } else { - NS_ASSERTION(dom::DOMJSClass::FromJSClass(JS_GetClass(obj))->mDOMObjectIsISupports, - "Someone added a wrapper for a non-nsISupports native to DOMExpandos!"); - native = dom::UnwrapDOMObject(obj); + const dom::DOMClass* clasp; + dom::DOMObjectSlot slot = GetDOMClass(obj, clasp); + MOZ_ASSERT(slot != dom::eNonDOMObject && clasp->mDOMObjectIsISupports); + native = dom::UnwrapDOMObject(obj, slot); } closure->cb->NoteXPCOMRoot(native); return PL_DHASH_NEXT; @@ -2294,7 +2293,8 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx) } } - ok = mozilla::dom::binding::DefineStaticJSVals(cx); + ok = mozilla::dom::DefineStaticJSVals(cx) && + mozilla::dom::oldproxybindings::DefineStaticJSVals(cx); if (!ok) return false; diff --git a/js/xpconnect/src/XPCQuickStubs.cpp b/js/xpconnect/src/XPCQuickStubs.cpp index 099bcf38d24..080100933c6 100644 --- a/js/xpconnect/src/XPCQuickStubs.cpp +++ b/js/xpconnect/src/XPCQuickStubs.cpp @@ -697,9 +697,7 @@ getWrapper(JSContext *cx, *cur = nullptr; *tearoff = nullptr; - js::Class* clasp = js::GetObjectClass(obj); - if (dom::IsDOMClass(clasp) || - dom::binding::instanceIsProxy(obj)) { + if (dom::IsDOMObject(obj)) { *cur = obj; return NS_OK; @@ -711,6 +709,7 @@ getWrapper(JSContext *cx, // object reflection of a particular interface (ie, |foo.nsIBar|). These // JS objects are parented to their wrapper, so we snag the tearoff object // along the way (if desired), and then set |obj| to its parent. + js::Class* clasp = js::GetObjectClass(obj); if (clasp == &XPC_WN_Tearoff_JSClass) { *tearoff = (XPCWrappedNativeTearOff*) js::GetObjectPrivate(obj); obj = js::GetObjectParent(obj); @@ -751,23 +750,14 @@ castNative(JSContext *cx, } else if (cur) { nsISupports *native; QITableEntry *entries; - js::Class* clasp = js::GetObjectClass(cur); - if (dom::IsDOMClass(clasp)) { - dom::DOMJSClass* domClass = dom::DOMJSClass::FromJSClass(clasp); - if (!domClass->mDOMObjectIsISupports) { - *pThisRef = nullptr; - return NS_ERROR_ILLEGAL_VALUE; - } - native = dom::UnwrapDOMObject(cur); + if (mozilla::dom::UnwrapDOMObjectToISupports(cur, native)) { entries = nullptr; - } else if (dom::binding::instanceIsProxy(cur)) { - native = static_cast(js::GetProxyPrivate(cur).toPrivate()); - entries = nullptr; - } else if (IS_WRAPPER_CLASS(clasp) && IS_SLIM_WRAPPER_OBJECT(cur)) { + } else if (IS_SLIM_WRAPPER(cur)) { native = static_cast(xpc_GetJSPrivate(cur)); entries = GetOffsetsFromSlimWrapper(cur); } else { - MOZ_NOT_REACHED("what kind of wrapper is this?"); + *pThisRef = nullptr; + return NS_ERROR_ILLEGAL_VALUE; } if (NS_SUCCEEDED(getNative(native, entries, cur, iid, ppThis, pThisRef, vp))) { diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index e5852e75016..4e5f9df7373 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -31,10 +31,9 @@ bool xpc_OkToHandOutWrapper(nsWrapperCache *cache) { NS_ABORT_IF_FALSE(cache->GetWrapper(), "Must have wrapper"); - NS_ABORT_IF_FALSE(cache->IsDOMBinding() || IS_WN_WRAPPER(cache->GetWrapper()), - "Must have proxy or XPCWrappedNative wrapper"); - return cache->IsDOMBinding() ? - mozilla::dom::binding::instanceIsProxy(cache->GetWrapper()) : + NS_ABORT_IF_FALSE(IS_WN_WRAPPER(cache->GetWrapper()), + "Must have XPCWrappedNative wrapper"); + return !static_cast(xpc_GetJSPrivate(cache->GetWrapper()))-> NeedsSOW(); } diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp index 9a4b21c6f2c..70afa6350cc 100644 --- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -217,23 +217,20 @@ XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal, native = aNative; } else { const JSClass *jsClass = js::GetObjectJSClass(aGlobal); - nsISupports *priv; if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { // Our global has an nsISupports native pointer. Let's // see whether it's what we want. - priv = static_cast(xpc_GetJSPrivate(aGlobal)); - } else if (dom::IsDOMClass(jsClass) && - dom::DOMJSClass::FromJSClass(jsClass)->mDOMObjectIsISupports) { - priv = dom::UnwrapDOMObject(aGlobal); - } else { - priv = nullptr; + nsISupports *priv = + static_cast(xpc_GetJSPrivate(aGlobal)); + nsCOMPtr wn = do_QueryInterface(priv); + if (wn) + native = static_cast(wn.get())->GetIdentityObject(); + else + native = nullptr; + } else if (!mozilla::dom::UnwrapDOMObjectToISupports(aGlobal, native)) { + native = nullptr; } - nsCOMPtr wn = do_QueryInterface(priv); - if (wn) - native = static_cast(wn.get())->GetIdentityObject(); - else - native = priv; } // Now init our script object principal, if the new global has one. diff --git a/js/xpconnect/src/codegen.py b/js/xpconnect/src/codegen.py index 08181c7199b..42bbebdf6e8 100644 --- a/js/xpconnect/src/codegen.py +++ b/js/xpconnect/src/codegen.py @@ -293,6 +293,8 @@ def outParamForm(name, type): if type.specialtype == 'jsval': return 'vp' elif type.modifier == 'ref': + if isStringType(type): + return '(nsAString&)' + name return name else: return '&' + name diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index dbf83bc982a..c15e41b3a34 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -26,7 +26,7 @@ using namespace mozilla::dom; namespace mozilla { namespace dom { -namespace binding { +namespace oldproxybindings { enum { JSPROXYSLOT_EXPANDO = 0 diff --git a/js/xpconnect/src/dombindings.h b/js/xpconnect/src/dombindings.h index c807eb14d24..5f15187edca 100644 --- a/js/xpconnect/src/dombindings.h +++ b/js/xpconnect/src/dombindings.h @@ -15,7 +15,7 @@ namespace mozilla { namespace dom { -namespace binding { +namespace oldproxybindings { class ProxyHandler : public js::BaseProxyHandler { protected: diff --git a/js/xpconnect/src/dombindingsgen.py b/js/xpconnect/src/dombindingsgen.py index b7ef536c6a5..019ce7723df 100644 --- a/js/xpconnect/src/dombindingsgen.py +++ b/js/xpconnect/src/dombindingsgen.py @@ -375,7 +375,7 @@ def writeHeaderFile(filename, config): f.write("namespace mozilla {\n" "namespace dom {\n" - "namespace binding {\n\n") + "namespace oldproxybindings {\n\n") f.write("bool\n" "DefinePropertyStaticJSVals(JSContext *cx);\n\n") @@ -638,7 +638,7 @@ def writeStubFile(filename, config, interfaces): f.write("namespace mozilla {\n" "namespace dom {\n" - "namespace binding {\n\n") + "namespace oldproxybindings {\n\n") f.write("// Property name ids\n\n") @@ -712,7 +712,7 @@ def writeStubFile(filename, config, interfaces): else: setterName = "NULL" - propertiesList.append(" { s_length_id, length_getter, %s }" % setterName) + propertiesList.append(" { mozilla::dom::s_length_id, length_getter, %s }" % setterName) continue isAttr = (member.kind == 'attribute') diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 14a0838a05f..a73d9b828e9 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -836,16 +836,17 @@ NoteGCThingXPCOMChildren(js::Class *clasp, JSObject *obj, clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)"); cb.NoteXPCOMChild(static_cast(xpc_GetJSPrivate(obj))); - } else if (binding::instanceIsProxy(obj)) { + } else if (oldproxybindings::instanceIsProxy(obj)) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "js::GetProxyPrivate(obj)"); nsISupports *identity = static_cast(js::GetProxyPrivate(obj).toPrivate()); cb.NoteXPCOMChild(identity); - } else if (IsDOMClass(clasp) && - DOMJSClass::FromJSClass(clasp)->mDOMObjectIsISupports) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "UnwrapDOMObject(obj)"); - nsISupports *identity = UnwrapDOMObject(obj); - cb.NoteXPCOMChild(identity); + } else { + nsISupports *identity; + if (UnwrapDOMObjectToISupports(obj, identity)) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "UnwrapDOMObject(obj)"); + cb.NoteXPCOMChild(identity); + } } } @@ -1419,7 +1420,8 @@ nsXPConnect::GetNativeOfWrapper(JSContext * aJSContext, if (obj2) return (nsISupports*)xpc_GetJSPrivate(obj2); - if (mozilla::dom::binding::instanceIsProxy(aJSObj)) { + if (mozilla::dom::IsDOMProxy(aJSObj) || + mozilla::dom::oldproxybindings::instanceIsProxy(aJSObj)) { // FIXME: Provide a fast non-refcounting way to get the canonical // nsISupports from the proxy. nsISupports *supports = @@ -1457,7 +1459,8 @@ nsXPConnect::GetJSObjectOfWrapper(JSContext * aJSContext, *_retval = obj2; return NS_OK; } - if (mozilla::dom::binding::instanceIsProxy(aJSObj)) { + if (mozilla::dom::IsDOMProxy(aJSObj) || + mozilla::dom::oldproxybindings::instanceIsProxy(aJSObj)) { *_retval = aJSObj; return NS_OK; } diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 1762d11df7a..646cb6f1ac1 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -131,7 +131,7 @@ xpc_FastGetCachedWrapper(nsWrapperCache *cache, JSObject *scope, jsval *vp) "Should never have a slim wrapper when IsDOMBinding()"); if (wrapper && js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(scope) && - (IS_SLIM_WRAPPER(wrapper) || + (IS_SLIM_WRAPPER(wrapper) || cache->IsDOMBinding() || xpc_OkToHandOutWrapper(cache))) { *vp = OBJECT_TO_JSVAL(wrapper); return wrapper; @@ -306,11 +306,10 @@ xpc_JSCompartmentParticipant(); namespace mozilla { namespace dom { -namespace binding { extern int HandlerFamily; inline void* ProxyFamily() { return &HandlerFamily; } -inline bool instanceIsProxy(JSObject *obj) +inline bool IsDOMProxy(JSObject *obj) { return js::IsProxy(obj) && js::GetProxyHandler(obj)->family() == ProxyFamily(); @@ -327,7 +326,22 @@ extern bool DefineConstructor(JSContext *cx, JSObject *obj, DefineInterface aDefine, nsresult *aResult); -} // namespace binding +namespace oldproxybindings { + +extern int HandlerFamily; +inline void* ProxyFamily() { return &HandlerFamily; } +inline bool instanceIsProxy(JSObject *obj) +{ + return js::IsProxy(obj) && + js::GetProxyHandler(obj)->family() == ProxyFamily(); +} +extern bool +DefineStaticJSVals(JSContext *cx); +void +Register(nsScriptNameSpaceManager* aNameSpaceManager); + +} // namespace oldproxybindings + } // namespace dom } // namespace mozilla diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index 63dc0e191fb..4d36a9152d9 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -299,7 +299,7 @@ GetXrayType(JSObject *obj) if (mozilla::dom::IsDOMClass(Jsvalify(clasp))) { return XrayForDOMObject; } - if (mozilla::dom::binding::instanceIsProxy(obj)) { + if (mozilla::dom::oldproxybindings::instanceIsProxy(obj)) { return XrayForDOMProxyObject; } if (IS_WRAPPER_CLASS(clasp) || clasp->ext.innerObject) { @@ -385,7 +385,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO wrapper = &FilteringWrapper::singleton; else wrapper = &FilteringWrapper::singleton; - } else if (mozilla::dom::binding::instanceIsProxy(obj)) { + } else if (mozilla::dom::oldproxybindings::instanceIsProxy(obj)) { wrapper = &FilteringWrapper::singleton; } else if (mozilla::dom::IsDOMClass(JS_GetClass(obj))) { wrapper = &FilteringWrapper::singleton; diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 7deea1211a9..7fd43ef7c97 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1057,7 +1057,7 @@ DOMXrayTraits::resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject { JSObject *obj = getInnerObject(wrapper); const NativePropertyHooks *nativeHooks = - DOMJSClass::FromJSClass(JS_GetClass(obj))->mNativeHooks; + DOMJSClass::FromJSClass(JS_GetClass(obj))->mClass.mNativeHooks; do { if (nativeHooks->mResolveProperty(cx, wrapper, id, set, desc) && @@ -1108,7 +1108,7 @@ DOMXrayTraits::enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags, JSObject *obj = getInnerObject(wrapper); const NativePropertyHooks *nativeHooks = - DOMJSClass::FromJSClass(JS_GetClass(obj))->mNativeHooks; + DOMJSClass::FromJSClass(JS_GetClass(obj))->mClass.mNativeHooks; do { if (!nativeHooks->mEnumerateProperties(props)) { @@ -1184,7 +1184,7 @@ XrayToString(JSContext *cx, unsigned argc, jsval *vp) nsAutoString result(NS_LITERAL_STRING("[object XrayWrapper ")); JSObject *obj = &js::GetProxyPrivate(wrapper).toObject(); - if (mozilla::dom::binding::instanceIsProxy(obj)) { + if (IsDOMProxy(obj) || oldproxybindings::instanceIsProxy(obj)) { JSString *wrapperStr = js::GetProxyHandler(wrapper)->obj_toString(cx, wrapper); size_t length; const jschar* chars = JS_GetStringCharsAndLength(cx, wrapperStr, &length);