Bug 1559244 - Step 1: make BrowserTestUtils@crashBrowser function work with JSWindowActor. r=mconley
☠☠ backed out by fa80b89e0ce4 ☠ ☠
authorAbdoulaye O. Ly <ablayelyfondou@gmail.com>
Thu, 15 Aug 2019 17:01:45 +0000
changeset 488316 dabda4e90259abe8a2fc766549a0dfd77beb6c31
parent 488315 140cb3a190e91200a67ac8fcfeebc5194d5cfa81
child 488317 dced8cea7b23c28a0e2b66b51a4b022fdec2c1d1
push id36440
push userncsoregi@mozilla.com
push dateFri, 16 Aug 2019 03:57:48 +0000
treeherdermozilla-central@a58b7dc85887 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1559244
milestone70.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 1559244 - Step 1: make BrowserTestUtils@crashBrowser function work with JSWindowActor. r=mconley Differential Revision: https://phabricator.services.mozilla.com/D37318
dom/base/ChromeUtils.cpp
dom/base/ChromeUtils.h
dom/chrome-webidl/ChromeUtils.webidl
testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
testing/mochitest/BrowserTestUtils/BrowserTestUtilsChild.jsm
testing/mochitest/BrowserTestUtils/moz.build
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -9,16 +9,17 @@
 #include "js/CharacterEncoding.h"
 #include "js/SavedFrameAPI.h"
 #include "jsfriendapi.h"
 #include "WrapperFactory.h"
 
 #include "mozilla/Base64.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/IntentionalCrash.h"
 #include "mozilla/PerformanceMetricsCollector.h"
 #include "mozilla/PerfStats.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcInfo.h"
 #include "mozilla/RDDProcessManager.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/BrowsingContext.h"
@@ -1113,10 +1114,20 @@ void ChromeUtils::UnregisterWindowActor(
 
 /* static */
 bool ChromeUtils::IsClassifierBlockingErrorCode(GlobalObject& aGlobal,
                                                 uint32_t aError) {
   return net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
       static_cast<nsresult>(aError));
 }
 
