--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -732,16 +732,25 @@ decoder.noCodecs.message = To play video, you may need to install Microsoft’s Media Feature Pack.
decoder.noCodecsVista.message = To play video, you may need to install Microsoft’s Platform Update Supplement for Windows Vista.
decoder.noCodecsXP.message = To play video, you may need to enable Adobe’s Primetime Content Decryption Module.
decoder.noCodecsLinux.message = To play video, you may need to install the required video codecs.
decoder.noHWAcceleration.message = To improve video quality, you may need to install Microsoft’s Media Feature Pack.
decoder.noHWAccelerationVista.message = To improve video quality, you may need to install Microsoft’s Platform Update Supplement for Windows Vista.
decoder.noPulseAudio.message = To play audio, you may need to install the required PulseAudio software.
decoder.unsupportedLibavcodec.message = libavcodec may be vulnerable or is not supported, and should be updated to play video.
+# LOCALIZATION NOTE (captivePortal.infoMessage):
+# This string is shown in a notification bar when we detect a captive portal is blocking network access
+# and requires the user to log in before browsing. %1$S is replaced with brandShortName.
+captivePortal.infoMessage=This network may require you to login to use the internet. %1$S has opened the login page for you.
+# LOCALIZATION NOTE (captivePortal.showLoginPage):
+# The label for a button shown in the info bar in all tabs except the login page tab.
+# The button shows the portal login page tab when clicked.
+captivePortal.showLoginPage=Show Login Page
+
permissions.remove.tooltip = Clear this permission and ask again
# LOCALIZATION NOTE (aboutDialog.architecture.*):
# The sixtyFourBit and thirtyTwoBit strings describe the architecture of the
# current Firefox build: 32-bit or 64-bit. These strings are used in parentheses
# between the Firefox version and the "What's new" link in the About dialog,
# e.g.: "48.0.2 (32-bit) <What's new>" or "51.0a1 (2016-09-05) (64-bit)".
aboutDialog.architecture.sixtyFourBit = 64-bit
--- a/browser/modules/CaptivePortalWatcher.jsm
+++ b/browser/modules/CaptivePortalWatcher.jsm
@@ -7,32 +7,38 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
/**
* This constant is chosen to be large enough for a portal recheck to complete,
* and small enough that the delay in opening a tab isn't too noticeable.
* Please see comments for _delayedAddCaptivePortalTab for more details.
*/
const PORTAL_RECHECK_DELAY_MS = 150;
+// This is the value used to identify the captive portal notification.
+const PORTAL_NOTIFICATION_VALUE = "captive-portal-detected";
+
this.EXPORTED_SYMBOLS = [ "CaptivePortalWatcher" ];
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cps",
"@mozilla.org/network/captive-portal-service;1",
"nsICaptivePortalService");
this.CaptivePortalWatcher = {
// This holds a weak reference to the captive portal tab so that we
// don't leak it if the user closes it.
_captivePortalTab: null,
+ // This holds a weak reference to the captive portal notification.
+ _captivePortalNotification: null,
+
_initialized: false,
/**
* If a portal is detected when we don't have focus, we first wait for focus
* and then add the tab after a small delay. This is set to true while we wait
* so that in the unlikely event that we receive another notification while
* waiting, we can avoid adding a second tab.
*/
@@ -45,17 +51,17 @@ this.CaptivePortalWatcher = {
init() {
Services.obs.addObserver(this, "captive-portal-login", false);
Services.obs.addObserver(this, "captive-portal-login-abort", false);
Services.obs.addObserver(this, "captive-portal-login-success", false);
this._initialized = true;
if (cps.state == cps.LOCKED_PORTAL) {
// A captive portal has already been detected.
- this._addCaptivePortalTab();
+ this._captivePortalDetected();
return;
}
cps.recheckCaptivePortal();
},
uninit() {
if (!this._initialized) {
@@ -64,48 +70,63 @@ this.CaptivePortalWatcher = {
Services.obs.removeObserver(this, "captive-portal-login");
Services.obs.removeObserver(this, "captive-portal-login-abort");
Services.obs.removeObserver(this, "captive-portal-login-success");
},
observe(subject, topic, data) {
switch (topic) {
case "captive-portal-login":
- this._addCaptivePortalTab();
+ this._captivePortalDetected();
break;
case "captive-portal-login-abort":
case "captive-portal-login-success":
this._captivePortalGone();
break;
case "xul-window-visible":
this._delayedAddCaptivePortalTab();
break;
}
},
- _addCaptivePortalTab() {
+ _captivePortalDetected() {
if (this._waitingToAddTab) {
return;
}
let win = RecentWindow.getMostRecentBrowserWindow();
// If there's no browser window or none have focus, open and show the
// tab when we regain focus. This is so that if a different application was
// focused, when the user (re-)focuses a browser window, we open the tab
// immediately in that window so they can login before continuing to browse.
if (!win || !win.document.hasFocus()) {
this._waitingToAddTab = true;
Services.obs.addObserver(this, "xul-window-visible", false);
return;
}
- // The browser is in use - add the tab without selecting it.
- let tab = win.gBrowser.addTab(this.canonicalURL);
+ // The browser is in use - show a notification and add the tab without
+ // selecting it, unless the caller specifically requested selection.
+ this._ensureCaptivePortalTab(win);
+ this._showNotification(win);
+ },
+
+ _ensureCaptivePortalTab(win) {
+ let tab;
+ if (this._captivePortalTab) {
+ tab = this._captivePortalTab.get();
+ }
+
+ // If the tab is gone or going, we need to open a new one.
+ if (!tab || tab.closing || !tab.parentNode) {
+ tab = win.gBrowser.addTab(this.canonicalURL);
+ }
+
this._captivePortalTab = Cu.getWeakReference(tab);
- return;
+ return tab;
},
/**
* Called after we regain focus if we detect a portal while a browser window
* doesn't have focus. Triggers a portal recheck to reaffirm state, and adds
* the tab if needed after a short delay to allow the recheck to complete.
*/
_delayedAddCaptivePortalTab() {
@@ -130,35 +151,37 @@ this.CaptivePortalWatcher = {
// - If it is, the delay is chosen to not be extremely noticeable.
setTimeout(() => {
this._waitingToAddTab = false;
if (cps.state != cps.LOCKED_PORTAL) {
// We're free of the portal!
return;
}
- let tab = win.gBrowser.addTab(this.canonicalURL);
+ this._showNotification(win);
+ let tab = this._ensureCaptivePortalTab(win);
+
// Focus the tab only if the recheck has completed, i.e. we're sure
// that the portal is still locked. This way, if the recheck completes
// after we add the tab and we're free of the portal, the tab contents
// won't flicker.
if (cps.lastChecked != lastChecked) {
win.gBrowser.selectedTab = tab;
}
-
- this._captivePortalTab = Cu.getWeakReference(tab);
}, PORTAL_RECHECK_DELAY_MS);
},
_captivePortalGone() {
if (this._waitingToAddTab) {
Services.obs.removeObserver(this, "xul-window-visible");
this._waitingToAddTab = false;
}
+ this._removeNotification();
+
if (!this._captivePortalTab) {
return;
}
let tab = this._captivePortalTab.get();
// In all the cases below, we want to stop treating the tab as a
// captive portal tab.
this._captivePortalTab = null;
@@ -176,9 +199,86 @@ this.CaptivePortalWatcher = {
if (tab.linkedBrowser.currentURI.spec != this.canonicalURL &&
tabbrowser.selectedTab == tab) {
return;
}
// Remove the tab.
tabbrowser.removeTab(tab);
},
+
+ get _productName() {
+ delete this._productName;
+ return this._productName =
+ Services.strings.createBundle("chrome://branding/locale/brand.properties")
+ .GetStringFromName("brandShortName");
+ },
+
+ get _browserBundle() {
+ delete this._browserBundle;
+ return this._browserBundle =
+ Services.strings.createBundle("chrome://browser/locale/browser.properties");
+ },
+
+ handleEvent(aEvent) {
+ if (aEvent.type != "TabSelect" || !this._captivePortalTab || !this._captivePortalNotification) {
+ return;
+ }
+
+ let tab = this._captivePortalTab.get();
+ let n = this._captivePortalNotification.get();
+ if (!tab || !n) {
+ return;
+ }
+
+ let doc = tab.ownerDocument;
+ let button = n.querySelector("button.notification-button");
+ if (doc.defaultView.gBrowser.selectedTab == tab) {
+ button.style.visibility = "hidden";
+ } else {
+ button.style.visibility = "visible";
+ }
+ },
+
+ _showNotification(win) {
+ let buttons = [
+ {
+ label: this._browserBundle.GetStringFromName("captivePortal.showLoginPage"),
+ callback: () => {
+ win.gBrowser.selectedTab = this._ensureCaptivePortalTab(win);
+
+ // Returning true prevents the notification from closing.
+ return true;
+ },
+ isDefault: true,
+ },
+ ];
+
+ let message = this._browserBundle.formatStringFromName("captivePortal.infoMessage",
+ [this._productName], 1);
+
+ let closeHandler = (aEventName) => {
+ if (aEventName != "removed") {
+ return;
+ }
+ win.gBrowser.tabContainer.removeEventListener("TabSelect", this);
+ };
+
+ let nb = win.document.getElementById("high-priority-global-notificationbox");
+ let n = nb.appendNotification(message, PORTAL_NOTIFICATION_VALUE, "",
+ nb.PRIORITY_INFO_MEDIUM, buttons, closeHandler);
+
+ this._captivePortalNotification = Cu.getWeakReference(n);
+
+ win.gBrowser.tabContainer.addEventListener("TabSelect", this);
+ },
+
+ _removeNotification() {
+ if (!this._captivePortalNotification)
+ return;
+ let n = this._captivePortalNotification.get();
+ this._captivePortalNotification = null;
+ if (!n || !n.parentNode) {
+ return;
+ }
+ n.close();
+ },
};
--- a/browser/modules/test/browser_CaptivePortalWatcher.js
+++ b/browser/modules/test/browser_CaptivePortalWatcher.js
@@ -1,15 +1,16 @@
"use strict";
Components.utils.import("resource:///modules/RecentWindow.jsm");
const CANONICAL_CONTENT = "success";
const CANONICAL_URL = "data:text/plain;charset=utf-8," + CANONICAL_CONTENT;
const CANONICAL_URL_REDIRECTED = "data:text/plain;charset=utf-8,redirected";
+const PORTAL_NOTIFICATION_VALUE = "captive-portal-detected";
add_task(function* setup() {
yield SpecialPowers.pushPrefEnv({
set: [["captivedetect.canonicalURL", CANONICAL_URL],
["captivedetect.canonicalContent", CANONICAL_CONTENT]],
});
});
@@ -20,170 +21,291 @@ add_task(function* setup() {
*/
function* portalDetectedNoBrowserWindow() {
let getMostRecentBrowserWindow = RecentWindow.getMostRecentBrowserWindow;
RecentWindow.getMostRecentBrowserWindow = () => {};
Services.obs.notifyObservers(null, "captive-portal-login", null);
RecentWindow.getMostRecentBrowserWindow = getMostRecentBrowserWindow;
}
-function* openWindowAndWaitForPortalTab() {
+function* openWindowAndWaitForPortalTabAndNotification() {
let win = yield BrowserTestUtils.openNewBrowserWindow();
- let tab = yield BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
+ let [notification, tab] = yield Promise.all([
+ BrowserTestUtils.waitForGlobalNotificationBar(win, PORTAL_NOTIFICATION_VALUE),
+ BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL)
+ ]);
is(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open and selected in the new window.");
+ testShowLoginPageButtonVisibility(notification, "hidden");
return win;
}
function freePortal(aSuccess) {
Services.obs.notifyObservers(null,
"captive-portal-login-" + (aSuccess ? "success" : "abort"), null);
}
+function ensurePortalTab(win) {
+ // For the tests that call this function, it's enough to ensure there
+ // are two tabs in the window - the default tab and the portal tab.
+ is(win.gBrowser.tabs.length, 2,
+ "There should be a captive portal tab in the window.");
+}
+
+function ensurePortalNotification(win) {
+ let notificationBox =
+ win.document.getElementById("high-priority-global-notificationbox");
+ let notification = notificationBox.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE)
+ isnot(notification, null,
+ "There should be a captive portal notification in the window.");
+ return notification;
+}
+
+// Helper to test whether the "Show Login Page" is visible in the captive portal
+// notification (it should be hidden when the portal tab is selected).
+function testShowLoginPageButtonVisibility(notification, visibility) {
+ let showLoginPageButton = notification.querySelector("button.notification-button");
+ // If the visibility property was never changed from default, it will be
+ // an empty string, so we pretend it's "visible" (effectively the same).
+ is(showLoginPageButton.style.visibility || "visible", visibility,
+ "The \"Show Login Page\" button should be " + visibility + ".");
+}
+
+function ensureNoPortalTab(win) {
+ is(win.gBrowser.tabs.length, 1,
+ "There should be no captive portal tab in the window.");
+}
+
+function ensureNoPortalNotification(win) {
+ let notificationBox =
+ win.document.getElementById("high-priority-global-notificationbox");
+ is(notificationBox.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE), null,
+ "There should be no captive portal notification in the window.");
+}
+
// Each of the test cases below is run twice: once for login-success and once
// for login-abort (aSuccess set to true and false respectively).
let testCasesForBothSuccessAndAbort = [
/**
* A portal is detected when there's no browser window,
* then a browser window is opened, then the portal is freed.
* The portal tab should be added and focused when the window is
* opened, and closed automatically when the success event is fired.
*/
function* test_detectedWithNoBrowserWindow_Open(aSuccess) {
yield portalDetectedNoBrowserWindow();
- let win = yield openWindowAndWaitForPortalTab();
+ let win = yield openWindowAndWaitForPortalTabAndNotification();
freePortal(aSuccess);
- is(win.gBrowser.tabs.length, 1,
- "The captive portal tab should have been closed.");
+ ensureNoPortalTab(win);
+ ensureNoPortalNotification(win);
yield BrowserTestUtils.closeWindow(win);
},
/**
* A portal is detected when there's no browser window, and the
* portal is freed before a browser window is opened. No portal
* tab should be added when a browser window is opened.
*/
function* test_detectedWithNoBrowserWindow_GoneBeforeOpen(aSuccess) {
yield portalDetectedNoBrowserWindow();
freePortal(aSuccess);
let win = yield BrowserTestUtils.openNewBrowserWindow();
// Wait for a while to make sure no tab is opened.
yield new Promise(resolve => {
setTimeout(resolve, 1000);
});
- is(win.gBrowser.tabs.length, 1,
- "No captive portal tab should have been opened.");
+ ensureNoPortalTab(win);
+ ensureNoPortalNotification(win);
yield BrowserTestUtils.closeWindow(win);
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab isn't focused, the tab should be closed automatically.
*/
function* test_detectedWithFocus(aSuccess) {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
+ ensurePortalTab(win);
+ ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
freePortal(aSuccess);
- is(win.gBrowser.tabs.length, 1,
- "The portal tab should have been closed.");
+ ensureNoPortalTab(win);
+ ensureNoPortalNotification(win);
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab has focus, the tab should be closed automatically.
*/
function* test_detectedWithFocus_selectedTab(aSuccess) {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
+ ensurePortalTab(win);
+ ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
win.gBrowser.selectedTab = tab;
freePortal(aSuccess);
- is(win.gBrowser.tabs.length, 1,
- "The portal tab should have been closed.");
+ ensureNoPortalTab(win);
+ ensureNoPortalNotification(win);
},
];
let singleRunTestCases = [
/**
* A portal is detected when there's no browser window,
* then a browser window is opened, and the portal is logged into
* and redirects to a different page. The portal tab should be added
* and focused when the window is opened, and left open after login
* since it redirected.
*/
function* test_detectedWithNoBrowserWindow_Redirect() {
yield portalDetectedNoBrowserWindow();
- let win = yield openWindowAndWaitForPortalTab();
+ let win = yield openWindowAndWaitForPortalTabAndNotification();
let browser = win.gBrowser.selectedTab.linkedBrowser;
let loadPromise =
BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
yield loadPromise;
freePortal(true);
- is(win.gBrowser.tabs.length, 2,
- "The captive portal tab should not have been closed.");
+ ensurePortalTab(win);
+ ensureNoPortalNotification(win);
yield BrowserTestUtils.closeWindow(win);
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab isn't focused, the tab should be closed automatically,
* even if the portal has redirected to a URL other than CANONICAL_URL.
*/
function* test_detectedWithFocus_redirectUnselectedTab() {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
+ ensurePortalTab(win);
+ ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
let browser = tab.linkedBrowser;
let loadPromise =
BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
yield loadPromise;
freePortal(true);
- is(win.gBrowser.tabs.length, 1,
- "The portal tab should have been closed.");
+ ensureNoPortalTab(win);
+ ensureNoPortalNotification(win);
},
/**
* A portal is detected when a browser window has focus. A portal tab should be
* opened in the background in the focused browser window. If the portal is
* freed when the tab has focus, and it has redirected to another page, the
* tab should be kept open.
*/
function* test_detectedWithFocus_redirectSelectedTab() {
let win = RecentWindow.getMostRecentBrowserWindow();
let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
Services.obs.notifyObservers(null, "captive-portal-login", null);
let tab = yield p;
+ ensurePortalNotification(win);
isnot(win.gBrowser.selectedTab, tab,
"The captive portal tab should be open in the background in the current window.");
win.gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
let loadPromise =
BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
yield loadPromise;
freePortal(true);
- is(win.gBrowser.tabs.length, 2,
- "The portal tab should not have been closed.");
+ ensurePortalTab(win);
+ ensureNoPortalNotification(win);
yield BrowserTestUtils.removeTab(tab);
},
+
+ /**
+ * Test the various expected behaviors of the "Show Login Page" button
+ * in the captive portal notification. The button should be visible for
+ * all tabs except the captive portal tab, and when clicked, should
+ * ensure a captive portal tab is open and select it.
+ */
+ function* test_showLoginPageButton() {
+ let win = RecentWindow.getMostRecentBrowserWindow();
+ let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
+ Services.obs.notifyObservers(null, "captive-portal-login", null);
+ let tab = yield p;
+ let notification = ensurePortalNotification(win);
+ isnot(win.gBrowser.selectedTab, tab,
+ "The captive portal tab should be open in the background in the current window.");
+ testShowLoginPageButtonVisibility(notification, "visible");
+
+ function testPortalTabSelectedAndButtonNotVisible() {
+ is(win.gBrowser.selectedTab, tab, "The captive portal tab should be selected.");
+ testShowLoginPageButtonVisibility(notification, "hidden");
+ }
+
+ // Select the captive portal tab. The button should hide.
+ let otherTab = win.gBrowser.selectedTab;
+ win.gBrowser.selectedTab = tab;
+ testShowLoginPageButtonVisibility(notification, "hidden");
+
+ // Select the other tab. The button should become visible.
+ win.gBrowser.selectedTab = otherTab;
+ testShowLoginPageButtonVisibility(notification, "visible");
+
+ // Simulate clicking the button. The portal tab should be selected and
+ // the button should hide.
+ let button = notification.querySelector("button.notification-button");
+ button.click();
+ testPortalTabSelectedAndButtonNotVisible();
+
+ // Close the tab. The button should become visible.
+ yield BrowserTestUtils.removeTab(tab);
+ ensureNoPortalTab(win);
+ testShowLoginPageButtonVisibility(notification, "visible");
+
+ function* clickButtonAndExpectNewPortalTab() {
+ p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
+ button.click();
+ tab = yield p;
+ is(win.gBrowser.selectedTab, tab, "The captive portal tab should be selected.");
+ }
+
+ // When the button is clicked, a new portal tab should be opened and
+ // selected.
+ yield clickButtonAndExpectNewPortalTab();
+
+ // Open another arbitrary tab. The button should become visible. When it's clicked,
+ // the portal tab should be selected.
+ let anotherTab = yield BrowserTestUtils.openNewForegroundTab(win.gBrowser);
+ testShowLoginPageButtonVisibility(notification, "visible");
+ button.click();
+ is(win.gBrowser.selectedTab, tab, "The captive portal tab should be selected.");
+
+ // Close the portal tab and select the arbitrary tab. The button should become
+ // visible and when it's clicked, a new portal tab should be opened.
+ yield BrowserTestUtils.removeTab(tab);
+ win.gBrowser.selectedTab = anotherTab;
+ testShowLoginPageButtonVisibility(notification, "visible");
+ yield clickButtonAndExpectNewPortalTab();
+
+ yield BrowserTestUtils.removeTab(anotherTab);
+ freePortal(true);
+ ensureNoPortalTab(win);
+ ensureNoPortalNotification(win);
+ },
];
for (let testcase of testCasesForBothSuccessAndAbort) {
add_task(testcase.bind(null, true));
add_task(testcase.bind(null, false));
}
for (let testcase of singleRunTestCases) {
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -1197,16 +1197,41 @@ this.BrowserTestUtils = {
* @param notificationValue (string)
* The "value" of the notification, which is often used as
* a unique identifier. Example: "plugin-crashed".
* @return Promise
* Resolves to the <xul:notification> that is being shown.
*/
waitForNotificationBar(tabbrowser, browser, notificationValue) {
let notificationBox = tabbrowser.getNotificationBox(browser);
+ return this.waitForNotificationInNotificationBox(notificationBox,
+ notificationValue);
+ },
+
+ /**
+ * Waits for a <xul:notification> with a particular value to appear
+ * in the global <xul:notificationbox> of the given browser window.
+ *
+ * @param win (<xul:window>)
+ * The browser window in whose global notificationbox the
+ * notification is expected to appear.
+ * @param notificationValue (string)
+ * The "value" of the notification, which is often used as
+ * a unique identifier. Example: "captive-portal-detected".
+ * @return Promise
+ * Resolves to the <xul:notification> that is being shown.
+ */
+ waitForGlobalNotificationBar(win, notificationValue) {
+ let notificationBox =
+ win.document.getElementById("high-priority-global-notificationbox");
+ return this.waitForNotificationInNotificationBox(notificationBox,
+ notificationValue);
+ },
+
+ waitForNotificationInNotificationBox(notificationBox, notificationValue) {
return new Promise((resolve) => {
let check = (event) => {
return event.target.value == notificationValue;
};
BrowserTestUtils.waitForEvent(notificationBox, "AlertActive",
false, check).then((event) => {
// The originalTarget of the AlertActive on a notificationbox