Bug 937317 - Tests. r=bz
☠☠ backed out by 28ef06bb9b8b ☠ ☠
authorBobby Holley <bobbyholley@gmail.com>
Thu, 05 Dec 2013 21:34:18 -0800
changeset 174787 c84430c040ddfb0f870aa6344d0cafcd007f8765
parent 174786 a0906b92810ef2802c2ddaacaf9e16dbe8e63581
child 174788 c76cf8b7ae40fdd6c6ab3033ba86c9f08150f36d
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs937317
milestone28.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 937317 - Tests. r=bz
js/xpconnect/idl/xpccomponents.idl
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/tests/chrome/chrome.ini
js/xpconnect/tests/chrome/test_scriptSettings.xul
--- a/js/xpconnect/idl/xpccomponents.idl
+++ b/js/xpconnect/idl/xpccomponents.idl
@@ -115,17 +115,17 @@ interface nsIXPCComponents_utils_Sandbox
 interface ScheduledGCCallback : nsISupports
 {
     void callback();
 };
 
 /**
 * interface of Components.utils
 */
-[scriptable, uuid(ef621cac-c818-464a-9fb1-9a35731a7f32)]
+[scriptable, uuid(e14f588b-63aa-4091-be82-a459a52f8ca6)]
 interface nsIXPCComponents_Utils : nsISupports
 {
 
     /* reportError is designed to be called from JavaScript only.
      *
      * It will report a JS Error object to the JS console, and return. It
      * is meant for use in exception handler blocks which want to "eat"
      * an exception, but still want to report it to the console.
@@ -503,16 +503,27 @@ interface nsIXPCComponents_Utils : nsISu
 
     /**
      * Get a DOM classinfo for the given classname.  Only some class
      * names are supported.
      */
     nsIClassInfo getDOMClassInfo(in AString aClassName);
 
     /**
+     * Gets the incument global for the execution of this function. For internal
+     * and testing use only.
+     *
+     * If |callback| is passed, it is invoked with the incumbent global as its
+     * sole argument. This allows the incumbent global to be measured in callback
+     * environments with no scripted frames on the stack.
+     */
+    [implicit_jscontext]
+    jsval getIncumbentGlobal([optional] in jsval callback);
+
+    /**
       * Retrieve the last time, in microseconds since epoch, that a given
       * watchdog-related event occured.
       *
       * Valid categories:
       *   "RuntimeStateChange"      - Runtime switching between active and inactive states
       *   "WatchdogWakeup"          - Watchdog waking up from sleeping
       *   "WatchdogHibernateStart"  - Watchdog begins hibernating
       *   "WatchdogHibernateStop"   - Watchdog stops hibernating
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3483,16 +3483,43 @@ NS_IMETHODIMP
 nsXPCComponents_Utils::GetDOMClassInfo(const nsAString& aClassName,
                                        nsIClassInfo** aClassInfo)
 {
     *aClassInfo = nullptr;
     return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
+nsXPCComponents_Utils::GetIncumbentGlobal(const Value &aCallback,
+                                          JSContext *aCx, Value *aOut)
+{
+    nsCOMPtr<nsIGlobalObject> global = mozilla::dom::GetIncumbentGlobal();
+    RootedValue globalVal(aCx);
+
+    if (!global) {
+        globalVal = NullValue();
+    } else {
+        // Note: We rely on the wrap call for outerization.
+        globalVal = ObjectValue(*global->GetGlobalJSObject());
+        if (!JS_WrapValue(aCx, &globalVal))
+            return NS_ERROR_FAILURE;
+    }
+
+    // Invoke the callback, if passed.
+    if (aCallback.isObject()) {
+        Value ignored;
+        if (!JS_CallFunctionValue(aCx, nullptr, aCallback, 1, globalVal.address(), &ignored))
+            return NS_ERROR_FAILURE;
+    }
+
+    *aOut = globalVal;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsXPCComponents_Utils::GetWatchdogTimestamp(const nsAString& aCategory, PRTime *aOut)
 {
     WatchdogTimestampCategory category;
     if (aCategory.EqualsLiteral("RuntimeStateChange"))
         category = TimestampRuntimeStateChange;
     else if (aCategory.EqualsLiteral("WatchdogWakeup"))
         category = TimestampWatchdogWakeup;
     else if (aCategory.EqualsLiteral("WatchdogHibernateStart"))
--- a/js/xpconnect/tests/chrome/chrome.ini
+++ b/js/xpconnect/tests/chrome/chrome.ini
@@ -62,16 +62,17 @@ support-files =
 [test_expandosharing.xul]
 [test_exposeInDerived.xul]
 [test_getweakmapkeys.xul]
 [test_mozMatchesSelector.xul]
 [test_nodelists.xul]
 [test_paris_weakmap_keys.xul]
 [test_precisegc.xul]
 [test_sandboxImport.xul]
+[test_scriptSettings.xul]
 [test_weakmap_keys_preserved.xul]
 [test_weakmap_keys_preserved2.xul]
 [test_weakref.xul]
 [test_wrappers.xul]
 [test_weakmaps.xul]
 skip-if = os == "win" # Bug 820471
 [test_wrappers-2.xul]
 # Disabled until this test gets updated to test the new proxy based wrappers.
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_scriptSettings.xul
@@ -0,0 +1,125 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=937317
+-->
+<window title="Mozilla Bug 937317"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=937317"
+     target="_blank">Mozilla Bug 937317</a>
+  </body>
+
+  <!-- test code goes here -->
+  <iframe></iframe>
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for the script settings stack. **/
+  SimpleTest.waitForExplicitFinish();
+  const Cu = Components.utils;
+  Cu.import("resource://gre/modules/Promise.jsm");
+  var iwin = window[0];
+
+  // Smoketest.
+  is(Cu.getIncumbentGlobal(), window, "smoketest");
+
+  // Calling a cross-compartment non-scripted function changes the
+  // compartment, but not the incumbent script settings object.
+  var sb = new Cu.Sandbox(window, { wantComponents: true });
+  is(sb.Components.utils.getIncumbentGlobal(), window, "cross-compartment sb non-scripted");
+  is(iwin.Components.utils.getIncumbentGlobal(), window, "cross-compartment win non-scripted");
+
+  // If we call a scripted function in the other compartment, that becomes
+  // the incumbent script.
+  function gib() { return Components.utils.getIncumbentGlobal(); };
+  Cu.evalInSandbox(gib.toSource(), sb);
+  iwin.eval(gib.toSource());
+  is(sb.gib(), sb, "cross-compartment sb scripted");
+  is(iwin.gib(), iwin, "cross-compartment win scripted");
+
+  // Eval-ing top-level script in another component makes that compartment the
+  // incumbent script.
+  is(Cu.evalInSandbox('Components.utils.getIncumbentGlobal()', sb), sb, 'eval sb');
+  is(iwin.eval('Components.utils.getIncumbentGlobal()'), iwin, 'eval iwin');
+
+  // Make sure that the callback mechanism works.
+  function makeCallback(expectedGlobal, deferred, kind) {
+    function cb(incumbentGlobal) {
+      is(incumbentGlobal, expectedGlobal, "Callback got the right incumbent global: " + kind);
+      if (deferred)
+        deferred.resolve();
+    }
+    info("Generated callback: " + kind);
+    return cb;
+  }
+
+  var bound = Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, undefined, "simple bound"));
+  is(bound(), window, "Bound method returns the right global");
+
+  // Callbacks grab the incumbent script at the time of invocation.
+  //
+  // Note - We avoid calling the initial defer |d| so that it's not in-scope for everything below.
+  let initialDefer = Promise.defer();
+  setTimeout(Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, initialDefer, "same-global setTimeout")), 0);
+  initialDefer.promise.then(function() {
+
+    // Try cross-global setTimeout where |window| is the incumbent script when the callback is created.
+    let d = Promise.defer();
+    iwin.setTimeout(Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, d, "cross-global setTimeout by |window|")), 0);
+    return d.promise;
+  }).then(function() {
+
+    // Try cross-global setTimeout where |iwin| is the incumbent script when the callback is created.
+    let d = Promise.defer();
+    iwin.wrappedJSObject.timeoutFun = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global setTimeout by |iwin|"));
+    iwin.eval('setTimeout(timeoutFun, 0);');
+    return d.promise;
+  }).then(function() {
+
+    // The incumbent script override doesn't take effect if the callback is scripted.
+    let d = Promise.defer();
+    iwin.wrappedJSObject.timeoutFun2 = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global setTimeout of scripted function"));
+    iwin.eval('let timeoutFun2Wrapper = function() { timeoutFun2(); }');
+    setTimeout(iwin.wrappedJSObject.timeoutFun2Wrapper, 0);
+    return d.promise;
+  }).then(function() {
+
+    // Try event listeners.
+    let d = Promise.defer();
+    let body = iwin.document.body;
+    body.addEventListener('click', Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, d, "cross-global event listener")));
+    body.dispatchEvent(new iwin.MouseEvent('click'));
+    return d.promise;
+
+  }).then(function() {
+
+    // Try an event handler set by |iwin|.
+    let d = Promise.defer();
+    let body = iwin.document.body;
+    iwin.wrappedJSObject.handler = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global event handler"));
+    iwin.eval('document.body.onmousemove = handler');
+    body.dispatchEvent(new iwin.MouseEvent('mousemove'));
+    return d.promise;
+
+  }).then(function() {
+
+    // Try an event handler compiled by a content attribute.
+    let d = Promise.defer();
+    let body = iwin.document.body;
+    iwin.wrappedJSObject.innerHandler = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global compiled event handler"));
+    iwin.eval("document.body.setAttribute('onmouseout', 'innerHandler()')");
+    body.dispatchEvent(new iwin.MouseEvent('mouseout'));
+    return d.promise;
+  }).then(function() {
+
+    SimpleTest.finish();
+  });
+
+  ]]>
+  </script>
+</window>