+/* static */
+void ChromeUtils::PrivateNoteIntentionalCrash(const GlobalObject& aGlobal,
+                                              ErrorResult& aError) {
+  if (XRE_IsContentProcess()) {
+    NoteIntentionalCrash("tab");
+    return;
+  }
+  aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -187,14 +187,17 @@ class ChromeUtils {
                                   const WindowActorOptions& aOptions,
                                   ErrorResult& aRv);
 
   static void UnregisterWindowActor(const GlobalObject& aGlobal,
                                     const nsAString& aName);
 
   static bool IsClassifierBlockingErrorCode(GlobalObject& aGlobal,
                                             uint32_t aError);
+
+  static void PrivateNoteIntentionalCrash(const GlobalObject& aGlobal,
+                                          ErrorResult& aError);
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_ChromeUtils__
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -422,16 +422,24 @@ partial namespace ChromeUtils {
   void registerWindowActor(DOMString aName, optional WindowActorOptions aOptions = {});
 
   [ChromeOnly]
   void unregisterWindowActor(DOMString aName);
 
   [ChromeOnly]
   // aError should a nsresult.
   boolean isClassifierBlockingErrorCode(unsigned long aError);
+
+  /**
+   * If leak detection is enabled, print a note to the leak log that this
+   * process will intentionally crash. This should be called only on child
+   * processes for testing purpose.
+   */
+  [ChromeOnly, Throws]
+  void privateNoteIntentionalCrash();
 };
 
 /**
  * Holds information about Firefox running processes & threads.
  *
  * See widget/ProcInfo.h for fields documentation.
  */
 enum ProcType {
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -83,16 +83,33 @@ Cu.permitCPOWsInScope(this);
 var gSendCharCount = 0;
 var gSynthesizeKeyCount = 0;
 var gSynthesizeCompositionCount = 0;
 var gSynthesizeCompositionChangeCount = 0;
 
 const kAboutPageRegistrationContentScript =
   "chrome://mochikit/content/tests/BrowserTestUtils/content-about-page-utils.js";
 
+/**
+ * Create and register BrowserTestUtils Window Actor.
+ */
+function registerActor() {
+  let actorOptions = {
+    child: {
+      moduleURI: "resource://testing-common/BrowserTestUtilsChild.jsm",
+    },
+
+    allFrames: true,
+    includeChrome: true,
+  };
+  ChromeUtils.registerWindowActor("BrowserTestUtils", actorOptions);
+}
+
+registerActor();
+
 var BrowserTestUtils = {
   /**
    * Loads a page in a new tab, executes a Task and closes the tab.
    *
    * @param options
    *        An object  or string.
    *        If this is a string it is the url to open and will be opened in the
    *        currently active browser window.
@@ -1655,36 +1672,16 @@ var BrowserTestUtils = {
     function removeFile(directory, filename) {
       let file = directory.clone();
       file.append(filename);
       if (file.exists()) {
         file.remove(false);
       }
     }
 
-    // This frame script is injected into the remote browser, and used to
-    // intentionally crash the tab. We crash by using js-ctypes and dereferencing
-    // a bad pointer. The crash should happen immediately upon loading this
-    // frame script.
-    let frame_script = () => {
-      const { ctypes } = ChromeUtils.import(
-        "resource://gre/modules/ctypes.jsm"
-      );
-
-      let dies = function() {
-        privateNoteIntentionalCrash();
-        let zero = new ctypes.intptr_t(8);
-        let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-        badptr.contents;
-      };
-
-      dump("\nEt tu, Brute?\n");
-      dies();
-    };
-
     let expectedPromises = [];
 
     let crashCleanupPromise = new Promise((resolve, reject) => {
       let observer = (subject, topic, data) => {
         if (topic != "ipc:content-shutdown") {
           reject("Received incorrect observer topic: " + topic);
           return;
         }
@@ -1766,20 +1763,22 @@ var BrowserTestUtils = {
             },
             false,
             true
           );
         })
       );
     }
 
-    // This frame script will crash the remote browser as soon as it is
-    // evaluated.
-    let mm = browser.messageManager;
-    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
+    // Trigger crash by sending a message to BrowserTestUtils actor.
+    this.sendAsyncMessage(
+      browser.browsingContext,
+      "BrowserTestUtils:CrashFrame",
+      {}
+    );
 
     await Promise.all(expectedPromises);
 
     if (shouldShowTabCrashPage) {
       let gBrowser = browser.ownerGlobal.gBrowser;
       let tab = gBrowser.getTabForBrowser(browser);
       if (tab.getAttribute("crashed") != "true") {
         throw new Error("Tab should be marked as crashed");
@@ -2202,9 +2201,29 @@ var BrowserTestUtils = {
         function(e) {
           beforeLoadFunc(e.target);
         },
         { once: true }
       );
     }
     return tabbrowser.addTab(uri, params);
   },
+
+  /**
+   * Sends a message to a specific BrowserTestUtils window actor.
+   * @param aBrowsingContext
+   *        The browsing context where the actor lives.
+   * @param {string} aMessageName
+   *        Name of the message to be sent to the actor.
+   * @param {object} aMessageData
+   *        Extra information to pass to the actor.
+   */
+  async sendAsyncMessage(aBrowsingContext, aMessageName, aMessageData) {
+    if (!aBrowsingContext.currentWindowGlobal) {
+      await this.waitForCondition(() => aBrowsingContext.currentWindowGlobal);
+    }
+
+    let actor = aBrowsingContext.currentWindowGlobal.getActor(
+      "BrowserTestUtils"
+    );
+    actor.sendAsyncMessage(aMessageName, aMessageData);
+  },
 };
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtilsChild.jsm
@@ -0,0 +1,34 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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/. */
+"use strict";
+
+var EXPORTED_SYMBOLS = ["BrowserTestUtilsChild"];
+
+class BrowserTestUtilsChild extends JSWindowActorChild {
+  receiveMessage(aMessage) {
+    switch (aMessage.name) {
+      case "BrowserTestUtils:CrashFrame": {
+        // This is to intentionally crash the frame.
+        // We crash by using js-ctypes and dereferencing
+        // a bad pointer. The crash should happen immediately
+        // upon loading this frame script.
+
+        const { ctypes } = ChromeUtils.import(
+          "resource://gre/modules/ctypes.jsm"
+        );
+
+        let dies = function() {
+          ChromeUtils.privateNoteIntentionalCrash();
+          let zero = new ctypes.intptr_t(8);
+          let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+          badptr.contents;
+        };
+
+        dump("\nEt tu, Brute?\n");
+        dies();
+      }
+    }
+  }
+}
--- a/testing/mochitest/BrowserTestUtils/moz.build
+++ b/testing/mochitest/BrowserTestUtils/moz.build
@@ -1,12 +1,13 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 TESTING_JS_MODULES += [
     'BrowserTestUtils.jsm',
+    'BrowserTestUtilsChild.jsm',
     'content/content-task.js',
     'ContentTask.jsm',
     'ContentTaskUtils.jsm',
 ]