Bug 983619 - Add Window.getInterface/QueryInterface to WebIDL. r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Sat, 15 Feb 2014 22:12:33 +0100
changeset 177573 39b430a304cabc4b4700ea00d95ac8c859ca19aa
parent 177572 f1e4f0f82a9a1572479ee08ed873cc61baeca0da
child 177574 1888dccad4f7b476ebc5b076c37a6c7b1affe6d4
push id26556
push userryanvm@gmail.com
push dateTue, 08 Apr 2014 22:16:57 +0000
treeherdermozilla-central@5811efc11011 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs983619
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 983619 - Add Window.getInterface/QueryInterface to WebIDL. r=bz.
content/base/src/nsXMLHttpRequest.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/LegacyQueryInterface.webidl
dom/webidl/Window.webidl
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -3659,27 +3659,17 @@ nsXMLHttpRequest::GetInterface(const nsI
   }
 
   return QueryInterface(aIID, aResult);
 }
 
 JS::Value
 nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aRv)
 {
-  const nsID* iid = aIID->GetID();
-  nsCOMPtr<nsISupports> result;
-  JS::Rooted<JS::Value> v(aCx, JSVAL_NULL);
-  aRv = GetInterface(*iid, getter_AddRefs(result));
-  NS_ENSURE_FALSE(aRv.Failed(), JSVAL_NULL);
-
-  JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
-  JSAutoCompartment ac(aCx, wrapper);
-  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, wrapper));
-  aRv = nsContentUtils::WrapNative(aCx, global, result, iid, &v);
-  return aRv.Failed() ? JSVAL_NULL : v;
+  return dom::GetInterface(aCx, this, aIID, aRv);
 }
 
 nsXMLHttpRequestUpload*
 nsXMLHttpRequest::Upload()
 {
   if (!mUpload) {
     mUpload = new nsXMLHttpRequestUpload(this);
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -10370,16 +10370,22 @@ nsGlobalWindow::GetInterface(const nsIID
   }
   else {
     return QueryInterface(aIID, aSink);
   }
 
   return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
 }
 
+JS::Value
+nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aError)
+{
+  return dom::GetInterface(aCx, this, aIID, aError);
+}
+
 void
 nsGlobalWindow::FireOfflineStatusEvent()
 {
   if (!IsCurrentInnerWindow())
     return;
   nsAutoString name;
   if (NS_IsOffline()) {
     name.AssignLiteral("offline");
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -79,16 +79,17 @@ class nsIArray;
 class nsIBaseWindow;
 class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShellTreeOwner;
 class nsIDOMCrypto;
 class nsIDOMOfflineResourceList;
 class nsIScrollableFrame;
 class nsIControllers;
+class nsIJSID;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
 class nsIWebBrowserChrome;
 
 class nsDOMWindowList;
 class nsLocation;
 class nsScreen;
 class nsHistory;
@@ -995,16 +996,19 @@ public:
                        mozilla::dom::Element* aPanel,
                        mozilla::ErrorResult& aError);
 
   JS::Value GetDialogArguments(JSContext* aCx, mozilla::ErrorResult& aError);
   JS::Value GetReturnValue(JSContext* aCx, mozilla::ErrorResult& aError);
   void SetReturnValue(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
                       mozilla::ErrorResult& aError);
 
+  JS::Value GetInterface(JSContext* aCx, nsIJSID* aIID,
+                         mozilla::ErrorResult& aError);
+
 protected:
   // Array of idle observers that are notified of idle events.
   nsTObserverArray<IdleObserverHolder> mIdleObservers;
 
   // Idle timer used for function callbacks to notify idle observers.
   nsCOMPtr<nsITimer> mIdleTimer;
 
   // Idle fuzz time added to idle timer callbacks.
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -810,23 +810,30 @@ QueryInterface(JSContext* cx, unsigned a
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
   if (thisv.isNull())
     return false;
 
   // Get the object. It might be a security wrapper, in which case we do a checked
   // unwrap.
   JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
-  JSObject* obj = js::CheckedUnwrap(origObj);
+  JSObject* obj = js::CheckedUnwrap(origObj, /* stopAtOuter = */ false);
   if (!obj) {
       JS_ReportError(cx, "Permission denied to access object");
       return false;
   }
 
-  nsISupports* native = UnwrapDOMObjectToISupports(obj);
+  // Switch this to UnwrapDOMObjectToISupports once our global objects are
+  // using new bindings.
+  JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj));
+  nsISupports* native = nullptr;
+  nsCOMPtr<nsISupports> nativeRef;
+  xpc_qsUnwrapArg<nsISupports>(cx, val, &native,
+                               static_cast<nsISupports**>(getter_AddRefs(nativeRef)),
+                               &val);
   if (!native) {
     return Throw(cx, NS_ERROR_FAILURE);
   }
 
   if (argc < 1) {
     return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   }
 
@@ -857,16 +864,39 @@ QueryInterface(JSContext* cx, unsigned a
   if (NS_FAILED(rv)) {
     return Throw(cx, rv);
   }
 
   *vp = thisv;
   return true;
 }
 
