author | Nihanth Subramanya <nhnt11@gmail.com> |
Fri, 11 Nov 2016 15:36:49 +0530 | |
changeset 322885 | 246f2ae7dca76a324752d033abc896fb13ea12fe |
parent 322884 | d6c648dd77c88fddd359fa10c4c8d78183ae770a |
child 322886 | 2244cd42937474ed35655f5399a45a1344199a29 |
push id | 83996 |
push user | kwierso@gmail.com |
push date | Thu, 17 Nov 2016 01:35:58 +0000 |
treeherder | mozilla-inbound@e79cc7c6e7a7 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | MattN |
bugs | 1315969 |
milestone | 53.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
|
browser/modules/CaptivePortalWatcher.jsm | file | annotate | diff | comparison | revisions | |
browser/modules/test/browser_CaptivePortalWatcher.js | file | annotate | diff | comparison | revisions |
--- a/browser/modules/CaptivePortalWatcher.jsm +++ b/browser/modules/CaptivePortalWatcher.jsm @@ -1,53 +1,58 @@ /* 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"; 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 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 _delayedCaptivePortalDetected for more details. + */ + PORTAL_RECHECK_DELAY_MS: 150, + + // This is the value used to identify the captive portal notification. + PORTAL_NOTIFICATION_VALUE: "captive-portal-detected", + // 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. + * and then add the tab if, after a recheck, the portal is still active. This + * is set to true while we wait so that in the unlikely event that we receive + * another notification while waiting, we don't do things twice. */ - _waitingToAddTab: false, + _delayedCaptivePortalDetectedInProgress: false, + + // In the situation above, this is set to true while we wait for the recheck. + // This flag exists so that tests can appropriately simulate a recheck. + _waitingForRecheck: false, get canonicalURL() { return Services.prefs.getCharPref("captivedetect.canonicalURL"); }, init() { Services.obs.addObserver(this, "captive-portal-login", false); Services.obs.addObserver(this, "captive-portal-login-abort", false); @@ -77,33 +82,33 @@ this.CaptivePortalWatcher = { case "captive-portal-login": this._captivePortalDetected(); break; case "captive-portal-login-abort": case "captive-portal-login-success": this._captivePortalGone(); break; case "xul-window-visible": - this._delayedAddCaptivePortalTab(); + this._delayedCaptivePortalDetected(); break; } }, _captivePortalDetected() { - if (this._waitingToAddTab) { + if (this._delayedCaptivePortalDetectedInProgress) { 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 != Services.ww.activeWindow) { - this._waitingToAddTab = true; + this._delayedCaptivePortalDetectedInProgress = true; Services.obs.addObserver(this, "xul-window-visible", false); return; } this._showNotification(win); }, _ensureCaptivePortalTab(win) { @@ -122,56 +127,59 @@ this.CaptivePortalWatcher = { 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() { - if (!this._waitingToAddTab) { + _delayedCaptivePortalDetected() { + if (!this._delayedCaptivePortalDetectedInProgress) { return; } let win = RecentWindow.getMostRecentBrowserWindow(); if (win != Services.ww.activeWindow) { // The window that got focused was not a browser window. return; } Services.obs.removeObserver(this, "xul-window-visible"); // Trigger a portal recheck. The user may have logged into the portal via // another client, or changed networks. cps.recheckCaptivePortal(); + this._waitingForRecheck = true; let requestTime = Date.now(); let self = this; Services.obs.addObserver(function observer() { + let time = Date.now() - requestTime; Services.obs.removeObserver(observer, "captive-portal-check-complete"); - self._waitingToAddTab = false; + self._waitingForRecheck = false; + self._delayedCaptivePortalDetectedInProgress = false; if (cps.state != cps.LOCKED_PORTAL) { // We're free of the portal! return; } self._showNotification(win); - if (Date.now() - requestTime <= PORTAL_RECHECK_DELAY_MS) { + if (time <= self.PORTAL_RECHECK_DELAY_MS) { // The amount of time elapsed since we requested a recheck (i.e. since // the browser window was focused) was small enough that we can add and // focus a tab with the login page with no noticeable delay. self._ensureCaptivePortalTab(win); } }, "captive-portal-check-complete", false); }, _captivePortalGone() { - if (this._waitingToAddTab) { + if (this._delayedCaptivePortalDetectedInProgress) { Services.obs.removeObserver(this, "xul-window-visible"); - this._waitingToAddTab = false; + this._delayedCaptivePortalDetectedInProgress = false; } this._removeNotification(); if (!this._captivePortalTab) { return; } @@ -244,17 +252,17 @@ this.CaptivePortalWatcher = { 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, "", + let n = nb.appendNotification(message, this.PORTAL_NOTIFICATION_VALUE, "", nb.PRIORITY_INFO_MEDIUM, buttons, closeHandler); this._captivePortalNotification = Cu.getWeakReference(n); win.gBrowser.tabContainer.addEventListener("TabSelect", this); }, _removeNotification() {
--- a/browser/modules/test/browser_CaptivePortalWatcher.js +++ b/browser/modules/test/browser_CaptivePortalWatcher.js @@ -1,12 +1,19 @@ "use strict"; Components.utils.import("resource:///modules/RecentWindow.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "CaptivePortalWatcher", + "resource:///modules/CaptivePortalWatcher.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "cps", + "@mozilla.org/network/captive-portal-service;1", + "nsICaptivePortalService"); + 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], @@ -17,52 +24,73 @@ add_task(function* setup() { /** * We can't close the original window opened by mochitest without failing, so * override RecentWindow.getMostRecentBrowserWindow to make CaptivePortalWatcher * think there's no window. */ function* portalDetectedNoBrowserWindow() { let getMostRecentBrowserWindow = RecentWindow.getMostRecentBrowserWindow; RecentWindow.getMostRecentBrowserWindow = () => {}; - Services.obs.notifyObservers(null, "captive-portal-login", null); + yield portalDetected(); RecentWindow.getMostRecentBrowserWindow = getMostRecentBrowserWindow; } -function* openWindowAndWaitForPortalTabAndNotification() { +function* portalDetected() { + Services.obs.notifyObservers(null, "captive-portal-login", null); + yield BrowserTestUtils.waitForCondition(() => { + return cps.state == cps.LOCKED_PORTAL; + }, "Waiting for Captive Portal Service to update state after portal detected."); +} + +function* freePortal(aSuccess) { + Services.obs.notifyObservers(null, + "captive-portal-login-" + (aSuccess ? "success" : "abort"), null); + yield BrowserTestUtils.waitForCondition(() => { + return cps.state != cps.LOCKED_PORTAL; + }, "Waiting for Captive Portal Service to update state after portal freed."); +} + +function* openWindowAndWaitForPortalUI(aLongRecheck) { + // CaptivePortalWatcher triggers a recheck when a window gains focus. If + // the time taken for the check to complete is under PORTAL_RECHECK_DELAY_MS, + // a tab with the login page is opened and selected. If it took longer, + // no tab is opened. It's not reliable to time things in an async test, + // so use a delay threshold of -1 to simulate a long recheck (so that any + // amount of time is considered excessive), and a very large threshold to + // simulate a short recheck. + CaptivePortalWatcher.PORTAL_RECHECK_DELAY_MS = aLongRecheck ? -1 : 1000000; + let win = yield BrowserTestUtils.openNewBrowserWindow(); - // Thanks to things being async, at this point we now have a new browser window - // but the portal notification and tab may or may not have opened. So first we - // check if there's already a portal notification, and if not, wait. - let notification = win.document.getElementById("high-priority-global-notificationbox") - .getNotificationWithValue(PORTAL_NOTIFICATION_VALUE); - if (!notification) { - notification = - yield BrowserTestUtils.waitForGlobalNotificationBar(win, PORTAL_NOTIFICATION_VALUE); + + // After a new window is opened, CaptivePortalWatcher asks for a recheck, and + // waits for it to complete. We need to manually tell it a recheck completed. + yield BrowserTestUtils.waitForCondition(() => { + return CaptivePortalWatcher._waitingForRecheck; + }, "Waiting for CaptivePortalWatcher to trigger a recheck."); + Services.obs.notifyObservers(null, "captive-portal-check-complete", null); + + let notification = ensurePortalNotification(win); + + if (aLongRecheck) { + ensureNoPortalTab(win); + testShowLoginPageButtonVisibility(notification, "visible"); + return win; } - // Then we see if there's already a portal tab. If it's open, it'll be the second one. + let tab = win.gBrowser.tabs[1]; - if (!tab || tab.linkedBrowser.currentURI.spec != CANONICAL_URL) { - // The tab either hasn't been opened yet or it hasn't loaded the portal URL. - // Waiting for a location change in the tabbrowser covers both cases. + if (tab.linkedBrowser.currentURI.spec != CANONICAL_URL) { + // The tab should load the canonical URL, wait for it. yield BrowserTestUtils.waitForLocationChange(win.gBrowser, CANONICAL_URL); - // At this point the portal tab should be the second tab. If there is still - // no second tab, something is wrong, and the selectedTab test below will fail. - tab = win.gBrowser.tabs[1]; } 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) { @@ -119,229 +147,159 @@ function* closeWindowAndWaitForXulWindow yield BrowserTestUtils.closeWindow(win); yield p; } // 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. + * 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. + * The captive portal notification should be shown when the window is + * opened, and closed automatically when the success event is fired. */ function* test_detectedWithNoBrowserWindow_Open(aSuccess) { yield portalDetectedNoBrowserWindow(); - let win = yield openWindowAndWaitForPortalTabAndNotification(); - freePortal(aSuccess); + let win = yield openWindowAndWaitForPortalUI(); + yield freePortal(aSuccess); + ensureNoPortalTab(win); + ensureNoPortalNotification(win); + yield closeWindowAndWaitForXulWindowVisible(win); + }, + + /** + * A portal is detected when there's no browser window, then a browser + * window is opened, then the portal is freed. + * The recheck triggered when the browser window is opened takes a + * long time. No portal tab should be added. + * The captive portal notification should be shown when the window is + * opened, and closed automatically when the success event is fired. + */ + function* test_detectedWithNoBrowserWindow_LongRecheck(aSuccess) { + yield portalDetectedNoBrowserWindow(); + let win = yield openWindowAndWaitForPortalUI(true); + yield freePortal(aSuccess); ensureNoPortalTab(win); ensureNoPortalNotification(win); yield closeWindowAndWaitForXulWindowVisible(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. + * UI should be shown when a browser window is opened. */ function* test_detectedWithNoBrowserWindow_GoneBeforeOpen(aSuccess) { yield portalDetectedNoBrowserWindow(); - freePortal(aSuccess); + yield freePortal(aSuccess); let win = yield BrowserTestUtils.openNewBrowserWindow(); - // Wait for a while to make sure no tab is opened. + // Wait for a while to make sure no UI is shown. yield new Promise(resolve => { setTimeout(resolve, 1000); }); ensureNoPortalTab(win); ensureNoPortalNotification(win); yield closeWindowAndWaitForXulWindowVisible(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. + * A portal is detected when a browser window has focus. No portal tab should + * be opened. A notification bar should be displayed in the focused window. */ 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); + yield portalDetected(); 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); - ensureNoPortalTab(win); + yield freePortal(aSuccess); 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 openWindowAndWaitForPortalTabAndNotification(); + let win = yield openWindowAndWaitForPortalUI(); 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); + yield freePortal(true); ensurePortalTab(win); ensureNoPortalNotification(win); yield closeWindowAndWaitForXulWindowVisible(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); - 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); - 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; + yield portalDetected(); 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"); + let button = notification.querySelector("button.notification-button"); + function* clickButtonAndExpectNewPortalTab() { + let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL); + button.click(); + let tab = yield p; + is(win.gBrowser.selectedTab, tab, "The captive portal tab should be selected."); + return tab; + } - // 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(); + // Simulate clicking the button. The portal tab should be opened and + // selected and the button should hide. + let tab = yield clickButtonAndExpectNewPortalTab(); 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(); + tab = 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(); + tab = yield clickButtonAndExpectNewPortalTab(); yield BrowserTestUtils.removeTab(anotherTab); - freePortal(true); + yield freePortal(true); ensureNoPortalTab(win); ensureNoPortalNotification(win); }, ]; for (let testcase of testCasesForBothSuccessAndAbort) { add_task(testcase.bind(null, true)); add_task(testcase.bind(null, false));