Bug 1641335, remove framescripts and message manager usage from browser_tabfocus.js test, r=Gijs
authorNeil Deakin <neil@mozilla.com>
Thu, 04 Jun 2020 11:44:12 +0000
changeset 533957 6acc469d5e4b09c8ca406b19b9bd6a1fecf45e38
parent 533956 1c15cc5d2280234f56c69822f19fc1db58a14ce3
child 533958 5aaac8ead1da567b26121de4715adfa26f7dffa9
push id37480
push userncsoregi@mozilla.com
push dateThu, 04 Jun 2020 22:00:12 +0000
treeherdermozilla-central@e33aea19d0c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1641335
milestone79.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 1641335, remove framescripts and message manager usage from browser_tabfocus.js test, r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D77142
browser/base/content/test/general/browser_tabfocus.js
--- a/browser/base/content/test/general/browser_tabfocus.js
+++ b/browser/base/content/test/general/browser_tabfocus.js
@@ -16,19 +16,21 @@ const fm = Services.focus;
 function EventStore() {
   this["main-window"] = [];
   this.window1 = [];
   this.window2 = [];
 }
 
 EventStore.prototype = {
   push(event) {
-    if (event.indexOf("1") > -1) {
+    if (event.includes("browser1") || event.includes("browser2")) {
+      this["main-window"].push(event);
+    } else if (event.includes("1")) {
       this.window1.push(event);
-    } else if (event.indexOf("2") > -1) {
+    } else if (event.includes("2")) {
       this.window2.push(event);
     } else {
       this["main-window"].push(event);
     }
   },
 };
 
 var tab1 = null;
@@ -41,155 +43,154 @@ var actualEvents = new EventStore();
 var expectedEvents = new EventStore();
 var currentTestName = "";
 var _expectedElement = null;
 var _expectedWindow = null;
 
 var currentPromiseResolver = null;
 
 function getFocusedElementForBrowser(browser, dontCheckExtraFocus = false) {
-  if (gMultiProcessBrowser) {
-    return new Promise((resolve, reject) => {
-      window.messageManager.addMessageListener(
-        "Browser:GetCurrentFocus",
-        function getCurrentFocus(message) {
-          window.messageManager.removeMessageListener(
-            "Browser:GetCurrentFocus",
-            getCurrentFocus
-          );
-          resolve(message.data.details);
-        }
+  return SpecialPowers.spawn(
+    browser,
+    [dontCheckExtraFocus],
+    dontCheckExtraFocusChild => {
+      const { Services } = ChromeUtils.import(
+        "resource://gre/modules/Services.jsm"
       );
 
-      // The dontCheckExtraFocus flag is used to indicate not to check some
-      // additional focus related properties. This is needed as both URLs are
-      // loaded using the same child process and share focus managers.
-      browser.messageManager.sendAsyncMessage("Browser:GetFocusedElement", {
-        dontCheckExtraFocus,
-      });
-    });
-  }
-  var focusedWindow = {};
-  var node = fm.getFocusedElementForWindow(
-    browser.contentWindow,
-    false,
-    focusedWindow
+      let focusedWindow = {};
+      let node = Services.focus.getFocusedElementForWindow(
+        content,
+        false,
+        focusedWindow
+      );
+      let details = "Focus is " + (node ? node.id : "<none>");
+
+      /* Check focus manager properties. Add an error onto the string if they are
+       not what is expected which will cause matching to fail in the parent process. */
+      let doc = content.document;
+      if (!dontCheckExtraFocusChild) {
+        if (Services.focus.focusedElement != node) {
+          details += "<ERROR: focusedElement doesn't match>";
+        }
+        if (
+          Services.focus.focusedWindow &&
+          Services.focus.focusedWindow != content
+        ) {
+          details += "<ERROR: focusedWindow doesn't match>";
+        }
+        if ((Services.focus.focusedWindow == content) != doc.hasFocus()) {
+          details += "<ERROR: child hasFocus() is not correct>";
+        }
+        if (
+          (Services.focus.focusedElement &&
+            doc.activeElement != Services.focus.focusedElement) ||
+          (!Services.focus.focusedElement && doc.activeElement != doc.body)
+        ) {
+          details += "<ERROR: child activeElement is not correct>";
+        }
+      }
+      return details;
+    }
   );
-  return "Focus is " + (node ? node.id : "<none>");
 }
 
-function focusInChild() {
+function focusInChild(event) {
   function getWindowDocId(target) {
     return String(target.location).includes("1") ? "window1" : "window2";
   }
 
-  function eventListener(event) {
-    // Stop the shim code from seeing this event process.
-    event.stopImmediatePropagation();
+  // Stop the shim code from seeing this event process.
+  event.stopImmediatePropagation();
 
-    var id;
-    if (event.target instanceof Ci.nsIDOMWindow) {
-      id = getWindowDocId(event.originalTarget) + "-window";
-    } else if (event.target.nodeType == event.target.DOCUMENT_NODE) {
-      id = getWindowDocId(event.originalTarget) + "-document";
-    } else {
-      id = event.originalTarget.id;
-    }
-    sendSyncMessage("Browser:FocusChanged", {
-      details: event.type + ": " + id,
-    });
+  var id;
+  if (event.target instanceof Ci.nsIDOMWindow) {
+    id = getWindowDocId(event.originalTarget) + "-window";
+  } else if (event.target.nodeType == event.target.DOCUMENT_NODE) {
+    id = getWindowDocId(event.originalTarget) + "-document";
+  } else {
+    id = event.originalTarget.id;
   }
 
-  addEventListener("focus", eventListener, true);
-  addEventListener("blur", eventListener, true);
-
-  addMessageListener("Browser:ChangeFocus", function changeFocus(message) {
-    content.document.getElementById(message.data.id)[message.data.type]();
-  });
-
-  addMessageListener("Browser:GetFocusedElement", function getFocusedElement(
-    message
-  ) {
-    var focusedWindow = {};
-    var node = Services.focus.getFocusedElementForWindow(
-      content,
-      false,
-      focusedWindow
-    );
-    var details = "Focus is " + (node ? node.id : "<none>");
-
-    /* Check focus manager properties. Add an error onto the string if they are
-       not what is expected which will cause matching to fail in the parent process. */
-    let doc = content.document;
-    if (!message.data.dontCheckExtraFocus) {
-      if (Services.focus.focusedElement != node) {
-        details += "<ERROR: focusedElement doesn't match>";
-      }
-      if (
-        Services.focus.focusedWindow &&
-        Services.focus.focusedWindow != content
-      ) {
-        details += "<ERROR: focusedWindow doesn't match>";
-      }
-      if ((Services.focus.focusedWindow == content) != doc.hasFocus()) {
-        details += "<ERROR: child hasFocus() is not correct>";
-      }
-      if (
-        (Services.focus.focusedElement &&
-          doc.activeElement != Services.focus.focusedElement) ||
-        (!Services.focus.focusedElement && doc.activeElement != doc.body)
-      ) {
-        details += "<ERROR: child activeElement is not correct>";
-      }
-    }
-
-    sendSyncMessage("Browser:GetCurrentFocus", { details });
-  });
+  let window = event.target.ownerGlobal;
+  if (!window._eventsOccurred) {
+    window._eventsOccurred = [];
+  }
+  window._eventsOccurred.push(event.type + ": " + id);
+  return true;
 }
 
-function focusElementInChild(elementid, type) {
+function focusElementInChild(elementid, elementtype) {
   let browser = elementid.includes("1") ? browser1 : browser2;
-  if (gMultiProcessBrowser) {
-    browser.messageManager.sendAsyncMessage("Browser:ChangeFocus", {
-      id: elementid,
-      type,
-    });
-  } else {
-    browser.contentDocument.getElementById(elementid)[type]();
-  }
+  return SpecialPowers.spawn(browser, [elementid, elementtype], (id, type) => {
+    content.document.getElementById(id)[type]();
+  });
 }
 
 add_task(async function() {
   tab1 = BrowserTestUtils.addTab(gBrowser);
   browser1 = gBrowser.getBrowserForTab(tab1);
 
   tab2 = BrowserTestUtils.addTab(gBrowser);
   browser2 = gBrowser.getBrowserForTab(tab2);
 
   await promiseTabLoadEvent(tab1, "data:text/html," + escape(testPage1));
   await promiseTabLoadEvent(tab2, "data:text/html," + escape(testPage2));
 
-  if (gMultiProcessBrowser) {
-    var childFocusScript = "data:,(" + escape(focusInChild.toString()) + ")();";
-    browser1.messageManager.loadFrameScript(childFocusScript, true);
-    browser2.messageManager.loadFrameScript(childFocusScript, true);
-  }
-
   gURLBar.focus();
   await SimpleTest.promiseFocus();
 
-  if (gMultiProcessBrowser) {
-    window.messageManager.addMessageListener(
-      "Browser:FocusChanged",
-      message => {
-        actualEvents.push(message.data.details);
-        compareFocusResults();
-      }
-    );
-  }
+  // In these listeners, focusInChild is used to cache details about the event
+  // on a temporary on the window (window._eventsOccurred), so that it can be
+  // retrieved later within compareFocusResults. focusInChild always returns true.
+  // compareFocusResults is called each time event occurs to check that the
+  // right events happened.
+  let listenersToRemove = [];
+  listenersToRemove.push(
+    BrowserTestUtils.addContentEventListener(
+      browser1,
+      "focus",
+      compareFocusResults,
+      { capture: true },
+      focusInChild
+    )
+  );
+  listenersToRemove.push(
+    BrowserTestUtils.addContentEventListener(
+      browser1,
+      "blur",
+      compareFocusResults,
+      { capture: true },
+      focusInChild
+    )
+  );
+  listenersToRemove.push(
+    BrowserTestUtils.addContentEventListener(
+      browser2,
+      "focus",
+      compareFocusResults,
+      { capture: true },
+      focusInChild
+    )
+  );
+  listenersToRemove.push(
+    BrowserTestUtils.addContentEventListener(
+      browser2,
+      "blur",
+      compareFocusResults,
+      { capture: true },
+      focusInChild
+    )
+  );
+
+  // Get the content processes to do something, so that we can better
+  // ensure that the listeners added above will have actually been added
+  // in the tabs.
+  await SpecialPowers.spawn(browser1, [], () => {});
+  await SpecialPowers.spawn(browser2, [], () => {});
 
   _lastfocus = "urlbar";
   _lastfocuswindow = "main-window";
 
   window.addEventListener("focus", _browser_tabfocus_test_eventOccured, true);
   window.addEventListener("blur", _browser_tabfocus_test_eventOccured, true);
 
   // make sure that the focus initially starts out blank
@@ -322,56 +323,51 @@ add_task(async function() {
   await expectFocusShiftAfterTabSwitch(
     tab1,
     "main-window",
     "tab1",
     true,
     "tab change when selected tab element was focused"
   );
 
-  let switchWaiter;
-  if (gMultiProcessBrowser) {
-    switchWaiter = new Promise((resolve, reject) => {
-      gBrowser.addEventListener(
-        "TabSwitchDone",
-        function() {
-          executeSoon(resolve);
-        },
-        { once: true }
-      );
-    });
-  }
+  let switchWaiter = new Promise((resolve, reject) => {
+    gBrowser.addEventListener(
+      "TabSwitchDone",
+      function() {
+        executeSoon(resolve);
+      },
+      { once: true }
+    );
+  });
 
   await expectFocusShiftAfterTabSwitch(
     tab2,
     "main-window",
     "tab2",
     true,
     "another tab change when selected tab element was focused"
   );
 
-  // When this a remote browser, wait for the paint on the second browser so that
-  // any post tab-switching stuff has time to complete before blurring the tab.
-  // Otherwise, the _adjustFocusAfterTabSwitch in tabbrowser gets confused and
-  // isn't sure what tab is really focused.
-  if (gMultiProcessBrowser) {
-    await switchWaiter;
-  }
+  // Wait for the paint on the second browser so that any post tab-switching
+  // stuff has time to complete before blurring the tab. Otherwise, the
+  // _adjustFocusAfterTabSwitch in tabbrowser gets confused and isn't sure
+  // what tab is really focused.
+  await switchWaiter;
 
   await expectFocusShift(
     () => gBrowser.selectedTab.blur(),
     "main-window",
     null,
     true,
     "blurring tab element"
   );
 
   // focusing the url field should switch active focus away from the browser but
   // not clear what would be the focus in the browser
-  focusElementInChild("button1", "focus");
+  await focusElementInChild("button1", "focus");
 
   await expectFocusShift(
     () => gURLBar.focus(),
     "main-window",
     "urlbar",
     true,
     "focusedWindow after url field focused"
   );
@@ -536,45 +532,18 @@ add_task(async function() {
   });
 
   is(
     window.document.activeElement,
     gURLBar.inputField,
     "urlbar still focused after navigating back"
   );
 
-  // Document navigation with F6 does not yet work in mutli-process browsers.
-  if (!gMultiProcessBrowser) {
-    gURLBar.focus();
-    actualEvents = new EventStore();
-    _lastfocus = "urlbar";
-    _lastfocuswindow = "main-window";
-
-    await expectFocusShift(
-      () => EventUtils.synthesizeKey("KEY_F6"),
-      "window1",
-      "html1",
-      true,
-      "switch document forward with f6"
-    );
-
-    EventUtils.synthesizeKey("KEY_F6");
-    is(fm.focusedWindow, window, "switch document forward again with f6");
-
-    browser1.style.MozUserFocus = "ignore";
-    browser1.clientWidth;
-    EventUtils.synthesizeKey("KEY_F6");
-    is(
-      fm.focusedWindow,
-      window,
-      "switch document forward again with f6 when browser non-focusable"
-    );
-
-    browser1.style.MozUserFocus = "normal";
-    browser1.clientWidth;
+  for (let listener of listenersToRemove) {
+    listener();
   }
 
   window.removeEventListener(
     "focus",
     _browser_tabfocus_test_eventOccured,
     true
   );
   window.removeEventListener("blur", _browser_tabfocus_test_eventOccured, true);
@@ -636,17 +605,43 @@ function getId(element) {
 
   if (element.localName == "tab") {
     return element == tab1 ? "tab1" : "tab2";
   }
 
   return element.localName == "input" ? "urlbar" : element.id;
 }
 
-function compareFocusResults() {
+async function compareFocusResults() {
+  if (!currentPromiseResolver) {
+    return;
+  }
+
+  // Get the events that occurred in each child browser and store them
+  // in 'actualEvents'. This is a global so if different calls to
+  // compareFocusResults occur together, whichever one happens to get
+  // called first after pulling all the events from the child will
+  // perform the matching.
+  let events = await SpecialPowers.spawn(browser1, [], () => {
+    let eventsOccurred = content._eventsOccurred;
+    content._eventsOccurred = [];
+    return eventsOccurred || [];
+  });
+  actualEvents.window1.push(...events);
+
+  events = await SpecialPowers.spawn(browser2, [], () => {
+    let eventsOccurred = content._eventsOccurred;
+    content._eventsOccurred = [];
+    return eventsOccurred || [];
+  });
+  actualEvents.window2.push(...events);
+
+  // Another call to compareFocusResults may have happened in the meantime.
+  // If currentPromiseResolver is null, then that call was successful so no
+  // need to check the events again.
   if (!currentPromiseResolver) {
     return;
   }
 
   let winIds = ["main-window", "window1", "window2"];
 
   for (let winId of winIds) {
     if (actualEvents[winId].length < expectedEvents[winId].length) {
@@ -660,65 +655,56 @@ function compareFocusResults() {
         actualEvents[winId][e],
         expectedEvents[winId][e],
         currentTestName + " events [event " + e + "]"
       );
     }
     actualEvents[winId] = [];
   }
 
-  // Use executeSoon as this will be called during a focus/blur event handler
-  executeSoon(() => {
-    let matchWindow = window;
-    if (gMultiProcessBrowser) {
-      is(_expectedWindow, "main-window", "main-window is always expected");
-    } else if (_expectedWindow != "main-window") {
-      matchWindow =
-        _expectedWindow == "window1"
-          ? browser1.contentWindow
-          : browser2.contentWindow;
-    }
-    if (_expectedWindow == "main-window") {
-      // The browser window's body doesn't have an id set usually - set one now
-      // so it can be used for id comparisons below.
-      matchWindow.document.body.id = "main-window-body";
-    }
+  let matchWindow = window;
+  is(_expectedWindow, "main-window", "main-window is always expected");
+  if (_expectedWindow == "main-window") {
+    // The browser window's body doesn't have an id set usually - set one now
+    // so it can be used for id comparisons below.
+    matchWindow.document.body.id = "main-window-body";
+  }
+
+  var focusedElement = fm.focusedElement;
+  is(
+    getId(focusedElement),
+    _expectedElement,
+    currentTestName + " focusedElement"
+  );
 
-    var focusedElement = fm.focusedElement;
-    is(
-      getId(focusedElement),
-      _expectedElement,
-      currentTestName + " focusedElement"
-    );
-    is(fm.focusedWindow, matchWindow, currentTestName + " focusedWindow");
-    var focusedWindow = {};
-    is(
-      getId(fm.getFocusedElementForWindow(matchWindow, false, focusedWindow)),
-      _expectedElement,
-      currentTestName + " getFocusedElementForWindow"
-    );
-    is(
-      focusedWindow.value,
-      matchWindow,
-      currentTestName + " getFocusedElementForWindow frame"
-    );
-    is(matchWindow.document.hasFocus(), true, currentTestName + " hasFocus");
-    var expectedActive = _expectedElement;
-    if (!expectedActive) {
-      expectedActive = getId(matchWindow.document.body);
-    }
-    is(
-      getId(matchWindow.document.activeElement),
-      expectedActive,
-      currentTestName + " activeElement"
-    );
+  is(fm.focusedWindow, matchWindow, currentTestName + " focusedWindow");
+  var focusedWindow = {};
+  is(
+    getId(fm.getFocusedElementForWindow(matchWindow, false, focusedWindow)),
+    _expectedElement,
+    currentTestName + " getFocusedElementForWindow"
+  );
+  is(
+    focusedWindow.value,
+    matchWindow,
+    currentTestName + " getFocusedElementForWindow frame"
+  );
+  is(matchWindow.document.hasFocus(), true, currentTestName + " hasFocus");
+  var expectedActive = _expectedElement;
+  if (!expectedActive) {
+    expectedActive = getId(matchWindow.document.body);
+  }
+  is(
+    getId(matchWindow.document.activeElement),
+    expectedActive,
+    currentTestName + " activeElement"
+  );
 
-    currentPromiseResolver();
-    currentPromiseResolver = null;
-  });
+  currentPromiseResolver();
+  currentPromiseResolver = null;
 }
 
 async function expectFocusShiftAfterTabSwitch(
   tab,
   expectedWindow,
   expectedElement,
   focusChanged,
   testid
@@ -731,17 +717,17 @@ async function expectFocusShiftAfterTabS
     expectedWindow,
     expectedElement,
     focusChanged,
     testid
   );
   await tabSwitchPromise;
 }
 
-function expectFocusShift(
+async function expectFocusShift(
   callback,
   expectedWindow,
   expectedElement,
   focusChanged,
   testid
 ) {
   currentPromiseResolver = null;
   currentTestName = testid;
@@ -749,86 +735,83 @@ function expectFocusShift(
   expectedEvents = new EventStore();
 
   if (focusChanged) {
     _expectedElement = expectedElement;
     _expectedWindow = expectedWindow;
 
     // When the content is in a child process, the expected element in the chrome window
     // will always be the urlbar or a browser element.
-    if (gMultiProcessBrowser) {
-      if (_expectedWindow == "window1") {
-        _expectedElement = "browser1";
-      } else if (_expectedWindow == "window2") {
-        _expectedElement = "browser2";
-      }
-      _expectedWindow = "main-window";
+    if (_expectedWindow == "window1") {
+      _expectedElement = "browser1";
+    } else if (_expectedWindow == "window2") {
+      _expectedElement = "browser2";
     }
+    _expectedWindow = "main-window";
 
     if (
-      gMultiProcessBrowser &&
       _lastfocuswindow != "main-window" &&
       _lastfocuswindow != expectedWindow
     ) {
       let browserid = _lastfocuswindow == "window1" ? "browser1" : "browser2";
       expectedEvents.push("blur: " + browserid);
     }
 
     var newElementIsFocused =
       expectedElement && !expectedElement.startsWith("html");
     if (
       newElementIsFocused &&
-      gMultiProcessBrowser &&
       _lastfocuswindow != "main-window" &&
       expectedWindow == "main-window"
     ) {
       // When switching from a child to a chrome element, the focus on the element will arrive first.
       expectedEvents.push("focus: " + expectedElement);
       newElementIsFocused = false;
     }
 
     if (_lastfocus && _lastfocus != _expectedElement) {
       expectedEvents.push("blur: " + _lastfocus);
     }
 
     if (_lastfocuswindow && _lastfocuswindow != expectedWindow) {
-      if (!gMultiProcessBrowser || _lastfocuswindow != "main-window") {
+      if (_lastfocuswindow != "main-window") {
         expectedEvents.push("blur: " + _lastfocuswindow + "-document");
         expectedEvents.push("blur: " + _lastfocuswindow + "-window");
       }
     }
 
     if (expectedWindow && _lastfocuswindow != expectedWindow) {
-      if (gMultiProcessBrowser && expectedWindow != "main-window") {
+      if (expectedWindow != "main-window") {
         let browserid = expectedWindow == "window1" ? "browser1" : "browser2";
         expectedEvents.push("focus: " + browserid);
       }
 
-      if (!gMultiProcessBrowser || expectedWindow != "main-window") {
+      if (expectedWindow != "main-window") {
         expectedEvents.push("focus: " + expectedWindow + "-document");
         expectedEvents.push("focus: " + expectedWindow + "-window");
       }
     }
 
     if (newElementIsFocused) {
       expectedEvents.push("focus: " + expectedElement);
     }
 
     _lastfocus = expectedElement;
     _lastfocuswindow = expectedWindow;
   }
 
-  return new Promise((resolve, reject) => {
+  // No events are expected, so return immediately. If events do occur, the following
+  // tests will fail.
+  if (
+    expectedEvents["main-window"].length +
+      expectedEvents.window1.length +
+      expectedEvents.window2.length ==
+    0
+  ) {
+    await callback();
+    return undefined;
+  }
+
+  return new Promise(resolve => {
     currentPromiseResolver = resolve;
     callback();
-
-    // No events are expected, so resolve the promise immediately.
-    if (
-      expectedEvents["main-window"].length +
-        expectedEvents.window1.length +
-        expectedEvents.window2.length ==
-      0
-    ) {
-      currentPromiseResolver();
-      currentPromiseResolver = null;
-    }
   });
 }