From 4406631ff2a6f6547de5e79d4c50718edacd5e57 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Sun, 22 Feb 2015 11:56:32 +0100 Subject: [PATCH 1/8] Bug 1135375 - indicate we intend to ship in-content prefs with 38, r=jaws --- browser/app/profile/firefox.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index cc65d5332bfe..4be3972f1ecb 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -860,11 +860,7 @@ pref("browser.preferences.instantApply", true); #endif // Toggles between the two Preferences implementations, pop-up window and in-content -#ifdef EARLY_BETA_OR_EARLIER pref("browser.preferences.inContent", true); -#else -pref("browser.preferences.inContent", false); -#endif pref("browser.download.show_plugins_in_list", true); pref("browser.download.hide_plugins_without_extensions", true); From 68269ca60b8692d593a19d210db8c172050c2d62 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Sun, 22 Feb 2015 12:00:47 +0100 Subject: [PATCH 2/8] Bug 1135237 - update message for EME notification, r=dolske --- browser/base/content/browser-eme.js | 14 ++------------ .../en-US/chrome/browser/browser.properties | 4 ++-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/browser/base/content/browser-eme.js b/browser/base/content/browser-eme.js index 2ef1817289b2..b90a42afecc9 100644 --- a/browser/base/content/browser-eme.js +++ b/browser/base/content/browser-eme.js @@ -25,15 +25,6 @@ let gEMEHandler = { return ""; }, - getDRMLabel: function(keySystem) { - if (keySystem.startsWith("com.adobe")) { - return "Adobe Primetime"; - } - if (keySystem == "org.w3.clearkey") { - return "ClearKey"; - } - return gNavigatorBundle.getString("emeNotifications.unknownDRMSoftware"); - }, onDontAskAgain: function(menuPopupItem) { let button = menuPopupItem.parentNode.anchorNode; let bar = button.parentNode; @@ -158,12 +149,11 @@ let gEMEHandler = { } let msgPrefix = "emeNotifications.drmContentPlaying."; - let msgId = msgPrefix + "message"; + let msgId = msgPrefix + "message2"; let btnLabelId = msgPrefix + "button.label"; let btnAccessKeyId = msgPrefix + "button.accesskey"; - let drmProvider = this.getDRMLabel(keySystem); - let message = gNavigatorBundle.getFormattedString(msgId, [drmProvider, this._brandShortName]); + let message = gNavigatorBundle.getFormattedString(msgId, [this._brandShortName]); let anchorId = "eme-notification-icon"; let mainAction = { diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 8c4e9b68c958..23a6227caabc 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -591,8 +591,8 @@ getUserMedia.sharingMenuMicrophoneWindow = %S (microphone and window) # origin for the sharing menu if no readable origin could be deduced from the URL. getUserMedia.sharingMenuUnknownHost = Unknown origin -# LOCALIZATION NOTE(emeNotifications.drmContentPlaying.message): %1$S is the vendor name of the DRM that's in use, %2$S is brandShortName. -emeNotifications.drmContentPlaying.message = Some audio or video on this site uses %1$S DRM software, which may limit what %2$S can let you do with it. +# LOCALIZATION NOTE(emeNotifications.drmContentPlaying.message2): %S is brandShortName. +emeNotifications.drmContentPlaying.message2 = Some audio or video on this site uses DRM software, which may limit what %S can let you do with it. emeNotifications.drmContentPlaying.button.label = Configureā€¦ emeNotifications.drmContentPlaying.button.accesskey = C From 3262cbbaff509dbbb3207a79cca0424ce71ceed0 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Sun, 22 Feb 2015 12:01:04 +0100 Subject: [PATCH 3/8] Bug 1131758 - add strings for 64-bit and unsupported OSes for EME, r=dolske --- browser/locales/en-US/chrome/browser/browser.properties | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 23a6227caabc..2f6550cb1925 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -614,6 +614,11 @@ emeNotifications.drmContentCDMInsufficientVersion.message = %S is installing upd # LOCALIZATION NOTE(emeNotifications.drmContentCDMInstalling.message): NB: inserted via innerHTML, so please don't use <, > or & in this string. %S is brandShortName emeNotifications.drmContentCDMInstalling.message = %S is installing components needed to play the audio or video on this page. Please try again later. +# LOCALIZATION NOTE(emeNotifications.drmContentCDMNotSupported.64bit.message): NB: inserted via innerHTML, so please don't use <, > or & in this string. %1$S is brandShortName, %2$S will be the 'learn more' link +emeNotifications.drmContentCDMNotSupported.64bit.message = The audio or video on this page requires DRM software that this 64-bit build of %1$S does not support. %2$S +# LOCALIZATION NOTE(emeNotifications.drmContentCDMNotSupported.unsupportedOS.message): NB: inserted via innerHTML, so please don't use <, > or & in this string. %1$S is brandShortName, %2$S is the name of the user's OS (Windows, Linux, Mac OS X), %3$S will be the 'learn more' link +emeNotifications.drmContentCDMNotSupported.unsupportedOS.message = The audio or video on this page requires DRM software that %1$S does not support on %2$S. %3$S + emeNotifications.optionsButton.label = Options emeNotifications.optionsButton.accesskey = O From f4e50a281f099339507edc7799ebbd3bcc7949a9 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Tue, 27 Jan 2015 08:11:00 +0100 Subject: [PATCH 4/8] Bug 1111022 - Load self-support page in a hidden tab. r=ttaubert --- browser/app/profile/firefox.js | 2 + browser/components/nsBrowserGlue.js | 7 + browser/modules/SelfSupportBackend.jsm | 323 +++++++++++++++++++++++++ browser/modules/moz.build | 1 + 4 files changed, 333 insertions(+) create mode 100644 browser/modules/SelfSupportBackend.jsm diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 4be3972f1ecb..f5ca9e04a2b6 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1028,6 +1028,8 @@ pref("browser.rights.3.shown", false); pref("browser.rights.override", true); #endif +pref("browser.selfsupport.url", "http://self-repair.mozilla.org/%LOCALE%/repair"); + pref("browser.sessionstore.resume_from_crash", true); pref("browser.sessionstore.resume_session_once", false); diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 90971caeca9d..99027513b91c 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -91,6 +91,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "RemotePrompt", XPCOMUtils.defineLazyModuleGetter(this, "ContentPrefServiceParent", "resource://gre/modules/ContentPrefServiceParent.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "SelfSupportBackend", + "resource:///modules/SelfSupportBackend.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"); @@ -627,6 +630,8 @@ BrowserGlue.prototype = { LoginManagerParent.init(); ReaderParent.init(); + SelfSupportBackend.init(); + #ifdef NIGHTLY_BUILD Services.prefs.addObserver(POLARIS_ENABLED, this, false); #endif @@ -888,6 +893,8 @@ BrowserGlue.prototype = { Cu.reportError("Could not end startup crash tracking in quit-application-granted: " + e); } + SelfSupportBackend.uninit(); + CustomizationTabPreloader.uninit(); WebappManager.uninit(); #ifdef NIGHTLY_BUILD diff --git a/browser/modules/SelfSupportBackend.jsm b/browser/modules/SelfSupportBackend.jsm new file mode 100644 index 000000000000..e9a6e09a15e6 --- /dev/null +++ b/browser/modules/SelfSupportBackend.jsm @@ -0,0 +1,323 @@ +/* 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"; + +this.EXPORTED_SYMBOLS = ["SelfSupportBackend"]; + +const Cu = Components.utils; +const Cc = Components.classes; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("resource://gre/modules/Preferences.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Timer.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "HiddenFrame", + "resource:///modules/HiddenFrame.jsm"); + +// Enables or disables the Self Support. +const PREF_ENABLED = "browser.selfsupport.enabled"; +// Url to open in the Self Support browser, in the urlFormatter service format. +const PREF_URL = "browser.selfsupport.url"; +// FHR status. +const PREF_FHR_ENABLED = "datareporting.healthreport.service.enabled"; +// UITour status. +const PREF_UITOUR_ENABLED = "browser.uitour.enabled"; + +// Controls the interval at which the self support page tries to reload in case of +// errors. +const RETRY_INTERVAL_MS = 30000; +// Maximum number of SelfSupport page load attempts in case of failure. +const MAX_RETRIES = 5; +// The delay after which to load the self-support, at startup. +const STARTUP_DELAY_MS = 5000; + +const LOGGER_NAME = "Browser.SelfSupportBackend"; +const PREF_BRANCH_LOG = "browser.selfsupport.log."; +const PREF_LOG_LEVEL = PREF_BRANCH_LOG + "level"; +const PREF_LOG_DUMP = PREF_BRANCH_LOG + "dump"; + +const HTML_NS = "http://www.w3.org/1999/xhtml"; +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +const UITOUR_FRAME_SCRIPT = "chrome://browser/content/content-UITour.js"; + +let gLogAppenderDump = null; + +this.SelfSupportBackend = Object.freeze({ + init: function () { + SelfSupportBackendInternal.init(); + }, + + uninit: function () { + SelfSupportBackendInternal.uninit(); + }, +}); + +let SelfSupportBackendInternal = { + // The browser element that will load the SelfSupport page. + _browser: null, + // The Id of the timer triggering delayed SelfSupport page load. + _delayedLoadTimerId: null, + // The HiddenFrame holding the _browser element. + _frame: null, + _log: null, + _progressListener: null, + + /** + * Initializes the self support backend. + */ + init: function () { + this._configureLogging(); + + this._log.trace("init"); + + Preferences.observe(PREF_BRANCH_LOG, this._configureLogging, this); + + // Only allow to use SelfSupport if FHR is enabled. + let fhrEnabled = Preferences.get(PREF_FHR_ENABLED, false); + if (!fhrEnabled) { + this._log.config("init - Disabling SelfSupport because Health Report is disabled."); + return; + } + + // Make sure UITour is enabled. + let uiTourEnabled = Preferences.get(PREF_UITOUR_ENABLED, false); + if (!uiTourEnabled) { + this._log.config("init - Disabling SelfSupport because UITour is disabled."); + return; + } + + // Check the preferences to see if we want this to be active. + if (!Preferences.get(PREF_ENABLED, true)) { + this._log.config("init - SelfSupport is disabled."); + return; + } + + Services.obs.addObserver(this, "sessionstore-windows-restored", false); + }, + + /** + * Shut down the self support backend, if active. + */ + uninit: function () { + this._log.trace("uninit"); + + Preferences.ignore(PREF_BRANCH_LOG, this._configureLogging, this); + + // Cancel delayed loading, if still active, when shutting down. + clearTimeout(this._delayedLoadTimerId); + + // Dispose of the hidden browser. + if (this._browser !== null) { + if (this._browser.contentWindow) { + this._browser.contentWindow.removeEventListener("DOMWindowClose", this, true); + } + + if (this._progressListener) { + this._browser.removeProgressListener(this._progressListener); + this._progressListener.destroy(); + this._progressListener = null; + } + + this._browser.remove(); + this._browser = null; + } + + if (this._frame) { + this._frame.destroy(); + this._frame = null; + } + }, + + /** + * Handle notifications. Once all windows are created, we wait a little bit more + * since tabs might still be loading. Then, we open the self support. + */ + observe: function (aSubject, aTopic, aData) { + this._log.trace("observe - Topic " + aTopic); + + if (aTopic === "sessionstore-windows-restored") { + Services.obs.removeObserver(this, "sessionstore-windows-restored"); + this._delayedLoadTimerId = setTimeout(this._loadSelfSupport.bind(this), STARTUP_DELAY_MS); + } + }, + + /** + * Configure the logger based on the preferences. + */ + _configureLogging: function() { + if (!this._log) { + this._log = Log.repository.getLogger(LOGGER_NAME); + + // Log messages need to go to the browser console. + let consoleAppender = new Log.ConsoleAppender(new Log.BasicFormatter()); + this._log.addAppender(consoleAppender); + } + + // Make sure the logger keeps up with the logging level preference. + this._log.level = Log.Level[Preferences.get(PREF_LOG_LEVEL, "Warn")]; + + // If enabled in the preferences, add a dump appender. + let logDumping = Preferences.get(PREF_LOG_DUMP, false); + if (logDumping != !!gLogAppenderDump) { + if (logDumping) { + gLogAppenderDump = new Log.DumpAppender(new Log.BasicFormatter()); + this._log.addAppender(gLogAppenderDump); + } else { + this._log.removeAppender(gLogAppenderDump); + gLogAppenderDump = null; + } + } + }, + + /** + * Create an hidden frame to host our |browser|, then load the SelfSupport page in it. + * @param aURL The URL to load in the browser. + */ + _makeHiddenBrowser: function(aURL) { + this._frame = new HiddenFrame(); + return this._frame.get().then(aFrame => { + let doc = aFrame.document; + + this._browser = doc.createElementNS(XUL_NS, "browser"); + this._browser.setAttribute("type", "content"); + this._browser.setAttribute("disableglobalhistory", "true"); + this._browser.setAttribute("src", aURL); + + doc.documentElement.appendChild(this._browser); + }); + }, + + handleEvent: function(aEvent) { + this._log.trace("handleEvent - aEvent.type " + aEvent.type + ", Trusted " + aEvent.isTrusted); + + if (aEvent.type === "DOMWindowClose") { + let window = this._browser.contentDocument.defaultView; + let target = aEvent.target; + + if (target == window) { + // preventDefault stops the default window.close(). We need to do that to prevent + // Services.appShell.hiddenDOMWindow from being destroyed. + aEvent.preventDefault(); + + this.uninit(); + } + } + }, + + /** + * Called when the self support page correctly loads. + */ + _pageSuccessCallback: function() { + this._log.debug("_pageSuccessCallback - Page correctly loaded."); + this._browser.removeProgressListener(this._progressListener); + this._progressListener.destroy(); + this._progressListener = null; + + // Allow SelfSupportBackend to catch |window.close()| issued by the content. + this._browser.contentWindow.addEventListener("DOMWindowClose", this, true); + }, + + /** + * Called when the self support page fails to load. + */ + _pageLoadErrorCallback: function() { + this._log.info("_pageLoadErrorCallback - Too many failed load attempts. Giving up."); + this.uninit(); + }, + + /** + * Create a browser and attach it to an hidden window. The browser will contain the + * self support page and attempt to load the page content. If loading fails, try again + * after an interval. + */ + _loadSelfSupport: function() { + // Fetch the Self Support URL from the preferences. + let unformattedURL = Preferences.get(PREF_URL, null); + let url = Services.urlFormatter.formatURL(unformattedURL); + + this._log.config("_loadSelfSupport - URL " + url); + + // Create the hidden browser. + this._makeHiddenBrowser(url).then(() => { + // Load UITour frame script. + this._browser.messageManager.loadFrameScript(UITOUR_FRAME_SCRIPT, true); + + // We need to watch for load errors as well and, in case, try to reload + // the self support page. + const webFlags = Ci.nsIWebProgress.NOTIFY_STATE_WINDOW | + Ci.nsIWebProgress.NOTIFY_STATE_REQUEST | + Ci.nsIWebProgress.NOTIFY_LOCATION; + + this._progressListener = new ProgressListener(() => this._pageLoadErrorCallback(), + () => this._pageSuccessCallback()); + + this._browser.addProgressListener(this._progressListener, webFlags); + }); + } +}; + +/** + * A progress listener object which notifies of page load error and load success + * through callbacks. When the page fails to load, the progress listener tries to + * reload it up to MAX_RETRIES times. The page is not loaded again immediately, but + * after a timeout. + * + * @param aLoadErrorCallback Called when a page failed to load MAX_RETRIES times. + * @param aLoadSuccessCallback Called when a page correctly loads. + */ +function ProgressListener(aLoadErrorCallback, aLoadSuccessCallback) { + this._loadErrorCallback = aLoadErrorCallback; + this._loadSuccessCallback = aLoadSuccessCallback; + // The number of page loads attempted. + this._loadAttempts = 0; + this._log = Log.repository.getLogger(LOGGER_NAME); + // The Id of the timer which triggers page load again in case of errors. + this._reloadTimerId = null; +} + +ProgressListener.prototype = { + onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) { + if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) { + this._log.warn("onLocationChange - There was a problem fetching the SelfSupport URL (attempt " + + this._loadAttempts + ")."); + + // Increase the number of attempts and bail out if we failed too many times. + this._loadAttempts++; + if (this._loadAttempts > MAX_RETRIES) { + this._loadErrorCallback(); + return; + } + + // Reload the page after the retry interval expires. The interval is multiplied + // by the number of attempted loads, so that it takes a bit more to try to reload + // when frequently failing. + this._reloadTimerId = setTimeout(() => { + this._log.debug("onLocationChange - Reloading SelfSupport URL in the hidden browser."); + aWebProgress.DOMWindow.location.reload(); + }, RETRY_INTERVAL_MS * this._loadAttempts); + } + }, + + onStateChange: function (aWebProgress, aRequest, aFlags, aStatus) { + if (aFlags & Ci.nsIWebProgressListener.STATE_STOP && + aFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK && + aFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW && + Components.isSuccessCode(aStatus)) { + this._loadSuccessCallback(); + } + }, + + destroy: function () { + // Make sure we don't try to reload self support when shutting down. + clearTimeout(this._reloadTimerId); + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference]), +}; diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 2dd2147cdf81..574455c28c5b 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -32,6 +32,7 @@ EXTRA_JS_MODULES += [ 'ProcessHangMonitor.jsm', 'ReaderParent.jsm', 'RemotePrompt.jsm', + 'SelfSupportBackend.jsm', 'SitePermissions.jsm', 'Social.jsm', 'TabCrashReporter.jsm', From deb826f3fbdac775ab91839a4aae58add20f2e91 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Fri, 9 Jan 2015 11:18:00 +0100 Subject: [PATCH 5/8] Bug 1111022 - Tests for the SelfSupport backend. r=gfritzsche --- browser/modules/test/browser.ini | 4 + .../test/browser_SelfSupportBackend.js | 158 ++++++++++++++++++ layout/tools/reftest/reftest-preferences.js | 3 + testing/mochitest/browser-test.js | 4 + testing/profiles/prefs_general.js | 3 + testing/xpcshell/head.js | 1 + 6 files changed, 173 insertions(+) create mode 100644 browser/modules/test/browser_SelfSupportBackend.js diff --git a/browser/modules/test/browser.ini b/browser/modules/test/browser.ini index a5bfb92a82ae..927644f651f9 100644 --- a/browser/modules/test/browser.ini +++ b/browser/modules/test/browser.ini @@ -12,6 +12,10 @@ support-files = contentSearchSuggestions.xml [browser_NetworkPrioritizer.js] skip-if = e10s # Bug 666804 - Support NetworkPrioritizer in e10s +[browser_SelfSupportBackend.js] +support-files = + ../../components/uitour/test/uitour.html + ../../components/uitour/UITour-lib.js [browser_SignInToWebsite.js] skip-if = e10s # Bug 941426 - SignIntoWebsite.jsm not e10s friendly [browser_taskbar_preview.js] diff --git a/browser/modules/test/browser_SelfSupportBackend.js b/browser/modules/test/browser_SelfSupportBackend.js new file mode 100644 index 000000000000..781d9044c8cc --- /dev/null +++ b/browser/modules/test/browser_SelfSupportBackend.js @@ -0,0 +1,158 @@ +/* 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"; + +// Pass an empty scope object to the import to prevent "leaked window property" +// errors in tests. +let Preferences = Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences; +let PromiseUtils = Cu.import("resource://gre/modules/PromiseUtils.jsm", {}).PromiseUtils; +let SelfSupportBackend = + Cu.import("resource:///modules/SelfSupportBackend.jsm", {}).SelfSupportBackend; + +const PREF_SELFSUPPORT_ENABLED = "browser.selfsupport.enabled"; +const PREF_SELFSUPPORT_URL = "browser.selfsupport.url"; +const PREF_UITOUR_ENABLED = "browser.uitour.enabled"; + +const TEST_WAIT_RETRIES = 60; + +const TEST_PAGE_URL = getRootDirectory(gTestPath) + "uitour.html"; + +/** + * Find a browser, with an IFRAME as parent, who has aURL as the source attribute. + * + * @param aURL The URL to look for to identify the browser. + * + * @returns {Object} The browser element or null on failure. + */ +function findSelfSupportBrowser(aURL) { + let frames = Services.appShell.hiddenDOMWindow.document.querySelectorAll('iframe'); + for (let frame of frames) { + try { + let browser = frame.contentDocument.getElementById("win").querySelectorAll('browser')[0]; + let url = browser.getAttribute("src"); + if (url == aURL) { + return browser; + } + } catch (e) { + continue; + } + } + return null; +} + +/** + * Wait for self support page to load. + * + * @param aURL The URL to look for to identify the browser. + * + * @returns {Promise} Return a promise which is resolved when SelfSupport page is fully + * loaded. + */ +function promiseSelfSupportLoad(aURL) { + return new Promise((resolve, reject) => { + // Find the SelfSupport browser. + let browserPromise = waitForConditionPromise(() => !!findSelfSupportBrowser(aURL), + "SelfSupport browser not found.", + TEST_WAIT_RETRIES); + + // Once found, append a "load" listener to catch page loads. + browserPromise.then(() => { + let browser = findSelfSupportBrowser(aURL); + if (browser.contentDocument.readyState === "complete") { + resolve(browser); + } else { + let handler = () => { + browser.removeEventListener("load", handler, true); + resolve(browser); + }; + browser.addEventListener("load", handler, true); + } + }, reject); + }); +} + +/** + * Wait for self support to close. + * + * @param aURL The URL to look for to identify the browser. + * + * @returns {Promise} Return a promise which is resolved when SelfSupport browser cannot + * be found anymore. + */ +function promiseSelfSupportClose(aURL) { + return waitForConditionPromise(() => !findSelfSupportBrowser(aURL), + "SelfSupport browser is still open.", TEST_WAIT_RETRIES); +} + +/** + * Prepare the test environment. + */ +add_task(function* setupEnvironment() { + // We always run the SelfSupportBackend in tests to check for weird behaviours. + // Disable it to test its start-up. + SelfSupportBackend.uninit(); + + // Testing prefs are set via |user_pref|, so we need to get their value in order + // to restore them. + let selfSupportEnabled = Preferences.get(PREF_SELFSUPPORT_ENABLED, true); + let uitourEnabled = Preferences.get(PREF_UITOUR_ENABLED, false); + let selfSupportURL = Preferences.get(PREF_SELFSUPPORT_URL, ""); + + // Enable the SelfSupport backend and set the page URL. We also make sure UITour + // is enabled. + Preferences.set(PREF_SELFSUPPORT_ENABLED, true); + Preferences.set(PREF_UITOUR_ENABLED, true); + Preferences.set(PREF_SELFSUPPORT_URL, TEST_PAGE_URL); + + registerCleanupFunction(() => { + Preferences.set(PREF_SELFSUPPORT_ENABLED, selfSupportEnabled); + Preferences.set(PREF_UITOUR_ENABLED, uitourEnabled); + Preferences.set(PREF_SELFSUPPORT_URL, selfSupportURL); + }); +}); + +/** + * Test that the self support page can use the UITour API and close itself. + */ +add_task(function* test_selfSupport() { + // Initialise the SelfSupport backend and trigger the load. + SelfSupportBackend.init(); + + // SelfSupportBackend waits for "sessionstore-windows-restored" to start loading. Send it. + info("Sending sessionstore-windows-restored"); + Services.obs.notifyObservers(null, "sessionstore-windows-restored", null); + + // Wait for the SelfSupport page to load. + info("Waiting for the SelfSupport local page to load."); + let selfSupportBrowser = yield promiseSelfSupportLoad(TEST_PAGE_URL); + Assert.ok(!!selfSupportBrowser, "SelfSupport browser must exist."); + + // Get a reference to the UITour API. + info("Testing access to the UITour API."); + let contentWindow = + Cu.waiveXrays(selfSupportBrowser.contentDocument.defaultView); + let uitourAPI = contentWindow.Mozilla.UITour; + + // Test the UITour API with a ping. + let pingPromise = new Promise((resolve) => { + uitourAPI.ping(resolve); + }); + yield pingPromise; + + // Close SelfSupport from content. + contentWindow.close(); + + // Wait until SelfSupport closes. + info("Waiting for the SelfSupport to close."); + yield promiseSelfSupportClose(TEST_PAGE_URL); + + // Find the SelfSupport browser, again. We don't expect to find it. + selfSupportBrowser = findSelfSupportBrowser(TEST_PAGE_URL); + Assert.ok(!selfSupportBrowser, "SelfSupport browser must not exist."); + + // We shouldn't need this, but let's keep it to make sure closing SelfSupport twice + // doesn't create any problem. + SelfSupportBackend.uninit(); +}); diff --git a/layout/tools/reftest/reftest-preferences.js b/layout/tools/reftest/reftest-preferences.js index be1363f22522..a4ff8f6e092e 100644 --- a/layout/tools/reftest/reftest-preferences.js +++ b/layout/tools/reftest/reftest-preferences.js @@ -58,3 +58,6 @@ // desired side-effect of preventing our geoip lookup. branch.setBoolPref("browser.search.isUS", true); branch.setCharPref("browser.search.countryCode", "US"); + + // Make sure SelfSupport doesn't hit the network. + branch.setCharPref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/"); diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index 11ada9fd82ef..11cba28e34f9 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -25,6 +25,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "CustomizationTabPreloader", XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch", "resource:///modules/ContentSearch.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "SelfSupportBackend", + "resource:///modules/SelfSupportBackend.jsm"); + const SIMPLETEST_OVERRIDES = ["ok", "is", "isnot", "ise", "todo", "todo_is", "todo_isnot", "info", "expectAssertions", "requestCompleteLog"]; @@ -523,6 +526,7 @@ Tester.prototype = { gBrowser.getNotificationBox(browser).remove(); } + SelfSupportBackend.uninit(); CustomizationTabPreloader.uninit(); SocialFlyout.unload(); SocialShare.uninit(); diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index d98e80b9a067..a773d09b96b7 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -281,6 +281,9 @@ user_pref("browser.uitour.url", "http://%(server)s/uitour-dummy/tour"); user_pref("browser.search.isUS", true); user_pref("browser.search.countryCode", "US"); +// Make sure the self support tab doesn't hit the network. +user_pref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/"); + user_pref("media.eme.enabled", true); user_pref("media.eme.apiVisible", true); diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index 5f46863385cd..e65f1f3fcde8 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -1417,5 +1417,6 @@ try { .getService(Components.interfaces.nsIPrefBranch); prefs.setCharPref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml"); + prefs.setCharPref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/"); } } catch (e) { } From 9a02041f82d75bd820b6e79fe8acee21234a230a Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Sun, 25 Jan 2015 07:13:00 +0100 Subject: [PATCH 6/8] Bug 1111022 - Changes UITour.jsm to work with windowless browsers. r=MattN --- browser/components/uitour/UITour.jsm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/browser/components/uitour/UITour.jsm b/browser/components/uitour/UITour.jsm index bbc1eeb53bb2..0c288b10292d 100644 --- a/browser/components/uitour/UITour.jsm +++ b/browser/components/uitour/UITour.jsm @@ -344,6 +344,15 @@ this.UITour = { onPageEvent: function(aMessage, aEvent) { let browser = aMessage.target; let window = browser.ownerDocument.defaultView; + + // Does the window have tabs? We need to make sure since windowless browsers do + // not have tabs. + if (!window.gBrowser) { + // When using windowless browsers we don't have a valid |window|. If that's the case, + // use the most recent window as a target for UITour functions (see Bug 1111022). + window = Services.wm.getMostRecentWindow("navigator:browser"); + } + let tab = window.gBrowser.getTabForBrowser(browser); let messageManager = browser.messageManager; From 7be22d582b703eec7ec7bb1cda0b0e06998cf815 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Sun, 25 Jan 2015 07:19:00 +0100 Subject: [PATCH 7/8] Bug 1111022 - Adds a test to make sure UITour works with no tabs/windowless browsers. r=MattN --- browser/components/uitour/test/browser.ini | 1 + .../components/uitour/test/browser_no_tabs.js | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 browser/components/uitour/test/browser_no_tabs.js diff --git a/browser/components/uitour/test/browser.ini b/browser/components/uitour/test/browser.ini index e86d5da55b2c..7ff21c5898d7 100644 --- a/browser/components/uitour/test/browser.ini +++ b/browser/components/uitour/test/browser.ini @@ -5,6 +5,7 @@ support-files = uitour.html ../UITour-lib.js +[browser_no_tabs.js] [browser_UITour.js] skip-if = os == "linux" || e10s # Intermittent failures, bug 951965 [browser_UITour2.js] diff --git a/browser/components/uitour/test/browser_no_tabs.js b/browser/components/uitour/test/browser_no_tabs.js new file mode 100644 index 000000000000..5b965c6e1d04 --- /dev/null +++ b/browser/components/uitour/test/browser_no_tabs.js @@ -0,0 +1,110 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const HTML_NS = "http://www.w3.org/1999/xhtml"; +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +/** + * Create a frame in the |hiddenDOMWindow| to host a |browser|, then load the URL in the + * latter. + * + * @param aURL + * The URL to open in the browser. + **/ +function createHiddenBrowser(aURL) { + let deferred = Promise.defer(); + let hiddenDoc = Services.appShell.hiddenDOMWindow.document; + + // Create a HTML iframe with a chrome URL, then this can host the browser. + let iframe = hiddenDoc.createElementNS(HTML_NS, "iframe"); + iframe.setAttribute("src", "chrome://global/content/mozilla.xhtml"); + iframe.addEventListener("load", function onLoad() { + iframe.removeEventListener("load", onLoad, true); + + let browser = iframe.contentDocument.createElementNS(XUL_NS, "browser"); + browser.setAttribute("type", "content"); + browser.setAttribute("disableglobalhistory", "true"); + browser.setAttribute("src", aURL); + + iframe.contentDocument.documentElement.appendChild(browser); + deferred.resolve({frame: iframe, browser: browser}); + }, true); + + hiddenDoc.documentElement.appendChild(iframe); + return deferred.promise; +}; + +/** + * Remove the browser and the iframe. + * + * @param aFrame + * The iframe to dismiss. + * @param aBrowser + * The browser to dismiss. + */ +function destroyHiddenBrowser(aFrame, aBrowser) { + // Dispose of the hidden browser. + aBrowser.remove(); + + // Take care of the frame holding our invisible browser. + if (!Cu.isDeadWrapper(aFrame)) { + aFrame.remove(); + } +}; + +/** + * Test that UITour works when called when no tabs are available (e.g., when using windowless + * browsers). + */ +add_task(function* test_windowless_UITour(){ + // Get the URL for the test page. + let pageURL = getRootDirectory(gTestPath) + "uitour.html"; + + // Allow the URL to use the UITour. + info("Adding UITour permission to the test page."); + let pageURI = Services.io.newURI(pageURL, null, null); + Services.perms.add(pageURI, "uitour", Services.perms.ALLOW_ACTION); + + // UITour's ping will resolve this promise. + let deferredPing = Promise.defer(); + + // Create a windowless browser and test that UITour works in it. + let browserPromise = createHiddenBrowser(pageURL); + browserPromise.then(frameInfo => { + isnot(frameInfo.browser, null, "The browser must exist and not be null."); + + // Load UITour frame script. + frameInfo.browser.messageManager.loadFrameScript( + "chrome://browser/content/content-UITour.js", false); + + // When the page loads, try to use UITour API. + frameInfo.browser.addEventListener("load", function loadListener() { + info("The test page was correctly loaded."); + + frameInfo.browser.removeEventListener("load", loadListener, true); + + // Get a reference to the UITour API. + info("Testing access to the UITour API."); + let contentWindow = Cu.waiveXrays(frameInfo.browser.contentDocument.defaultView); + isnot(contentWindow, null, "The content window must exist and not be null."); + + let uitourAPI = contentWindow.Mozilla.UITour; + + // Test the UITour API with a ping. + uitourAPI.ping(function() { + info("Ping response received from the UITour API."); + + // Make sure to clean up. + destroyHiddenBrowser(frameInfo.frame, frameInfo.browser); + + // Resolve our promise. + deferredPing.resolve(); + }); + }, true); + }); + + // Wait for the UITour ping to complete. + yield deferredPing.promise; +}); From 0ee0c8cd0e9ce824be0d0df32b54c4136edda3fb Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Sun, 22 Feb 2015 12:28:32 +0100 Subject: [PATCH 8/8] Bug 1111022 - Fixes the accessibility test_docload.html test failing with hidden windows. --- .../tests/mochitest/events/test_docload.html | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/accessible/tests/mochitest/events/test_docload.html b/accessible/tests/mochitest/events/test_docload.html index 01abeb32d765..7d3d515430b5 100644 --- a/accessible/tests/mochitest/events/test_docload.html +++ b/accessible/tests/mochitest/events/test_docload.html @@ -15,6 +15,25 @@ src="../role.js"> + + + @@ -174,6 +193,12 @@ var accTree = { role: ROLE_APP_ROOT, children: [ + { + role: ROLE_CHROME_WINDOW + }, + { + role: ROLE_CHROME_WINDOW + }, { role: ROLE_CHROME_WINDOW },