+JS::Value
+GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
+                 nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError)
+{
+  const nsID* iid = aIID->GetID();
+
+  nsRefPtr<nsISupports> result;
+  aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
+  if (aError.Failed()) {
+    return JS::NullValue();
+  }
+
+  JS::Rooted<JSObject*> wrapper(aCx, aCache->GetWrapper());
+  JS::Rooted<JSObject*> global(aCx, js::GetGlobalForObjectCrossCompartment(wrapper));
+  JS::Rooted<JS::Value> v(aCx, JSVAL_NULL);
+  if (!WrapObject(aCx, global, result, iid, &v)) {
+    aError.Throw(NS_ERROR_FAILURE);
+    return JS::NullValue();
+  }
+
+  return v;
+}
+
 bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -29,16 +29,17 @@
 #include "MainThreadUtils.h"
 #include "nsISupportsImpl.h"
 #include "qsObjectHelper.h"
 #include "xpcpublic.h"
 #include "nsIVariant.h"
 
 #include "nsWrapperCacheInlines.h"
 
+class nsIJSID;
 class nsPIDOMWindow;
 
 extern nsresult
 xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle<JS::Value> v, const nsIID& iid, void** ppArg,
                     nsISupports** ppArgRef, JS::MutableHandle<JS::Value> vp);
 
 namespace mozilla {
 namespace dom {
@@ -1601,16 +1602,27 @@ WantsQueryInterface
   static_assert(IsBaseOf<nsISupports, T>::value,
                 "QueryInterface can't work without an nsISupports.");
   static bool Enabled(JSContext* aCx, JSObject* aGlobal)
   {
     return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal);
   }
 };
 
+JS::Value
+GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
+                 nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError);
+
+template<class T>
+JS::Value
+GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, ErrorResult& aError)
+{
+  return GetInterfaceImpl(aCx, aThis, aThis, aIID, aError);
+}
+
 bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 
 // vp is allowed to be null; in that case no get will be attempted,
 // and *found will simply indicate whether the property exists.
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -1219,37 +1219,43 @@ function createInterfaceMap(isXBLScope) 
   var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
   var isB2G = !isDesktop && !navigator.userAgent.contains("Android");
   var hasPermission = function (aPermission) {
     return SpecialPowers.hasPermission(aPermission, window.document);
   };
 
   var interfaceMap = {};
 
-  function addInterfaces(interfaces, shouldExpect)
+  function addInterfaces(interfaces)
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
-        interfaceMap[entry] = shouldExpect;
+        interfaceMap[entry] = true;
       } else if ((entry.nightly === !isNightly) ||
                  (entry.xbl === !isXBLScope) ||
                  (entry.desktop === !isDesktop) ||
                  (entry.b2g === !isB2G) ||
                  (entry.release === !isRelease) ||
                  (entry.pref && !prefs.getBoolPref(entry.pref))  ||
                  (entry.permission && !hasPermission(entry.permission))) {
         interfaceMap[entry.name] = false;
       } else {
-        interfaceMap[entry.name] = shouldExpect;
+        interfaceMap[entry.name] = true;
       }
     }
   }
 
-  addInterfaces(ecmaGlobals, true);
-  addInterfaces(interfaceNamesInGlobalScope, true);
+  addInterfaces(ecmaGlobals);
+  addInterfaces(interfaceNamesInGlobalScope);
+  if (isXBLScope) {
+    // We expose QueryInterface to XBL scopes. It's not an interface but we
+    // need to handle it because it's an own property of the global and the
+    // property name starts with an uppercase letter.
+    interfaceMap["QueryInterface"] = true;
+  }
 
   return interfaceMap;
 }
 
 function runTest(isXBLScope) {
   var interfaceMap = createInterfaceMap(isXBLScope);
   for (var name of Object.getOwnPropertyNames(window)) {
     // An interface name should start with an upper case character.
--- a/dom/webidl/LegacyQueryInterface.webidl
+++ b/dom/webidl/LegacyQueryInterface.webidl
@@ -81,12 +81,13 @@ StyleSheet implements LegacyQueryInterfa
 Text implements LegacyQueryInterface;
 Touch implements LegacyQueryInterface;
 TouchList implements LegacyQueryInterface;
 TreeColumns implements LegacyQueryInterface;
 TreeWalker implements LegacyQueryInterface;
 UndoManager implements LegacyQueryInterface;
 ValidityState implements LegacyQueryInterface;
 WebSocket implements LegacyQueryInterface;
+Window implements LegacyQueryInterface;
 XMLHttpRequest implements LegacyQueryInterface;
 XMLHttpRequestUpload implements LegacyQueryInterface;
 XMLSerializer implements LegacyQueryInterface;
 XPathEvaluator implements LegacyQueryInterface;
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -11,16 +11,17 @@
  * http://dev.w3.org/csswg/cssom-view/
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
  * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
  */
 
 interface ApplicationCache;
+interface IID;
 interface MozFrameRequestCallback;
 interface nsIBrowserDOMWindow;
 interface nsIMessageBroadcaster;
 interface nsIDOMCrypto;
 typedef any Transferable;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
 [Global, NeedNewResolve]
@@ -344,16 +345,18 @@ partial interface Window {
   [Throws, ChromeOnly] WindowProxy? openDialog(optional DOMString url = "",
                                                optional DOMString name = "",
                                                optional DOMString options = "",
                                                any... extraArguments);
 
   [Replaceable, Throws] readonly attribute object? content;
 
   [ChromeOnly, Throws] readonly attribute object? __content;
+
+  [Throws, ChromeOnly] any getInterface(IID iid);
 };
 
 Window implements TouchEventHandlers;
 
 Window implements OnErrorEventHandlerForWindow;
 
 // ConsoleAPI
 partial interface Window {