From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- browser/tools/mozscreenshots/.eslintrc.js | 15 ++ browser/tools/mozscreenshots/browser.ini | 12 + .../tools/mozscreenshots/browser_screenshots.js | 16 ++ .../tools/mozscreenshots/controlCenter/browser.ini | 6 + .../controlCenter/browser_controlCenter.js | 16 ++ browser/tools/mozscreenshots/devtools/browser.ini | 6 + .../mozscreenshots/devtools/browser_devtools.js | 16 ++ browser/tools/mozscreenshots/head.js | 53 ++++ browser/tools/mozscreenshots/moz.build | 20 ++ .../mozscreenshots/extension/Makefile.in | 12 + .../mozscreenshots/extension/Screenshot.jsm | 179 +++++++++++++ .../mozscreenshots/extension/TestRunner.jsm | 279 +++++++++++++++++++++ .../mozscreenshots/extension/bootstrap.js | 65 +++++ .../extension/configurations/AppMenu.jsm | 84 +++++++ .../extension/configurations/Buttons.jsm | 85 +++++++ .../extension/configurations/ControlCenter.jsm | 243 ++++++++++++++++++ .../extension/configurations/CustomizeMode.jsm | 61 +++++ .../extension/configurations/DevEdition.jsm | 42 ++++ .../extension/configurations/DevTools.jsm | 59 +++++ .../extension/configurations/LightweightThemes.jsm | 92 +++++++ .../extension/configurations/PermissionPrompts.jsm | 130 ++++++++++ .../extension/configurations/Preferences.jsm | 127 ++++++++++ .../extension/configurations/Tabs.jsm | 152 +++++++++++ .../extension/configurations/TabsInTitlebar.jsm | 38 +++ .../extension/configurations/Toolbars.jsm | 57 +++++ .../extension/configurations/WindowSize.jsm | 68 +++++ .../mozscreenshots/extension/install.rdf | 33 +++ .../mozscreenshots/mozscreenshots/extension/jar.mn | 6 + .../mozscreenshots/extension/lib/black_theme.png | Bin 0 -> 977 bytes .../mozscreenshots/extension/lib/borderify.xpi | Bin 0 -> 1611 bytes .../extension/lib/controlCenter/mixed.html | 11 + .../extension/lib/controlCenter/mixed_active.html | 10 + .../extension/lib/controlCenter/mixed_passive.html | 10 + .../extension/lib/controlCenter/password.html | 13 + .../extension/lib/controlCenter/tracking.html | 10 + .../extension/lib/mozscreenshots-script.js | 13 + .../extension/lib/mozscreenshots-style.css | 28 +++ .../extension/lib/mozscreenshots.html | 36 +++ .../extension/lib/permissionPrompts.html | 30 +++ .../mozscreenshots/extension/lib/robot.png | Bin 0 -> 9817 bytes .../mozscreenshots/extension/lib/white_theme.png | Bin 0 -> 977 bytes .../mozscreenshots/extension/moz.build | 17 ++ .../mozscreenshots/permissionPrompts/browser.ini | 6 + .../permissionPrompts/browser_permissionPrompts.js | 16 ++ .../tools/mozscreenshots/preferences/browser.ini | 6 + .../preferences/browser_preferences.js | 16 ++ browser/tools/mozscreenshots/primaryUI/browser.ini | 6 + .../mozscreenshots/primaryUI/browser_primaryUI.js | 18 ++ 48 files changed, 2218 insertions(+) create mode 100644 browser/tools/mozscreenshots/.eslintrc.js create mode 100644 browser/tools/mozscreenshots/browser.ini create mode 100644 browser/tools/mozscreenshots/browser_screenshots.js create mode 100644 browser/tools/mozscreenshots/controlCenter/browser.ini create mode 100644 browser/tools/mozscreenshots/controlCenter/browser_controlCenter.js create mode 100644 browser/tools/mozscreenshots/devtools/browser.ini create mode 100644 browser/tools/mozscreenshots/devtools/browser_devtools.js create mode 100644 browser/tools/mozscreenshots/head.js create mode 100644 browser/tools/mozscreenshots/moz.build create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/Makefile.in create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/Screenshot.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/bootstrap.js create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/AppMenu.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Buttons.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/ControlCenter.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/CustomizeMode.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/DevEdition.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/DevTools.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/LightweightThemes.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Tabs.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/TabsInTitlebar.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Toolbars.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/configurations/WindowSize.jsm create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/install.rdf create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/jar.mn create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/black_theme.png create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/borderify.xpi create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/controlCenter/mixed.html create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/controlCenter/mixed_active.html create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/controlCenter/mixed_passive.html create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/controlCenter/password.html create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/controlCenter/tracking.html create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/mozscreenshots-script.js create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/mozscreenshots-style.css create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/mozscreenshots.html create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/permissionPrompts.html create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/robot.png create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/lib/white_theme.png create mode 100644 browser/tools/mozscreenshots/mozscreenshots/extension/moz.build create mode 100644 browser/tools/mozscreenshots/permissionPrompts/browser.ini create mode 100644 browser/tools/mozscreenshots/permissionPrompts/browser_permissionPrompts.js create mode 100644 browser/tools/mozscreenshots/preferences/browser.ini create mode 100644 browser/tools/mozscreenshots/preferences/browser_preferences.js create mode 100644 browser/tools/mozscreenshots/primaryUI/browser.ini create mode 100644 browser/tools/mozscreenshots/primaryUI/browser_primaryUI.js (limited to 'browser/tools') diff --git a/browser/tools/mozscreenshots/.eslintrc.js b/browser/tools/mozscreenshots/.eslintrc.js new file mode 100644 index 000000000..55b05398e --- /dev/null +++ b/browser/tools/mozscreenshots/.eslintrc.js @@ -0,0 +1,15 @@ +"use strict"; + +module.exports = { // eslint-disable-line no-undef + "extends": [ + "../../../testing/mochitest/browser.eslintrc.js" + ], + + "rules": { + "no-unused-vars": ["error", { + "vars": "all", + "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$", + "args": "none" + }] + } +}; diff --git a/browser/tools/mozscreenshots/browser.ini b/browser/tools/mozscreenshots/browser.ini new file mode 100644 index 000000000..e72a3acb4 --- /dev/null +++ b/browser/tools/mozscreenshots/browser.ini @@ -0,0 +1,12 @@ +[DEFAULT] +subsuite = screenshots +support-files = + head.js + mozscreenshots/extension/lib/permissionPrompts.html + mozscreenshots/extension/lib/controlCenter/password.html + mozscreenshots/extension/lib/controlCenter/mixed.html + mozscreenshots/extension/lib/controlCenter/mixed_active.html + mozscreenshots/extension/lib/controlCenter/mixed_passive.html + mozscreenshots/extension/lib/borderify.xpi + +[browser_screenshots.js] diff --git a/browser/tools/mozscreenshots/browser_screenshots.js b/browser/tools/mozscreenshots/browser_screenshots.js new file mode 100644 index 000000000..502f90fec --- /dev/null +++ b/browser/tools/mozscreenshots/browser_screenshots.js @@ -0,0 +1,16 @@ +/* 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"; + +add_task(function* capture() { + let setsEnv = env.get("MOZSCREENSHOTS_SETS"); + if (!setsEnv) { + ok(true, "MOZSCREENSHOTS_SETS wasn't specified so there's nothing to capture"); + return; + } + + let sets = setsEnv.trim().split(","); + yield TestRunner.start(sets); +}); diff --git a/browser/tools/mozscreenshots/controlCenter/browser.ini b/browser/tools/mozscreenshots/controlCenter/browser.ini new file mode 100644 index 000000000..f5b084edc --- /dev/null +++ b/browser/tools/mozscreenshots/controlCenter/browser.ini @@ -0,0 +1,6 @@ +[DEFAULT] +subsuite = screenshots +support-files = + ../head.js + +[browser_controlCenter.js] diff --git a/browser/tools/mozscreenshots/controlCenter/browser_controlCenter.js b/browser/tools/mozscreenshots/controlCenter/browser_controlCenter.js new file mode 100644 index 000000000..7e1ffb125 --- /dev/null +++ b/browser/tools/mozscreenshots/controlCenter/browser_controlCenter.js @@ -0,0 +1,16 @@ +/* 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/. */ + +/* import-globals-from ../head.js */ + +"use strict"; + +add_task(function* capture() { + if (!shouldCapture()) { + return; + } + let sets = ["LightweightThemes", "ControlCenter"]; + + yield TestRunner.start(sets, "controlCenter"); +}); diff --git a/browser/tools/mozscreenshots/devtools/browser.ini b/browser/tools/mozscreenshots/devtools/browser.ini new file mode 100644 index 000000000..e4fa0a988 --- /dev/null +++ b/browser/tools/mozscreenshots/devtools/browser.ini @@ -0,0 +1,6 @@ +[DEFAULT] +subsuite = screenshots +support-files = + ../head.js + +[browser_devtools.js] diff --git a/browser/tools/mozscreenshots/devtools/browser_devtools.js b/browser/tools/mozscreenshots/devtools/browser_devtools.js new file mode 100644 index 000000000..a47548958 --- /dev/null +++ b/browser/tools/mozscreenshots/devtools/browser_devtools.js @@ -0,0 +1,16 @@ +/* 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/. */ + +/* import-globals-from ../head.js */ + +"use strict"; + +add_task(function* capture() { + if (!shouldCapture()) { + return; + } + let sets = ["DevTools"]; + + yield TestRunner.start(sets, "devtools"); +}); diff --git a/browser/tools/mozscreenshots/head.js b/browser/tools/mozscreenshots/head.js new file mode 100644 index 000000000..c1702ab08 --- /dev/null +++ b/browser/tools/mozscreenshots/head.js @@ -0,0 +1,53 @@ +/* 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/. */ + +/* exported TestRunner, shouldCapture */ + +"use strict"; + +const {AddonWatcher} = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}); +const chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry); +const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); +const EXTENSION_DIR = "chrome://mochitests/content/extensions/mozscreenshots/browser/"; + +let TestRunner; + +function* setup() { + requestLongerTimeout(10); + + info("installing extension temporarily"); + let chromeURL = Services.io.newURI(EXTENSION_DIR, null, null); + let dir = chromeRegistry.convertChromeURL(chromeURL).QueryInterface(Ci.nsIFileURL).file; + yield AddonManager.installTemporaryAddon(dir); + + info("Checking for mozscreenshots extension"); + return new Promise((resolve) => { + AddonManager.getAddonByID("mozscreenshots@mozilla.org", function(aAddon) { + isnot(aAddon, null, "The mozscreenshots extension should be installed"); + AddonWatcher.ignoreAddonPermanently(aAddon.id); + TestRunner = Cu.import("chrome://mozscreenshots/content/TestRunner.jsm", {}).TestRunner; + resolve(); + }); + }); +} + +function shouldCapture() { + // Try pushes only capture in browser_screenshots.js with MOZSCREENSHOTS_SETS. + if (env.get("MOZSCREENSHOTS_SETS")) { + ok(true, "MOZSCREENSHOTS_SETS was specified so only capture what was " + + "requested (in browser_screenshots.js)"); + return false; + } + + // Automation isn't able to schedule test jobs to only run on nightlies so we handle it here + // (see also: bug 1116275). + let capture = AppConstants.MOZ_UPDATE_CHANNEL == "nightly" || + AppConstants.SOURCE_REVISION_URL == ""; + if (!capture) { + ok(true, "Capturing is disabled for this MOZ_UPDATE_CHANNEL or REPO"); + } + return capture; +} + +add_task(setup); diff --git a/browser/tools/mozscreenshots/moz.build b/browser/tools/mozscreenshots/moz.build new file mode 100644 index 000000000..0c8b98b6a --- /dev/null +++ b/browser/tools/mozscreenshots/moz.build @@ -0,0 +1,20 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +BROWSER_CHROME_MANIFESTS += [ + # Each test is in it's own directory so it gets run in a clean profile with + # run-by-dir. + 'browser.ini', + 'controlCenter/browser.ini', + 'devtools/browser.ini', + 'permissionPrompts/browser.ini', + 'preferences/browser.ini', + 'primaryUI/browser.ini', +] + +TEST_DIRS += [ + 'mozscreenshots/extension', +] diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/Makefile.in b/browser/tools/mozscreenshots/mozscreenshots/extension/Makefile.in new file mode 100644 index 000000000..b0004aaa7 --- /dev/null +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/Makefile.in @@ -0,0 +1,12 @@ +# 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/. + +TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions +GENERATED_DIRS = $(TEST_EXTENSIONS_DIR) +XPI_PKGNAME = mozscreenshots@mozilla.org + +include $(topsrcdir)/config/rules.mk + +libs:: + (cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -) diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/Screenshot.jsm b/browser/tools/mozscreenshots/mozscreenshots/extension/Screenshot.jsm new file mode 100644 index 000000000..c43514e94 --- /dev/null +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/Screenshot.jsm @@ -0,0 +1,179 @@ +/* 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 = ["Screenshot"]; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Timer.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); + +// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref. +// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error". +const PREF_LOG_LEVEL = "extensions.mozscreenshots@mozilla.org.loglevel"; +XPCOMUtils.defineLazyGetter(this, "log", () => { + let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI; + let consoleOptions = { + maxLogLevel: "info", + maxLogLevelPref: PREF_LOG_LEVEL, + prefix: "mozscreenshots", + }; + return new ConsoleAPI(consoleOptions); +}); + +this.Screenshot = { + _extensionPath: null, + _path: null, + _imagePrefix: "", + _imageExtension: ".png", + _screenshotFunction: null, + + init(path, extensionPath, imagePrefix = "") { + this._path = path; + + let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + dir.initWithPath(this._path); + if (!dir.exists()) { + dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + } + + this._extensionPath = extensionPath; + this._imagePrefix = imagePrefix; + switch (Services.appinfo.OS) { + case "WINNT": + this._screenshotFunction = this._screenshotWindows; + break; + case "Darwin": + this._screenshotFunction = this._screenshotOSX; + break; + case "Linux": + this._screenshotFunction = this._screenshotLinux; + break; + default: + throw new Error("Unsupported operating system"); + } + }, + + _buildImagePath(baseName) { + return OS.Path.join(this._path, this._imagePrefix + baseName + this._imageExtension); + }, + + // Capture the whole screen using an external application. + captureExternal(filename) { + let imagePath = this._buildImagePath(filename); + return this._screenshotFunction(imagePath).then(() => { + log.debug("saved screenshot: " + filename); + }); + }, + + // helpers + + _screenshotWindows(filename) { + return new Promise((resolve, reject) => { + let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile); + exe.append("screenshot.exe"); + if (!exe.exists()) { + exe = Services.dirsvc.get("CurWorkD", Ci.nsIFile).parent; + exe.append("bin"); + exe.append("screenshot.exe"); + } + let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); + process.init(exe); + + let args = [filename]; + process.runAsync(args, args.length, this._processObserver(resolve, reject)); + }); + }, + + _screenshotOSX: Task.async(function*(filename) { + let screencapture = (windowID = null) => { + return new Promise((resolve, reject) => { + // Get the screencapture executable + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + file.initWithPath("/usr/sbin/screencapture"); + + let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); + process.init(file); + + // Run the process. + let args = ["-x", "-t", "png"]; + // Darwin version number for OS X 10.6 is 10.x + if (windowID && Services.sysinfo.getProperty("version").indexOf("10.") !== 0) { + // Capture only that window on 10.7+ + args.push("-l"); + args.push(windowID); + } + args.push(filename); + process.runAsync(args, args.length, this._processObserver(resolve, reject)); + }); + }; + + function readWindowID() { + let decoder = new TextDecoder(); + let promise = OS.File.read("/tmp/mozscreenshots-windowid"); + return promise.then(function onSuccess(array) { + return decoder.decode(array); + }); + } + + let promiseWindowID = () => { + return new Promise((resolve, reject) => { + // Get the window ID of the application (assuming its front-most) + let osascript = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + osascript.initWithPath("/bin/bash"); + + let osascriptP = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); + osascriptP.init(osascript); + let osaArgs = ["-c", "/usr/bin/osascript -e 'tell application (path to frontmost application as text) to set winID to id of window 1' > /tmp/mozscreenshots-windowid"]; + osascriptP.runAsync(osaArgs, osaArgs.length, this._processObserver(resolve, reject)); + }); + }; + + yield promiseWindowID(); + let windowID = yield readWindowID(); + yield screencapture(windowID); + }), + + _screenshotLinux(filename) { + return new Promise((resolve, reject) => { + let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile); + exe.append("screentopng"); + if (!exe.exists()) { + exe = Services.dirsvc.get("CurWorkD", Ci.nsIFile).parent; + exe.append("bin"); + exe.append("screentopng"); + } + let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); + process.init(exe); + + let args = [filename]; + process.runAsync(args, args.length, this._processObserver(resolve, reject)); + }); + }, + + _processObserver(resolve, reject) { + return { + observe(subject, topic, data) { + switch (topic) { + case "process-finished": + try { + // Wait 1s after process to resolve + setTimeout(resolve, 1000); + } catch (ex) { + reject(ex); + } + break; + default: + reject(topic); + break; + } + }, + }; + }, +}; diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm b/browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm new file mode 100644 index 000000000..557b867b9 --- /dev/null +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm @@ -0,0 +1,279 @@ +/* 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 = ["TestRunner"]; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); +const APPLY_CONFIG_TIMEOUT_MS = 60 * 1000; +const HOME_PAGE = "chrome://mozscreenshots/content/lib/mozscreenshots.html"; + +Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Timer.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils", + "resource://testing-common/BrowserTestUtils.jsm"); + +Cu.import("chrome://mozscreenshots/content/Screenshot.jsm"); + +// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref. +// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error". +const PREF_LOG_LEVEL = "extensions.mozscreenshots@mozilla.org.loglevel"; +XPCOMUtils.defineLazyGetter(this, "log", () => { + let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI; + let consoleOptions = { + maxLogLevel: "info", + maxLogLevelPref: PREF_LOG_LEVEL, + prefix: "mozscreenshots", + }; + return new ConsoleAPI(consoleOptions); +}); + +this.TestRunner = { + combos: null, + completedCombos: 0, + currentComboIndex: 0, + _lastCombo: null, + _libDir: null, + + init(extensionPath) { + log.debug("init"); + this._extensionPath = extensionPath; + }, + + /** + * Load specified sets, execute all combinations of them, and capture screenshots. + */ + start: Task.async(function*(setNames, jobName = null) { + let subDirs = ["mozscreenshots", + (new Date()).toISOString().replace(/:/g, "-") + "_" + Services.appinfo.OS]; + let screenshotPath = FileUtils.getFile("TmpD", subDirs).path; + + const MOZ_UPLOAD_DIR = env.get("MOZ_UPLOAD_DIR"); + if (MOZ_UPLOAD_DIR) { + screenshotPath = MOZ_UPLOAD_DIR; + } + + log.info("Saving screenshots to:", screenshotPath); + + let screenshotPrefix = Services.appinfo.appBuildID; + if (jobName) { + screenshotPrefix += "-" + jobName; + } + screenshotPrefix += "_"; + Screenshot.init(screenshotPath, this._extensionPath, screenshotPrefix); + this._libDir = this._extensionPath.QueryInterface(Ci.nsIFileURL).file.clone(); + this._libDir.append("chrome"); + this._libDir.append("mozscreenshots"); + this._libDir.append("lib"); + + let sets = this.loadSets(setNames); + + log.info(sets.length + " sets:", setNames); + this.combos = new LazyProduct(sets); + log.info(this.combos.length + " combinations"); + + this.currentComboIndex = this.completedCombos = 0; + this._lastCombo = null; + + // Setup some prefs + Services.prefs.setCharPref("browser.aboutHomeSnippets.updateUrl", + "data:text/html;charset=utf-8,Generated by mozscreenshots"); + Services.prefs.setCharPref("extensions.ui.lastCategory", "addons://list/extension"); + // Don't let the caret blink since it causes false positives for image diffs + Services.prefs.setIntPref("ui.caretBlinkTime", -1); + + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + let selectedBrowser = browserWindow.gBrowser.selectedBrowser; + yield BrowserTestUtils.loadURI(selectedBrowser, HOME_PAGE); + yield BrowserTestUtils.browserLoaded(selectedBrowser); + + for (let i = 0; i < this.combos.length; i++) { + this.currentComboIndex = i; + yield this._performCombo(this.combos.item(this.currentComboIndex)); + } + + log.info("Done: Completed " + this.completedCombos + " out of " + + this.combos.length + " configurations."); + this.cleanup(); + }), + + /** + * Load sets of configurations from JSMs. + * @param {String[]} setNames - array of set names (e.g. ["Tabs", "WindowSize"]. + * @return {Object[]} Array of sets containing `name` and `configurations` properties. + */ + loadSets(setNames) { + let sets = []; + for (let setName of setNames) { + try { + let imported = {}; + Cu.import("chrome://mozscreenshots/content/configurations/" + setName + ".jsm", + imported); + imported[setName].init(this._libDir); + let configurationNames = Object.keys(imported[setName].configurations); + if (!configurationNames.length) { + throw new Error(setName + " has no configurations for this environment"); + } + for (let config of configurationNames) { + // Automatically set the name property of the configuration object to + // its name from the configuration object. + imported[setName].configurations[config].name = config; + } + sets.push(imported[setName].configurations); + } catch (ex) { + log.error("Error loading set: " + setName); + log.error(ex); + throw ex; + } + } + return sets; + }, + + cleanup() { + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + let gBrowser = browserWindow.gBrowser; + while (gBrowser.tabs.length > 1) { + gBrowser.removeTab(gBrowser.selectedTab, {animate: false}); + } + gBrowser.unpinTab(gBrowser.selectedTab); + gBrowser.selectedBrowser.loadURI("data:text/html;charset=utf-8,

Done!"); + browserWindow.restore(); + }, + + // helpers + + _performCombo: function*(combo) { + let paddedComboIndex = padLeft(this.currentComboIndex + 1, String(this.combos.length).length); + log.info("Combination " + paddedComboIndex + "/" + this.combos.length + ": " + + this._comboName(combo).substring(1)); + + function changeConfig(config) { + log.debug("calling " + config.name); + let applyPromise = Promise.resolve(config.applyConfig()); + let timeoutPromise = new Promise((resolve, reject) => { + setTimeout(reject, APPLY_CONFIG_TIMEOUT_MS, "Timed out"); + }); + log.debug("called " + config.name); + // Add a default timeout of 500ms to avoid conflicts when configurations + // try to apply at the same time. e.g WindowSize and TabsInTitlebar + return Promise.race([applyPromise, timeoutPromise]).then(() => { + return new Promise((resolve) => { + setTimeout(resolve, 500); + }); + }); + } + + try { + // First go through and actually apply all of the configs + for (let i = 0; i < combo.length; i++) { + let config = combo[i]; + if (!this._lastCombo || config !== this._lastCombo[i]) { + log.debug("promising", config.name); + yield changeConfig(config); + } + } + + // Update the lastCombo since it's now been applied regardless of whether it's accepted below. + log.debug("fulfilled all applyConfig so setting lastCombo."); + this._lastCombo = combo; + + // Then ask configs if the current setup is valid. We can't can do this in + // the applyConfig methods of the config since it doesn't know what configs + // later in the loop will do that may invalidate the combo. + for (let i = 0; i < combo.length; i++) { + let config = combo[i]; + // A configuration can specify an optional verifyConfig method to indicate + // if the current config is valid for a screenshot. This gets called even + // if the this config was used in the lastCombo since another config may + // have invalidated it. + if (config.verifyConfig) { + log.debug("checking if the combo is valid with", config.name); + yield config.verifyConfig(); + } + } + } catch (ex) { + log.warn("\tskipped configuration: " + ex); + // Don't set lastCombo here so that we properly know which configurations + // need to be applied since the last screenshot + + // Return so we don't take a screenshot. + return; + } + + yield this._onConfigurationReady(combo); + }, + + _onConfigurationReady(combo) { + let delayedScreenshot = () => { + let filename = padLeft(this.currentComboIndex + 1, + String(this.combos.length).length) + this._comboName(combo); + return Screenshot.captureExternal(filename) + .then(() => { + this.completedCombos++; + }); + }; + + log.debug("_onConfigurationReady"); + return Task.spawn(delayedScreenshot); + }, + + _comboName(combo) { + return combo.reduce(function(a, b) { + return a + "_" + b.name; + }, ""); + }, +}; + +/** + * Helper to lazily compute the Cartesian product of all of the sets of configurations. + **/ +function LazyProduct(sets) { + /** + * An entry for each set with the value being: + * [the number of permutations of the sets with lower index, + * the number of items in the set at the index] + */ + this.sets = sets; + this.lookupTable = []; + let combinations = 1; + for (let i = this.sets.length - 1; i >= 0; i--) { + let set = this.sets[i]; + let setLength = Object.keys(set).length; + this.lookupTable[i] = [combinations, setLength]; + combinations *= setLength; + } +} +LazyProduct.prototype = { + get length() { + let last = this.lookupTable[0]; + if (!last) + return 0; + return last[0] * last[1]; + }, + + item(n) { + // For set i, get the item from the set with the floored value of + // (n / the number of permutations of the sets already chosen from) modulo the length of set i + let result = []; + for (let i = this.sets.length - 1; i >= 0; i--) { + let priorCombinations = this.lookupTable[i][0]; + let setLength = this.lookupTable[i][1]; + let keyIndex = Math.floor(n / priorCombinations) % setLength; + let keys = Object.keys(this.sets[i]); + result[i] = this.sets[i][keys[keyIndex]]; + } + return result; + }, +}; + +function padLeft(number, width, padding = "0") { + return padding.repeat(Math.max(0, width - String(number).length)) + number; +} diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/bootstrap.js b/browser/tools/mozscreenshots/mozscreenshots/extension/bootstrap.js new file mode 100644 index 000000000..c7e238606 --- /dev/null +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/bootstrap.js @@ -0,0 +1,65 @@ +/* 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/. */ +/* +#if 0 +Workaround a build system bug where this file doesn't get packaged if not pre-processed. +#endif +*/ + +/* exported install, uninstall, startup, shutdown */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/AddonManager.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Timer.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "TestRunner", + "chrome://mozscreenshots/content/TestRunner.jsm"); + +function install(data, reason) { + if (!isAppSupported()) { + uninstallExtension(data); + return; + } + + AddonManager.getAddonByID(data.id, function(addon) { + // Enable on install in case the user disabled a prior version + if (addon) { + addon.userDisabled = false; + } + }); +} + +function startup(data, reason) { + if (!isAppSupported()) { + uninstallExtension(data); + return; + } + + AddonManager.getAddonByID(data.id, function(addon) { + let extensionPath = addon.getResourceURI(); + TestRunner.init(extensionPath); + }); +} + +function shutdown(data, reason) { } + +function uninstall(data, reason) { } + +/** + * @return boolean whether the test suite applies to the application. + */ +function isAppSupported() { + return true; +} + +function uninstallExtension(data) { + AddonManager.getAddonByID(data.id, function(addon) { + addon.uninstall(); + }); +} diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/AppMenu.jsm b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/AppMenu.jsm new file mode 100644 index 000000000..5dbb2c1e2 --- /dev/null +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/AppMenu.jsm @@ -0,0 +1,84 @@ +/* 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 = ["AppMenu"]; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + +this.AppMenu = { + + init(libDir) {}, + + configurations: { + appMenuClosed: { + applyConfig: Task.async(function*() { + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + browserWindow.PanelUI.hide(); + }), + }, + + appMenuMainView: { + applyConfig() { + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + let promise = browserWindow.PanelUI.show(); + browserWindow.PanelUI.showMainView(); + return promise; + }, + }, + + appMenuHistorySubview: { + applyConfig() { + // History has a footer + if (isCustomizing()) { + return Promise.reject("Can't show subviews while customizing"); + } + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + let promise = browserWindow.PanelUI.show(); + return promise.then(() => { + browserWindow.PanelUI.showMainView(); + browserWindow.document.getElementById("history-panelmenu").click(); + }); + }, + + verifyConfig: verifyConfigHelper, + }, + + appMenuHelpSubview: { + applyConfig() { + if (isCustomizing()) { + return Promise.reject("Can't show subviews while customizing"); + } + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + let promise = browserWindow.PanelUI.show(); + return promise.then(() => { + browserWindow.PanelUI.showMainView(); + browserWindow.document.getElementById("PanelUI-help").click(); + }); + }, + + verifyConfig: verifyConfigHelper, + }, + + }, +}; + +function verifyConfigHelper() { + if (isCustomizing()) { + return Promise.reject("AppMenu verifyConfigHelper"); + } + return Promise.resolve("AppMenu verifyConfigHelper"); +} + +function isCustomizing() { + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + if (browserWindow.document.documentElement.hasAttribute("customizing")) { + return true; + } + return false; +} diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Buttons.jsm b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Buttons.jsm new file mode 100644 index 000000000..97d8354d5 --- /dev/null +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Buttons.jsm @@ -0,0 +1,85 @@ +/* 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 = ["Buttons"]; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource:///modules/CustomizableUI.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + +this.Buttons = { + + init(libDir) { + createWidget(); + }, + + configurations: { + navBarButtons: { + applyConfig: Task.async(() => { + CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_NAVBAR); + }), + }, + + tabsToolbarButtons: { + applyConfig: Task.async(() => { + CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_TABSTRIP); + }), + }, + + menuPanelButtons: { + applyConfig: Task.async(() => { + CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_PANEL); + }), + + verifyConfig() { + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + if (browserWindow.PanelUI.panel.state == "closed") { + return Promise.reject("The button isn't shown when the panel isn't open."); + } + return Promise.resolve("menuPanelButtons.verifyConfig"); + }, + }, + + custPaletteButtons: { + applyConfig: Task.async(() => { + CustomizableUI.removeWidgetFromArea("screenshot-widget"); + }), + + verifyConfig() { + let browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + if (browserWindow.document.documentElement.getAttribute("customizing") != "true") { + return Promise.reject("The button isn't shown when we're not in customize mode."); + } + return Promise.resolve("custPaletteButtons.verifyConfig"); + }, + }, + }, +}; + +function createWidget() { + let id = "screenshot-widget"; + let spec = { + id: id, + label: "My Button", + removable: true, + tooltiptext: "", + type: "button", + }; + CustomizableUI.createWidget(spec); + + // Append a