From baa5e3ef1c8dee9fbffe82fd57b17e2c1558641b Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 29 Feb 2016 17:42:31 -0600 Subject: [PATCH 01/31] Bug 1252287 - Repair gDevTools shim to use Services.jsm. r=ochameau MozReview-Commit-ID: BKmaGubgLAd --- devtools/client/shims/gDevTools.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/client/shims/gDevTools.jsm b/devtools/client/shims/gDevTools.jsm index 12cf7edf30a..0bdf45633f6 100644 --- a/devtools/client/shims/gDevTools.jsm +++ b/devtools/client/shims/gDevTools.jsm @@ -11,7 +11,7 @@ const Cu = Components.utils; -const Services = require("Services"); +const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); const WARNING_PREF = "devtools.migration.warnings"; if (Services.prefs.getBoolPref(WARNING_PREF)) { From c49e4ff857dce20866aeb3ae0c3dfe616fe3e763 Mon Sep 17 00:00:00 2001 From: James Long Date: Tue, 1 Mar 2016 11:43:08 -0500 Subject: [PATCH 02/31] Bug 1251965 - fix "remove other breakpoints" command in debugger r=me --- .../debugger/content/actions/breakpoints.js | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/devtools/client/debugger/content/actions/breakpoints.js b/devtools/client/debugger/content/actions/breakpoints.js index 85b4bf30a22..e96eae133db 100644 --- a/devtools/client/debugger/content/actions/breakpoints.js +++ b/devtools/client/debugger/content/actions/breakpoints.js @@ -100,13 +100,24 @@ function _removeOrDisableBreakpoint(location, isDisabled) { } const bpClient = getBreakpointClient(bp.actor); - - return dispatch({ + const action = { type: constants.REMOVE_BREAKPOINT, breakpoint: bp, - disabled: isDisabled, - [PROMISE]: bpClient.remove() - }); + disabled: isDisabled + }; + + // If the breakpoint is already disabled, we don't need to remove + // it from the server. We just need to dispatch an action + // simulating a successful server request to remove it, and it + // will be removed completely from the state. + if(!bp.disabled) { + return dispatch(Object.assign({}, action, { + [PROMISE]: bpClient.remove() + })); + } + else { + return dispatch(Object.assign({}, action, { status: "done" })); + } } } From 64abed4d8ffa98a040dceadeb1dec472014b868e Mon Sep 17 00:00:00 2001 From: Michael Kaply Date: Tue, 1 Mar 2016 10:58:04 -0600 Subject: [PATCH 03/31] Bug 1252466 - Switch distribution.js to use Preferences.jsm; r=mixedpuppy --- browser/components/distribution.js | 45 ++++++------------- .../components/tests/unit/distribution.ini | 1 + .../tests/unit/test_distribution.js | 4 ++ 3 files changed, 18 insertions(+), 32 deletions(-) diff --git a/browser/components/distribution.js b/browser/components/distribution.js index 778dcfcc890..19f278f4208 100644 --- a/browser/components/distribution.js +++ b/browser/components/distribution.js @@ -15,6 +15,7 @@ const DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC = Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Preferences.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); @@ -322,27 +323,24 @@ DistributionCustomizer.prototype = { if (!(globalPrefs["id"] && globalPrefs["version"] && globalPrefs["about"])) return this._checkCustomizationComplete(); - let defaults = this._prefSvc.getDefaultBranch(null); + let defaults = new Preferences({defaultBranch: true}); // Global really contains info we set as prefs. They're only // separate because they are "special" (read: required) - defaults.setCharPref("distribution.id", this._ini.getString("Global", "id")); - defaults.setCharPref("distribution.version", - this._ini.getString("Global", "version")); + defaults.set("distribution.id", this._ini.getString("Global", "id")); + defaults.set("distribution.version", this._ini.getString("Global", "version")); - let partnerAbout = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); + let partnerAbout; try { if (globalPrefs["about." + this._locale]) { - partnerAbout.data = this._ini.getString("Global", "about." + this._locale); + partnerAbout = this._ini.getString("Global", "about." + this._locale); } else if (globalPrefs["about." + this._language]) { - partnerAbout.data = this._ini.getString("Global", "about." + this._language); + partnerAbout = this._ini.getString("Global", "about." + this._language); } else { - partnerAbout.data = this._ini.getString("Global", "about"); + partnerAbout = this._ini.getString("Global", "about"); } - defaults.setComplexValue("distribution.about", - Ci.nsISupportsString, partnerAbout); + defaults.set("distribution.about", partnerAbout); } catch (e) { /* ignore bad prefs due to bug 895473 and move on */ Cu.reportError(e); @@ -352,28 +350,11 @@ DistributionCustomizer.prototype = { for (let key of enumerate(this._ini.getKeys("Preferences"))) { try { let value = parseValue(this._ini.getString("Preferences", key)); - switch (typeof value) { - case "boolean": - defaults.setBoolPref(key, value); - break; - case "number": - defaults.setIntPref(key, value); - break; - case "string": - defaults.setCharPref(key, value); - break; - case "undefined": - defaults.setCharPref(key, value); - break; - } + Preferences.set(key, value); } catch (e) { /* ignore bad prefs and move on */ } } } - // We eval() the localizable prefs as well (even though they'll - // always get set as a string) to keep the INI format consistent: - // string prefs always need to be in quotes - let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"]. createInstance(Ci.nsIPrefLocalizedString); @@ -385,7 +366,7 @@ DistributionCustomizer.prototype = { let value = parseValue(this._ini.getString("LocalizablePreferences-" + this._locale, key)); if (value !== undefined) { localizedStr.data = "data:text/plain," + key + "=" + value; - defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); + defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); } usedLocalizablePreferences.push(key); } catch (e) { /* ignore bad prefs and move on */ } @@ -401,7 +382,7 @@ DistributionCustomizer.prototype = { let value = parseValue(this._ini.getString("LocalizablePreferences-" + this._language, key)); if (value !== undefined) { localizedStr.data = "data:text/plain," + key + "=" + value; - defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); + defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); } usedLocalizablePreferences.push(key); } catch (e) { /* ignore bad prefs and move on */ } @@ -419,7 +400,7 @@ DistributionCustomizer.prototype = { value = value.replace(/%LOCALE%/g, this._locale); value = value.replace(/%LANGUAGE%/g, this._language); localizedStr.data = "data:text/plain," + key + "=" + value; - defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); + defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr); } } catch (e) { /* ignore bad prefs and move on */ } } diff --git a/browser/components/tests/unit/distribution.ini b/browser/components/tests/unit/distribution.ini index e6576f5d62d..618fa1ebfa7 100644 --- a/browser/components/tests/unit/distribution.ini +++ b/browser/components/tests/unit/distribution.ini @@ -5,6 +5,7 @@ id=disttest version=1.0 about=Test distribution file +about.en-US=Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè [Preferences] distribution.test.string="Test String" diff --git a/browser/components/tests/unit/test_distribution.js b/browser/components/tests/unit/test_distribution.js index 29da76b8c2e..efb2e6b2a71 100644 --- a/browser/components/tests/unit/test_distribution.js +++ b/browser/components/tests/unit/test_distribution.js @@ -62,6 +62,10 @@ add_task(function* () { let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver) glue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_DISTRIBUTION_CUSTOMIZATION); + Assert.equal(Services.prefs.getCharPref("distribution.id"), "disttest"); + Assert.equal(Services.prefs.getCharPref("distribution.version"), "1.0"); + Assert.equal(Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString).data, "Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè"); + Assert.equal(Services.prefs.getCharPref("distribution.test.string"), "Test String"); Assert.equal(Services.prefs.getCharPref("distribution.test.string.noquotes"), "Test String"); Assert.equal(Services.prefs.getIntPref("distribution.test.int"), 777); From 7f5c1e2f4bcc364218fbc932246f3ecdc0bc5a96 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Mon, 29 Feb 2016 19:34:49 -0800 Subject: [PATCH 04/31] Bug 1214955: [webext] Automatically localize all localizable manifest properties. r=billm MozReview-Commit-ID: 2kvYT44NIE8 --- .../extensions/ext-browserAction.js | 13 ++--- .../components/extensions/ext-pageAction.js | 13 ++--- browser/components/extensions/ext-utils.js | 9 +--- .../extensions/schemas/browser_action.json | 18 +++++-- .../extensions/schemas/page_action.json | 18 +++++-- .../browser_ext_browserAction_context.js | 23 +++++++- .../browser/browser_ext_pageAction_context.js | 22 +++++++- toolkit/components/extensions/Extension.jsm | 18 +++++-- toolkit/components/extensions/Schemas.jsm | 52 ++++++++++++++----- .../extensions/ext-backgroundPage.js | 9 +--- .../extensions/schemas/manifest.json | 21 ++++++-- .../test/xpcshell/test_ext_schemas.js | 32 ++++++++++++ .../test/xpcshell/test_locale_data.js | 45 ++++++++++------ .../extensions/internal/XPIProvider.jsm | 12 +++-- 14 files changed, 223 insertions(+), 82 deletions(-) diff --git a/browser/components/extensions/ext-browserAction.js b/browser/components/extensions/ext-browserAction.js index 718eccd143f..4f5c84ef087 100644 --- a/browser/components/extensions/ext-browserAction.js +++ b/browser/components/extensions/ext-browserAction.js @@ -33,20 +33,13 @@ function BrowserAction(options, extension) { this.tabManager = TabManager.for(extension); - let title = extension.localize(options.default_title || ""); - let popup = extension.localize(options.default_popup || ""); - if (popup) { - popup = extension.baseURI.resolve(popup); - } - this.defaults = { enabled: true, - title: title || extension.name, + title: options.default_title || extension.name, badgeText: "", badgeBackgroundColor: null, - icon: IconDetails.normalize({path: options.default_icon}, extension, - null, true), - popup: popup, + icon: IconDetails.normalize({path: options.default_icon}, extension), + popup: options.default_popup || "", }; this.tabContext = new TabContext(tab => Object.create(this.defaults), diff --git a/browser/components/extensions/ext-pageAction.js b/browser/components/extensions/ext-pageAction.js index 138069999ad..29876c2ea1e 100644 --- a/browser/components/extensions/ext-pageAction.js +++ b/browser/components/extensions/ext-pageAction.js @@ -19,18 +19,11 @@ function PageAction(options, extension) { this.tabManager = TabManager.for(extension); - let title = extension.localize(options.default_title || ""); - let popup = extension.localize(options.default_popup || ""); - if (popup) { - popup = extension.baseURI.resolve(popup); - } - this.defaults = { show: false, - title: title || extension.name, - icon: IconDetails.normalize({path: options.default_icon}, extension, - null, true), - popup: popup && extension.baseURI.resolve(popup), + title: options.default_title || extension.name, + icon: IconDetails.normalize({path: options.default_icon}, extension), + popup: options.default_popup || "", }; this.tabContext = new TabContext(tab => Object.create(this.defaults), diff --git a/browser/components/extensions/ext-utils.js b/browser/components/extensions/ext-utils.js index fb3d749291d..480054e1325 100644 --- a/browser/components/extensions/ext-utils.js +++ b/browser/components/extensions/ext-utils.js @@ -36,7 +36,7 @@ global.IconDetails = { // // If no context is specified, instead of throwing an error, this // function simply logs a warning message. - normalize(details, extension, context = null, localize = false) { + normalize(details, extension, context = null) { let result = {}; try { @@ -73,12 +73,7 @@ global.IconDetails = { throw new Error(`Invalid icon size ${size}, must be an integer`); } - let url = path[size]; - if (localize) { - url = extension.localize(url); - } - - url = baseURI.resolve(path[size]); + let url = baseURI.resolve(path[size]); // The Chrome documentation specifies these parameters as // relative paths. We currently accept absolute URLs as well, diff --git a/browser/components/extensions/schemas/browser_action.json b/browser/components/extensions/schemas/browser_action.json index b8047d86840..c73643c753a 100644 --- a/browser/components/extensions/schemas/browser_action.json +++ b/browser/components/extensions/schemas/browser_action.json @@ -12,9 +12,21 @@ "browser_action": { "type": "object", "properties": { - "default_title": { "type": "string", "optional": true }, - "default_icon": { "$ref": "IconPath", "optional": true }, - "default_popup": { "type": "string", "format": "relativeUrl", "optional": true } + "default_title": { + "type": "string", + "optional": true, + "preprocess": "localize" + }, + "default_icon": { + "$ref": "IconPath", + "optional": true + }, + "default_popup": { + "type": "string", + "format": "relativeUrl", + "optional": true, + "preprocess": "localize" + } }, "optional": true } diff --git a/browser/components/extensions/schemas/page_action.json b/browser/components/extensions/schemas/page_action.json index de4de3e4e9d..62ac18b2936 100644 --- a/browser/components/extensions/schemas/page_action.json +++ b/browser/components/extensions/schemas/page_action.json @@ -12,9 +12,21 @@ "page_action": { "type": "object", "properties": { - "default_title": { "type": "string", "optional": true }, - "default_icon": { "$ref": "IconPath", "optional": true }, - "default_popup": { "type": "string", "format": "relativeUrl", "optional": true } + "default_title": { + "type": "string", + "optional": true, + "preprocess": "localize" + }, + "default_icon": { + "$ref": "IconPath", + "optional": true + }, + "default_popup": { + "type": "string", + "format": "relativeUrl", + "optional": true, + "preprocess": "localize" + } }, "optional": true } diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js index eee0d97a9bd..19c6f786609 100644 --- a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js +++ b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js @@ -82,6 +82,8 @@ function* runTests(options) { let extension = ExtensionTestUtils.loadExtension({ manifest: options.manifest, + files: options.files || {}, + background: `(${background})(${options.getTests})`, }); @@ -140,12 +142,29 @@ add_task(function* testTabSwitchContext() { manifest: { "browser_action": { "default_icon": "default.png", - "default_popup": "default.html", - "default_title": "Default Title", + "default_popup": "__MSG_popup__", + "default_title": "Default __MSG_title__", }, + + "default_locale": "en", + "permissions": ["tabs"], }, + "files": { + "_locales/en/messages.json": { + "popup": { + "message": "default.html", + "description": "Popup", + }, + + "title": { + "message": "Title", + "description": "Title", + }, + }, + }, + getTests(tabs, expectDefaults) { let details = [ {"icon": browser.runtime.getURL("default.png"), diff --git a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js index 6b9b17a9a27..98422b2fc89 100644 --- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js +++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js @@ -82,6 +82,8 @@ function* runTests(options) { let extension = ExtensionTestUtils.loadExtension({ manifest: options.manifest, + files: options.files || {}, + background: `(${background})(${options.getTests})`, }); @@ -154,13 +156,29 @@ add_task(function* testTabSwitchContext() { "page_action": { "default_icon": "default.png", - "default_popup": "default.html", - "default_title": "Default Title \u263a", + "default_popup": "__MSG_popup__", + "default_title": "Default __MSG_title__ \u263a", }, + "default_locale": "en", + "permissions": ["tabs"], }, + "files": { + "_locales/en/messages.json": { + "popup": { + "message": "default.html", + "description": "Popup", + }, + + "title": { + "message": "Title", + "description": "Title", + }, + }, + }, + getTests(tabs) { let details = [ {"icon": browser.runtime.getURL("default.png"), diff --git a/toolkit/components/extensions/Extension.jsm b/toolkit/components/extensions/Extension.jsm index 113c84283b4..cc548a64630 100644 --- a/toolkit/components/extensions/Extension.jsm +++ b/toolkit/components/extensions/Extension.jsm @@ -639,6 +639,13 @@ ExtensionData.prototype = { this.readJSON("manifest.json"), Management.lazyInit(), ]).then(([manifest]) => { + this.manifest = manifest; + this.rawManifest = manifest; + + if (manifest && manifest.default_locale) { + return this.initLocale(); + } + }).then(() => { let context = { url: this.baseURI && this.baseURI.spec, @@ -647,12 +654,17 @@ ExtensionData.prototype = { logError: error => { this.logger.warn(`Loading extension '${this.id}': Reading manifest: ${error}`); }, + + preprocessors: {}, }; - let normalized = Schemas.normalize(manifest, "manifest.WebExtensionManifest", context); + if (this.localeData) { + context.preprocessors.localize = this.localize.bind(this); + } + + let normalized = Schemas.normalize(this.manifest, "manifest.WebExtensionManifest", context); if (normalized.error) { this.manifestError(normalized.error); - this.manifest = manifest; } else { this.manifest = normalized.value; } @@ -696,7 +708,7 @@ ExtensionData.prototype = { // stores its parsed contents in |this.localeMessages.get(locale)|. readLocaleFile: Task.async(function* (locale) { let locales = yield this.promiseLocales(); - let dir = locales.get(locale); + let dir = locales.get(locale) || locale; let file = `_locales/${dir}/messages.json`; try { diff --git a/toolkit/components/extensions/Schemas.jsm b/toolkit/components/extensions/Schemas.jsm index 994d6d658e7..117d5969271 100644 --- a/toolkit/components/extensions/Schemas.jsm +++ b/toolkit/components/extensions/Schemas.jsm @@ -83,19 +83,25 @@ class Context { this.params = params; this.path = []; + this.preprocessors = { + localize(value, context) { + return value; + }, + }; let props = ["addListener", "callFunction", "callAsyncFunction", "hasListener", "removeListener", - "getProperty", "setProperty"]; + "getProperty", "setProperty", + "checkLoadURL", "logError", + "preprocessors"]; for (let prop of props) { - this[prop] = params[prop]; - } - - if ("checkLoadURL" in params) { - this.checkLoadURL = params.checkLoadURL; - } - if ("logError" in params) { - this.logError = params.logError; + if (prop in params) { + if (prop in this && typeof this[prop] == "object") { + Object.assign(this[prop], params[prop]); + } else { + this[prop] = params[prop]; + } + } } } @@ -267,6 +273,24 @@ class Entry { if ("deprecated" in schema) { this.deprecated = schema.deprecated; } + + /** + * If set to a string value, and a preprocessor of the same is + * defined in the validation context, it will be applied to this + * value prior to any normalization. + */ + this.preprocessor = schema.preprocess || null; + } + + /** + * Preprocess the given value with the preprocessor declared in + * `preprocessor`. + */ + preprocess(value, context) { + if (this.preprocessor) { + return context.preprocessors[this.preprocessor](value, context); + } + return value; } /** @@ -335,7 +359,7 @@ class Type extends Entry { normalizeBase(type, value, context) { if (this.checkBaseType(getValueBaseType(value))) { this.checkDeprecated(context, value); - return {value}; + return {value: this.preprocess(value, context)}; } return context.error(`Expected ${type} instead of ${JSON.stringify(value)}`); } @@ -434,6 +458,7 @@ class StringType extends Type { if (r.error) { return r; } + value = r.value; if (this.enumeration) { if (this.enumeration.includes(value)) { @@ -510,6 +535,7 @@ class ObjectType extends Type { if (v.error) { return v; } + value = v.value; if (this.isInstanceOf) { if (Object.keys(this.properties).length || @@ -639,7 +665,7 @@ class NumberType extends Type { return r; } - if (isNaN(value) || !Number.isFinite(value)) { + if (isNaN(r.value) || !Number.isFinite(r.value)) { return context.error("NaN or infinity are not valid"); } @@ -663,6 +689,7 @@ class IntegerType extends Type { if (r.error) { return r; } + value = r.value; // Ensure it's between -2**31 and 2**31-1 if (!Number.isSafeInteger(value)) { @@ -707,6 +734,7 @@ class ArrayType extends Type { if (v.error) { return v; } + value = v.value; let result = []; for (let [i, element] of value.entries()) { @@ -1032,7 +1060,7 @@ this.Schemas = { // Do some simple validation of our own schemas. function checkTypeProperties(...extra) { - let allowedSet = new Set([...allowedProperties, ...extra, "description", "deprecated"]); + let allowedSet = new Set([...allowedProperties, ...extra, "description", "deprecated", "preprocess"]); for (let prop of Object.keys(type)) { if (!allowedSet.has(prop)) { throw new Error(`Internal error: Namespace ${path.join(".")} has invalid type property "${prop}" in type "${type.id || JSON.stringify(type)}"`); diff --git a/toolkit/components/extensions/ext-backgroundPage.js b/toolkit/components/extensions/ext-backgroundPage.js index 1deca087116..1ceb6e04c74 100644 --- a/toolkit/components/extensions/ext-backgroundPage.js +++ b/toolkit/components/extensions/ext-backgroundPage.js @@ -76,15 +76,8 @@ BackgroundPage.prototype = { if (this.scripts) { let doc = window.document; for (let script of this.scripts) { - let url = this.extension.baseURI.resolve(script); - - if (!this.extension.isExtensionURL(url)) { - this.extension.manifestError("Background scripts must be files within the extension"); - continue; - } - let tag = doc.createElement("script"); - tag.setAttribute("src", url); + tag.setAttribute("src", script); tag.async = false; doc.body.appendChild(tag); } diff --git a/toolkit/components/extensions/schemas/manifest.json b/toolkit/components/extensions/schemas/manifest.json index 9812f443e52..d4d7f3dfa34 100644 --- a/toolkit/components/extensions/schemas/manifest.json +++ b/toolkit/components/extensions/schemas/manifest.json @@ -43,12 +43,26 @@ "name": { "type": "string", - "optional": false + "optional": false, + "preprocess": "localize" + }, + + "short_name": { + "type": "string", + "optional": true, + "preprocess": "localize" }, "description": { "type": "string", - "optional": true + "optional": true, + "preprocess": "localize" + }, + + "creator": { + "type": "string", + "optional": true, + "preprocess": "localize" }, "version": { @@ -59,7 +73,8 @@ "homepage_url": { "type": "string", "format": "url", - "optional": true + "optional": true, + "preprocess": "localize" }, "icons": { diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js index 35a7a43d692..116576d400f 100644 --- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js @@ -248,6 +248,22 @@ let json = [ ], }, + { + name: "localize", + type: "function", + parameters: [ + { + name: "arg", + type: "object", + properties: { + foo: {type: "string", "preprocess": "localize", "optional": true}, + bar: {type: "string", "optional": true}, + url: {type: "string", "preprocess": "localize", "format": "url", "optional": true}, + }, + }, + ], + }, + { name: "extended1", type: "function", @@ -325,6 +341,12 @@ let wrapper = { return !url.startsWith("chrome:"); }, + preprocessors: { + localize(value, context) { + return value.replace(/__MSG_(.*?)__/g, (m0, m1) => `${m1.toUpperCase()}`); + }, + }, + logError(message) { talliedErrors.push(message); }, @@ -605,6 +627,16 @@ add_task(function* () { } + root.testing.localize({foo: "__MSG_foo__", bar: "__MSG_foo__", url: "__MSG_http://example.com/__"}); + verify("call", "testing", "localize", [{foo: "FOO", bar: "__MSG_foo__", url: "http://example.com/"}]); + tallied = null; + + + Assert.throws(() => root.testing.localize({url: "__MSG_/foo/bar__"}), + /\/FOO\/BAR is not a valid URL\./, + "should throw for invalid URL"); + + root.testing.extended1({prop1: "foo", prop2: "bar"}); verify("call", "testing", "extended1", [{prop1: "foo", prop2: "bar"}]); tallied = null; diff --git a/toolkit/components/extensions/test/xpcshell/test_locale_data.js b/toolkit/components/extensions/test/xpcshell/test_locale_data.js index c719aa22fef..c5ffcb6362d 100644 --- a/toolkit/components/extensions/test/xpcshell/test_locale_data.js +++ b/toolkit/components/extensions/test/xpcshell/test_locale_data.js @@ -55,15 +55,20 @@ add_task(function* testInvalidDefaultLocale() { }, }); - equal(extension.errors.length, 0, "No errors reported"); - - yield extension.initAllLocales(); - equal(extension.errors.length, 1, "One error reported"); do_print(`Got error: ${extension.errors[0]}`); - ok(extension.errors[0].includes('"default_locale" property must correspond'), + ok(extension.errors[0].includes("Loading locale file _locales/en/messages.json"), + "Got invalid default_locale error"); + + yield extension.initAllLocales(); + + equal(extension.errors.length, 2, "Two errors reported"); + + do_print(`Got error: ${extension.errors[1]}`); + + ok(extension.errors[1].includes('"default_locale" property must correspond'), "Got invalid default_locale error"); }); @@ -75,15 +80,20 @@ add_task(function* testUnexpectedDefaultLocale() { }, }); - equal(extension.errors.length, 0, "No errors reported"); - - yield extension.initAllLocales(); - equal(extension.errors.length, 1, "One error reported"); do_print(`Got error: ${extension.errors[0]}`); - ok(extension.errors[0].includes('"default_locale" property must correspond'), + ok(extension.errors[0].includes("Loading locale file _locales/en-US/messages.json"), + "Got invalid default_locale error"); + + yield extension.initAllLocales(); + + equal(extension.errors.length, 2, "One error reported"); + + do_print(`Got error: ${extension.errors[1]}`); + + ok(extension.errors[1].includes('"default_locale" property must correspond'), "Got unexpected default_locale error"); }); @@ -99,14 +109,19 @@ add_task(function* testInvalidSyntax() { }, }); - equal(extension.errors.length, 0, "No errors reported"); - - yield extension.initAllLocales(); - - equal(extension.errors.length, 1, "One error reported"); + equal(extension.errors.length, 1, "No errors reported"); do_print(`Got error: ${extension.errors[0]}`); ok(extension.errors[0].includes("Loading locale file _locales\/en_US\/messages\.json: SyntaxError"), "Got syntax error"); + + yield extension.initAllLocales(); + + equal(extension.errors.length, 2, "One error reported"); + + do_print(`Got error: ${extension.errors[1]}`); + + ok(extension.errors[1].includes("Loading locale file _locales\/en_US\/messages\.json: SyntaxError"), + "Got syntax error"); }); diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index bdc14f739ea..bcc2055f869 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -898,11 +898,15 @@ var loadManifestFromWebManifest = Task.async(function*(aUri) { addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT; function getLocale(aLocale) { + // Use the raw manifest, here, since we need values with their + // localization placeholders still in place. + let rawManifest = extension.rawManifest; + let result = { - name: extension.localize(manifest.name, aLocale), - description: extension.localize(manifest.description, aLocale), - creator: null, - homepageURL: null, + name: extension.localize(rawManifest.name, aLocale), + description: extension.localize(rawManifest.description, aLocale), + creator: extension.localize(rawManifest.creator, aLocale), + homepageURL: extension.localize(rawManifest.homepage_url, aLocale), developers: null, translators: null, From a5d98d1decedcdfe8bf91f124642c3e6f1c271b3 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Mon, 15 Feb 2016 17:37:19 -0800 Subject: [PATCH 05/31] =?UTF-8?q?Bug=201248497=20=E2=80=93=20Add=20promise?= =?UTF-8?q?=20support=20to=20the=20sendMessage=20APIs.=20r=3Dbillm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MozReview-Commit-ID: AZH9LUq8kGr --- .../browser/browser_ext_tabs_sendMessage.js | 89 +++++++++++++++++++ .../extensions/ExtensionContent.jsm | 2 +- .../components/extensions/ExtensionUtils.jsm | 81 ++++++++++------- 3 files changed, 140 insertions(+), 32 deletions(-) diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js b/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js index 0c0215f3f10..e68a4039081 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js @@ -2,6 +2,95 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; +add_task(function* tabsSendMessageReply() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "permissions": ["tabs"], + + "content_scripts": [{ + "matches": ["http://example.com/"], + "js": ["content-script.js"], + "run_at": "document_start", + }], + }, + + background: function() { + let promiseResponse = new Promise(resolve => { + browser.runtime.onMessage.addListener((msg, sender, respond) => { + if (msg == "content-script-ready") { + let tabId = sender.tab.id; + + browser.tabs.sendMessage(tabId, "respond-never", response => { + browser.test.fail("Got unexpected response callback"); + browser.test.notifyFail("sendMessage"); + }); + + Promise.all([ + promiseResponse, + browser.tabs.sendMessage(tabId, "respond-now"), + new Promise(resolve => browser.tabs.sendMessage(tabId, "respond-soon", resolve)), + browser.tabs.sendMessage(tabId, "respond-promise"), + browser.tabs.sendMessage(tabId, "respond-never"), + browser.tabs.sendMessage(tabId, "respond-error").catch(error => Promise.resolve({error})), + browser.tabs.sendMessage(tabId, "throw-error").catch(error => Promise.resolve({error})), + ]).then(([response, respondNow, respondSoon, respondPromise, respondNever, respondError, throwError]) => { + browser.test.assertEq("expected-response", response, "Content script got the expected response"); + + browser.test.assertEq("respond-now", respondNow, "Got the expected immediate response"); + browser.test.assertEq("respond-soon", respondSoon, "Got the expected delayed response"); + browser.test.assertEq("respond-promise", respondPromise, "Got the expected promise response"); + browser.test.assertEq(undefined, respondNever, "Got the expected no-response resolution"); + + browser.test.assertEq("respond-error", respondError.error.message, "Got the expected error response"); + browser.test.assertEq("throw-error", throwError.error.message, "Got the expected thrown error response"); + + return browser.tabs.remove(tabId); + }).then(() => { + browser.test.notifyPass("sendMessage"); + }); + + return Promise.resolve("expected-response"); + } else if (msg[0] == "got-response") { + resolve(msg[1]); + } + }); + }); + + browser.tabs.create({url: "http://example.com/"}); + }, + + files: { + "content-script.js": function() { + browser.runtime.onMessage.addListener((msg, sender, respond) => { + if (msg == "respond-now") { + respond(msg); + } else if (msg == "respond-soon") { + setTimeout(() => { respond(msg); }, 0); + return true; + } else if (msg == "respond-promise") { + return Promise.resolve(msg); + } else if (msg == "respond-never") { + return; + } else if (msg == "respond-error") { + return Promise.reject(new Error(msg)); + } else if (msg == "throw-error") { + throw new Error(msg); + } + }); + browser.runtime.sendMessage("content-script-ready").then(response => { + browser.runtime.sendMessage(["got-response", response]); + }); + }, + }, + }); + + yield extension.startup(); + + yield extension.awaitFinish("sendMessage"); + + yield extension.unload(); +}); + add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() { let extension = ExtensionTestUtils.loadExtension({ manifest: { diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index f755746c584..674960527dc 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -99,7 +99,7 @@ var api = context => { } let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId}; - context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback); + return context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback); }, }, diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm index 7fbddd185e6..4e12087d773 100644 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -862,25 +862,34 @@ Messenger.prototype = { recipient.messageId = id; this.broker.sendMessage(messageManager, "message", msg, this.sender, recipient); - let onClose; - let listener = ({data: response}) => { - messageManager.removeMessageListener(replyName, listener); - this.context.forgetOnClose(onClose); - - if (response.gotData) { - // TODO: Handle failure to connect to the extension? - runSafe(this.context, responseCallback, response.data); - } - }; - onClose = { - close() { + let promise = new Promise((resolve, reject) => { + let onClose; + let listener = ({data: response}) => { messageManager.removeMessageListener(replyName, listener); - }, - }; - if (responseCallback) { + this.context.forgetOnClose(onClose); + + if (response.gotData) { + resolve(response.data); + } else if (response.error) { + reject(response.error); + } else if (!responseCallback) { + // As a special case, we don't call the callback variant if we + // receive no response, but the promise needs to resolve or + // reject in either case. + resolve(); + } + }; + onClose = { + close() { + messageManager.removeMessageListener(replyName, listener); + }, + }; + messageManager.addMessageListener(replyName, listener); this.context.callOnClose(onClose); - } + }); + + return this.context.wrapPromise(promise, responseCallback); }, onMessage(name) { @@ -895,23 +904,33 @@ Messenger.prototype = { let mm = getMessageManager(target); let replyName = `Extension:Reply-${recipient.messageId}`; - let valid = true, sent = false; - let sendResponse = data => { - if (!valid) { - return; - } - sent = true; - mm.sendAsyncMessage(replyName, {data, gotData: true}); - }; - sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope); + new Promise((resolve, reject) => { + let sendResponse = Cu.exportFunction(resolve, this.context.cloneScope); - let result = runSafeSyncWithoutClone(callback, message, sender, sendResponse); - if (result !== true) { - valid = false; - if (!sent) { - mm.sendAsyncMessage(replyName, {gotData: false}); + // Note: We intentionally do not use runSafe here so that any + // errors are propagated to the message sender. + let result = callback(message, sender, sendResponse); + if (result instanceof Promise) { + resolve(result); + } else if (result !== true) { + reject(); } - } + }).then( + data => { + mm.sendAsyncMessage(replyName, {data, gotData: true}); + }, + error => { + if (error) { + // The result needs to be structured-clonable, which + // ordinary Error objects are not. + try { + error = {message: String(error.message), stack: String(error.stack)}; + } catch (e) { + error = {message: String(error)}; + } + } + mm.sendAsyncMessage(replyName, {error, gotData: false}); + }); }; this.broker.addListener("message", listener, this.filter); From 571eaa990861919c6c830cefbfded0e580567265 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Sat, 27 Feb 2016 14:11:25 -0800 Subject: [PATCH 06/31] Bug 1251890: Fix RemoveTopWindowID listener. r=billm MozReview-Commit-ID: 4nQdPszQOy --- .../extensions/test/browser/.eslintrc | 1 + .../extensions/test/browser/browser.ini | 1 + .../test/browser/browser_ext_topwindowid.js | 23 +++++++++++++++++++ .../extensions/ExtensionManagement.jsm | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 browser/components/extensions/test/browser/browser_ext_topwindowid.js diff --git a/browser/components/extensions/test/browser/.eslintrc b/browser/components/extensions/test/browser/.eslintrc index 934b8317926..11b44f5d344 100644 --- a/browser/components/extensions/test/browser/.eslintrc +++ b/browser/components/extensions/test/browser/.eslintrc @@ -15,6 +15,7 @@ // Test harness globals "ExtensionTestUtils": false, + "TestUtils": false, "clickBrowserAction": true, "clickPageAction": true, diff --git a/browser/components/extensions/test/browser/browser.ini b/browser/components/extensions/test/browser/browser.ini index 852cbe8d5dc..90428fb5b9e 100644 --- a/browser/components/extensions/test/browser/browser.ini +++ b/browser/components/extensions/test/browser/browser.ini @@ -50,4 +50,5 @@ support-files = [browser_ext_windows_update.js] [browser_ext_contentscript_connect.js] [browser_ext_tab_runtimeConnect.js] +[browser_ext_topwindowid.js] [browser_ext_webNavigation_getFrames.js] diff --git a/browser/components/extensions/test/browser/browser_ext_topwindowid.js b/browser/components/extensions/test/browser/browser_ext_topwindowid.js new file mode 100644 index 00000000000..9176ac94626 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_topwindowid.js @@ -0,0 +1,23 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +add_task(function* test_topwindowid_cleanup() { + let {Frames} = Cu.import("resource://gre/modules/ExtensionManagement.jsm", {}); + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/"); + + let {outerWindowID, messageManager} = tab.linkedBrowser; + + ok(Frames.topWindowIds.has(outerWindowID), "Outer window ID is registered"); + + let awaitDisconnect = TestUtils.topicObserved("message-manager-disconnect", + subject => subject === messageManager); + + yield BrowserTestUtils.removeTab(tab); + + yield awaitDisconnect; + + ok(!Frames.topWindowIds.has(outerWindowID), "Outer window ID is no longer registered"); +}); + diff --git a/toolkit/components/extensions/ExtensionManagement.jsm b/toolkit/components/extensions/ExtensionManagement.jsm index db0a492ae3d..c784e6a31a5 100644 --- a/toolkit/components/extensions/ExtensionManagement.jsm +++ b/toolkit/components/extensions/ExtensionManagement.jsm @@ -35,7 +35,7 @@ var Frames = { } Services.mm.addMessageListener("Extension:TopWindowID", this); - Services.mm.addMessageListener("Extension:RemoveTopWindowID", this); + Services.mm.addMessageListener("Extension:RemoveTopWindowID", this, true); }, isTopWindowId(windowId) { From 06e48c841b846f0503da81b5abeab0e9be773bb8 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Fri, 26 Feb 2016 13:20:28 -0800 Subject: [PATCH 07/31] Bug 1248499: [webext] Implement tabs.detectLanguage. r=billm MozReview-Commit-ID: F4GpSesj2ho --- browser/components/extensions/ext-tabs.js | 13 +++++ .../extensions/test/browser/browser.ini | 3 + .../browser_ext_tabs_detectLanguage.js | 57 +++++++++++++++++++ .../test/browser/file_language_fr_en.html | 14 +++++ .../test/browser/file_language_ja.html | 10 ++++ .../extensions/ExtensionContent.jsm | 37 +++++++++++- .../components/extensions/ExtensionUtils.jsm | 23 ++++++++ 7 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 browser/components/extensions/test/browser/browser_ext_tabs_detectLanguage.js create mode 100644 browser/components/extensions/test/browser/file_language_fr_en.html create mode 100644 browser/components/extensions/test/browser/file_language_ja.html diff --git a/browser/components/extensions/ext-tabs.js b/browser/components/extensions/ext-tabs.js index 27ccccaf977..baf926bb99f 100644 --- a/browser/components/extensions/ext-tabs.js +++ b/browser/components/extensions/ext-tabs.js @@ -656,6 +656,19 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => { message, recipient); }, + detectLanguage: function(tabId) { + let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab; + if (!tab) { + return Promise.reject({message: `Invalid tab ID: ${tabId}`}); + } + + let browser = tab.linkedBrowser; + let recipient = {innerWindowID: browser.innerWindowID}; + + return context.sendMessage(browser.messageManager, "Extension:DetectLanguage", + {}, recipient); + }, + _execute: function(tabId, details, kind, method) { let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab; let mm = tab.linkedBrowser.messageManager; diff --git a/browser/components/extensions/test/browser/browser.ini b/browser/components/extensions/test/browser/browser.ini index 90428fb5b9e..c2907e00e86 100644 --- a/browser/components/extensions/test/browser/browser.ini +++ b/browser/components/extensions/test/browser/browser.ini @@ -9,6 +9,8 @@ support-files = file_popup_api_injection_b.html file_iframe_document.html file_iframe_document.sjs + file_language_fr_en.html + file_language_ja.html [browser_ext_simple.js] [browser_ext_currentWindow.js] @@ -28,6 +30,7 @@ support-files = [browser_ext_runtime_setUninstallURL.js] [browser_ext_tabs_audio.js] [browser_ext_tabs_captureVisibleTab.js] +[browser_ext_tabs_detectLanguage.js] [browser_ext_tabs_events.js] [browser_ext_tabs_executeScript.js] [browser_ext_tabs_executeScript_good.js] diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_detectLanguage.js b/browser/components/extensions/test/browser/browser_ext_tabs_detectLanguage.js new file mode 100644 index 00000000000..3c3017e7779 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_tabs_detectLanguage.js @@ -0,0 +1,57 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +add_task(function* testDetectLanguage() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "permissions": ["tabs"], + }, + + background() { + const BASE_PATH = "browser/browser/components/extensions/test/browser"; + + function loadTab(url) { + let tabId; + let awaitUpdated = new Promise(resolve => { + browser.tabs.onUpdated.addListener(function onUpdated(changedTabId, changed, tab) { + if (changedTabId === tabId && changed.url) { + browser.tabs.onUpdated.removeListener(onUpdated); + resolve(tab); + } + }); + }); + + return browser.tabs.create({url}).then(tab => { + tabId = tab.id; + return awaitUpdated; + }); + } + + loadTab(`http://example.co.jp/${BASE_PATH}/file_language_ja.html`).then(tab => { + return browser.tabs.detectLanguage(tab.id).then(lang => { + browser.test.assertEq("ja", lang, "Japanese document should be detected as Japanese"); + return browser.tabs.remove(tab.id); + }); + }).then(() => { + return loadTab(`http://example.co.jp/${BASE_PATH}/file_language_fr_en.html`); + }).then(tab => { + return browser.tabs.detectLanguage(tab.id).then(lang => { + browser.test.assertEq("fr", lang, "French/English document should be detected as primarily French"); + return browser.tabs.remove(tab.id); + }); + }).then(() => { + browser.test.notifyPass("detectLanguage"); + }).catch(e => { + browser.test.fail(`Error: ${e} :: ${e.stack}`); + browser.test.notifyFail("detectLanguage"); + }); + }, + }); + + yield extension.startup(); + + yield extension.awaitFinish("detectLanguage"); + + yield extension.unload(); +}); diff --git a/browser/components/extensions/test/browser/file_language_fr_en.html b/browser/components/extensions/test/browser/file_language_fr_en.html new file mode 100644 index 00000000000..5e3c7b3b080 --- /dev/null +++ b/browser/components/extensions/test/browser/file_language_fr_en.html @@ -0,0 +1,14 @@ + + + + + + + + France is the largest country in Western Europe and the third-largest in Europe as a whole. + A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections et exporter + Cet article concerne le pays européen aujourd’hui appelé République française. Pour d’autres usages du nom France, + Pour une aide rapide et effective, veuiller trouver votre aide dans le menu ci-dessus. + Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. The quick brown fox jumps over the lazy dog. + + diff --git a/browser/components/extensions/test/browser/file_language_ja.html b/browser/components/extensions/test/browser/file_language_ja.html new file mode 100644 index 00000000000..ed07ba70e51 --- /dev/null +++ b/browser/components/extensions/test/browser/file_language_ja.html @@ -0,0 +1,10 @@ + + + + + + + + このペ ジでは アカウントに指定された予算の履歴を一覧にしています それぞれの項目には 予算額と特定期間のステ タスが表示されます 現在または今後の予算を設定するには + + diff --git a/toolkit/components/extensions/ExtensionContent.jsm b/toolkit/components/extensions/ExtensionContent.jsm index 674960527dc..a2bb518148a 100644 --- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -25,6 +25,8 @@ Cu.import("resource://gre/modules/AppConstants.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement", "resource://gre/modules/ExtensionManagement.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector", + "resource:///modules/translation/LanguageDetector.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern", "resource://gre/modules/MatchPattern.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", @@ -33,7 +35,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils", "resource://gre/modules/PromiseUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel", "resource://gre/modules/MessageChannel.jsm"); - XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames", "resource://gre/modules/WebNavigationFrames.jsm"); @@ -47,6 +48,7 @@ var { injectAPI, flushJarCache, detectLanguage, + promiseDocumentReady, } = ExtensionUtils; function isWhenBeforeOrSame(when1, when2) { @@ -732,6 +734,7 @@ class ExtensionGlobal { this.global = global; MessageChannel.addListener(global, "Extension:Capture", this); + MessageChannel.addListener(global, "Extension:DetectLanguage", this); MessageChannel.addListener(global, "Extension:Execute", this); MessageChannel.addListener(global, "WebNavigation:GetFrame", this); MessageChannel.addListener(global, "WebNavigation:GetAllFrames", this); @@ -760,6 +763,8 @@ class ExtensionGlobal { switch (messageName) { case "Extension:Capture": return this.handleExtensionCapture(data.width, data.height, data.options); + case "Extension:DetectLanguage": + return this.handleDetectLanguage(target); case "Extension:Execute": return this.handleExtensionExecute(target, recipient.extensionId, data.options); case "WebNavigation:GetFrame": @@ -790,6 +795,36 @@ class ExtensionGlobal { return canvas.toDataURL(`image/${options.format}`, options.quality / 100); } + handleDetectLanguage(target) { + let doc = target.content.document; + + return promiseDocumentReady(doc).then(() => { + let elem = doc.documentElement; + + let language = (elem.getAttribute("xml:lang") || elem.getAttribute("lang") || + doc.contentLanguage || null); + + // We only want the last element of the TLD here. + // Only country codes have any effect on the results, but other + // values cause no harm. + let tld = doc.location.hostname.match(/[a-z]*$/)[0]; + + // The CLD2 library used by the language detector is capable of + // analyzing raw HTML. Unfortunately, that takes much more memory, + // and since it's hosted by emscripten, and therefore can't shrink + // its heap after it's grown, it has a performance cost. + // So we send plain text instead. + let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"].createInstance(Ci.nsIDocumentEncoder); + encoder.init(doc, "text/plain", encoder.SkipInvisibleContent); + let text = encoder.encodeToStringWithMaxLength(60 * 1024); + + let encoding = doc.characterSet; + + return LanguageDetector.detectLanguage({language, tld, text, encoding}) + .then(result => result.language); + }); + } + handleExtensionExecute(target, extensionId, options) { return DocumentManager.executeScript(target, extensionId, options).then(result => { try { diff --git a/toolkit/components/extensions/ExtensionUtils.jsm b/toolkit/components/extensions/ExtensionUtils.jsm index 4e12087d773..bfdff778ba6 100644 --- a/toolkit/components/extensions/ExtensionUtils.jsm +++ b/toolkit/components/extensions/ExtensionUtils.jsm @@ -630,6 +630,28 @@ function injectAPI(source, dest) { } } +/** + * Returns a Promise which resolves when the given document's DOM has + * fully loaded. + * + * @param {Document} doc The document to await the load of. + * @returns {Promise} + */ +function promiseDocumentReady(doc) { + if (doc.readyState == "interactive" || doc.readyState == "complete") { + return Promise.resolve(doc); + } + + return new Promise(resolve => { + doc.addEventListener("DOMContentLoaded", function onReady(event) { + if (event.target === event.currentTarget) { + doc.removeEventListener("DOMContentLoaded", onReady, true); + resolve(doc); + } + }, true); + }); +} + /* * Messaging primitives. */ @@ -1011,6 +1033,7 @@ this.ExtensionUtils = { ignoreEvent, injectAPI, instanceOf, + promiseDocumentReady, runSafe, runSafeSync, runSafeSyncWithoutClone, From 5d73f095ab41a11a68ee5821c81683d8120dd2bb Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Tue, 1 Mar 2016 08:54:50 -0800 Subject: [PATCH 08/31] Bug 1250611: Switch test_signed_long to use add-ons signed by the production server. r=rhelmer MozReview-Commit-ID: C0e6uYINLxJ --- .../data/signing_checks/long_63_hash.xpi | Bin 4431 -> 4471 bytes .../data/signing_checks/long_63_plain.xpi | Bin 4395 -> 4433 bytes .../data/signing_checks/long_64_hash.xpi | Bin 4437 -> 4474 bytes .../data/signing_checks/long_64_plain.xpi | Bin 4396 -> 4436 bytes .../data/signing_checks/long_65_hash.xpi | Bin 4436 -> 4487 bytes .../test/xpcshell/test_signed_long.js | 7 +------ 6 files changed, 1 insertion(+), 6 deletions(-) diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_63_hash.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_63_hash.xpi index 1f7375b7d342b535b0c3406c2cc72737bf4a710b..1682a75068baf7353c4dc634f4fd1a89453c963e 100644 GIT binary patch delta 3547 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 V00000fB~?RBM~MBOAr76008Rd#$Nyc delta 3489 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 Ph?7eZCI%xA00000e#y*B diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_63_plain.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_63_plain.xpi index 0d3f4b19b699e18ec1267d54e8a0b6a8b43e4664..cd67e25fcfb1b362023a0c8389b1c96b92299cb0 100644 GIT binary patch delta 3541 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 diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_64_plain.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_64_plain.xpi index 50e5cd5d9e610cb67642d7927ede6a93603f1c70..ca453b9d5e1219eea106cea083e71fbda6d89871 100644 GIT binary patch delta 3514 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 delta 3459 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 diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_65_hash.xpi b/toolkit/mozapps/extensions/test/xpcshell/data/signing_checks/long_65_hash.xpi index 993cd4cb4793b1e0ffa04156b68d8c30eafca61b..69579d2dca969029912aafb52ecbff8fe6960da4 100644 GIT binary patch delta 3555 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 AddonManager.SIGNEDSTATE_MISSING); addon.uninstall(); } From 16ad854269d35c88ad3e27973b33a70e1373a4a5 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Tue, 1 Mar 2016 18:48:05 +0100 Subject: [PATCH 09/31] Bug 828008 - Add an UI to emulate user agent from responsive mode. r=jryans * * * [mq]: foo MozReview-Commit-ID: I6Z4VclffGM * * * Wait for reloads MozReview-Commit-ID: FEmN7dgIkjq --- .../locales/en-US/responsiveUI.properties | 3 + .../responsivedesign/responsivedesign.jsm | 82 +++++++++++++++++-- .../client/themes/responsivedesign.inc.css | 16 ++++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/devtools/client/locales/en-US/responsiveUI.properties b/devtools/client/locales/en-US/responsiveUI.properties index 50f0f8a8ddc..76e75de0fe3 100644 --- a/devtools/client/locales/en-US/responsiveUI.properties +++ b/devtools/client/locales/en-US/responsiveUI.properties @@ -18,6 +18,9 @@ responsiveUI.rotate2=Rotate # LOCALIZATION NOTE (responsiveUI.screenshot): tooltip of the screenshot button. responsiveUI.screenshot=Screenshot +# LOCALIZATION NOTE (responsiveUI.userAgentPlaceholder): placeholder for the user agent input. +responsiveUI.userAgentPlaceholder=Custom User Agent + # LOCALIZATION NOTE (responsiveUI.screenshotGeneratedFilename): The auto generated filename. # The first argument (%1$S) is the date string in yyyy-mm-dd format and the second # argument (%2$S) is the time string in HH.MM.SS format. diff --git a/devtools/client/responsivedesign/responsivedesign.jsm b/devtools/client/responsivedesign/responsivedesign.jsm index 738bd79be37..56b5aa703f5 100644 --- a/devtools/client/responsivedesign/responsivedesign.jsm +++ b/devtools/client/responsivedesign/responsivedesign.jsm @@ -7,18 +7,22 @@ const Ci = Components.interfaces; const Cu = Components.utils; -var { loader, require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); +var {loader, require} = Cu.import("resource://devtools/shared/Loader.jsm", {}); var Telemetry = require("devtools/client/shared/telemetry"); -var { showDoorhanger } = require("devtools/client/shared/doorhanger"); -var { TouchEventSimulator } = require("devtools/shared/touch/simulator"); -var { Task } = require("resource://gre/modules/Task.jsm"); +var {showDoorhanger} = require("devtools/client/shared/doorhanger"); +var {TouchEventSimulator} = require("devtools/shared/touch/simulator"); +var {Task} = require("resource://gre/modules/Task.jsm"); var promise = require("promise"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); var Services = require("Services"); var EventEmitter = require("devtools/shared/event-emitter"); -var { ViewHelpers } = require("devtools/client/shared/widgets/ViewHelpers.jsm"); +var {ViewHelpers} = require("devtools/client/shared/widgets/ViewHelpers.jsm"); loader.lazyImporter(this, "SystemAppProxy", "resource://gre/modules/SystemAppProxy.jsm"); +loader.lazyRequireGetter(this, "DebuggerClient", + "devtools/shared/client/main", true); +loader.lazyRequireGetter(this, "DebuggerServer", + "devtools/server/main", true); this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"]; @@ -175,6 +179,7 @@ function ResponsiveUI(aWindow, aTab) this.bound_startResizing = this.startResizing.bind(this); this.bound_stopResizing = this.stopResizing.bind(this); this.bound_onDrag = this.onDrag.bind(this); + this.bound_changeUA = this.changeUA.bind(this); this.bound_onContentResize = this.onContentResize.bind(this); this.mm.addMessageListener("ResponsiveMode:OnContentResize", @@ -201,7 +206,6 @@ ResponsiveUI.prototype = { init: Task.async(function*() { debug("INIT BEGINS"); - let ready = this.waitForMessage("ResponsiveMode:ChildScriptReady"); this.mm.loadFrameScript("resource://devtools/client/responsivedesign/responsivedesign-child.js", true); yield ready; @@ -241,6 +245,9 @@ ResponsiveUI.prototype = { this.touchEnableBefore = false; this.touchEventSimulator = new TouchEventSimulator(this.browser); + yield this.connectToServer(); + this.userAgentInput.hidden = false; + // Hook to display promotional Developer Edition doorhanger. // Only displayed once. showDoorhanger({ @@ -254,6 +261,21 @@ ResponsiveUI.prototype = { ResponsiveUIManager.emit("on", { tab: this.tab }); }), + connectToServer: Task.async(function*() { + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + this.client = new DebuggerClient(DebuggerServer.connectPipe()); + yield this.client.connect(); + let {tab} = yield this.client.getTab(); + let [response, tabClient] = yield this.client.attachTab(tab.actor); + this.tabClient = tabClient; + if (!tabClient) { + Cu.reportError("Responsive Mode: failed to attach tab"); + } + }), + loadPresets: function() { // Try to load presets from prefs let presets = defaultPresets; @@ -359,7 +381,14 @@ ResponsiveUI.prototype = { if (this.touchEventSimulator) { this.touchEventSimulator.stop(); } + + yield new Promise((resolve, reject) => { + this.client.close(resolve); + this.client = this.tabClient = null; + }); + this._telemetry.toolClosed("responsive"); + let stopped = this.waitForMessage("ResponsiveMode:Stop:Done"); this.tab.linkedBrowser.messageManager.sendAsyncMessage("ResponsiveMode:Stop"); yield stopped; @@ -510,6 +539,14 @@ ResponsiveUI.prototype = { this.toolbar.appendChild(this.screenshotbutton); + this.userAgentInput = this.chromeDoc.createElement("textbox"); + this.userAgentInput.className = "devtools-responsiveui-textinput"; + this.userAgentInput.setAttribute("placeholder", + this.strings.GetStringFromName("responsiveUI.userAgentPlaceholder")); + this.userAgentInput.addEventListener("blur", this.bound_changeUA, true); + this.userAgentInput.hidden = true; + this.toolbar.appendChild(this.userAgentInput); + // Resizers let resizerTooltip = this.strings.GetStringFromName("responsiveUI.resizerTooltip"); this.resizer = this.chromeDoc.createElement("box"); @@ -905,6 +942,39 @@ ResponsiveUI.prototype = { } }), + waitForReload() { + let navigatedDeferred = promise.defer(); + let onNavigated = (_, { state }) => { + if (state != "stop") { + return; + } + this.client.removeListener("tabNavigated", onNavigated); + navigatedDeferred.resolve(); + }; + this.client.addListener("tabNavigated", onNavigated); + return navigatedDeferred.promise; + }, + + /** + * Change the user agent string + */ + changeUA: Task.async(function*() { + let value = this.userAgentInput.value; + if (value) { + this.userAgentInput.setAttribute("attention", "true"); + } else { + this.userAgentInput.removeAttribute("attention"); + } + + // Changing the UA triggers an automatic reload. Ensure we wait for this to + // complete before emitting the changed event, so that tests wait for the + // reload. + let reloaded = this.waitForReload(); + yield this.tabClient.reconfigure({customUserAgent: value}); + yield reloaded; + ResponsiveUIManager.emit("userAgentChanged", { tab: this.tab }); + }), + /** * Get the current width and height. */ diff --git a/devtools/client/themes/responsivedesign.inc.css b/devtools/client/themes/responsivedesign.inc.css index be90b9d55c3..6a9204c0df8 100644 --- a/devtools/client/themes/responsivedesign.inc.css +++ b/devtools/client/themes/responsivedesign.inc.css @@ -28,6 +28,22 @@ border-bottom-width: 0; } +.devtools-responsiveui-textinput { + -moz-appearance: none; + background: #333; + color: #fff; + border: 1px solid #111; + border-radius: 2px; + padding: 5px; + width: 200px; + margin: 0; +} + +.devtools-responsiveui-textinput[attention] { + border-color: #38ace6; + background: rgba(56,172,230,0.4); +} + .devtools-responsiveui-menulist, .devtools-responsiveui-toolbarbutton { -moz-appearance: none; From 29bcd962775ca2bedaa5198b41c45690f5139f3c Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Tue, 1 Mar 2016 18:48:15 +0100 Subject: [PATCH 10/31] Bug 828008 - Add test for responsive mode UA textbox. r=jryans MozReview-Commit-ID: 7ZHxEICgIG6 --- .../client/responsivedesign/test/browser.ini | 3 + .../browser_responsiveui_customuseragent.js | 56 +++++++++++++++++++ devtools/client/styleeditor/test/browser.ini | 1 + 3 files changed, 60 insertions(+) create mode 100644 devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js diff --git a/devtools/client/responsivedesign/test/browser.ini b/devtools/client/responsivedesign/test/browser.ini index f319f15cd02..eec6cbc8860 100644 --- a/devtools/client/responsivedesign/test/browser.ini +++ b/devtools/client/responsivedesign/test/browser.ini @@ -7,8 +7,11 @@ support-files = [browser_responsive_cmd.js] [browser_responsivecomputedview.js] +skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s [browser_responsiveruleview.js] +skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s [browser_responsiveui.js] [browser_responsiveui_touch.js] [browser_responsiveuiaddcustompreset.js] [browser_responsive_devicewidth.js] +[browser_responsiveui_customuseragent.js] diff --git a/devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js b/devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js new file mode 100644 index 00000000000..47f9e18f820 --- /dev/null +++ b/devtools/client/responsivedesign/test/browser_responsiveui_customuseragent.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html, Custom User Agent test"; +const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"] + .getService(Ci.nsIHttpProtocolHandler) + .userAgent; +const CHROME_UA = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36" + + " (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"; +add_task(function*() { + yield addTab(TEST_URI); + + let {rdm, manager} = yield openRDM(); + yield testUserAgent(DEFAULT_UA); + + info("Setting UA to " + CHROME_UA); + yield setUserAgent(CHROME_UA, rdm, manager); + yield testUserAgent(CHROME_UA); + + info("Resetting UA"); + yield setUserAgent("", rdm, manager); + yield testUserAgent(DEFAULT_UA); + + info("Setting UA to " + CHROME_UA); + yield setUserAgent(CHROME_UA, rdm, manager); + yield testUserAgent(CHROME_UA); + + info("Closing responsive mode"); + + yield closeRDM(rdm); + yield testUserAgent(DEFAULT_UA); +}); + +function* setUserAgent(ua, rdm, manager) { + let input = rdm.userAgentInput; + input.focus(); + input.value = ua; + let onUAChanged = once(manager, "userAgentChanged"); + input.blur(); + yield onUAChanged; + + if (ua !== "") { + ok(input.hasAttribute("attention"), "UA input should be highlighted"); + } else { + ok(!input.hasAttribute("attention"), "UA input shouldn't be highlighted"); + } +} + +function* testUserAgent(value) { + let ua = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() { + return content.navigator.userAgent; + }); + is(ua, value, `UA should be set to ${value}`); +} diff --git a/devtools/client/styleeditor/test/browser.ini b/devtools/client/styleeditor/test/browser.ini index 784dd7cf41b..9cf9f6dcf7e 100644 --- a/devtools/client/styleeditor/test/browser.ini +++ b/devtools/client/styleeditor/test/browser.ini @@ -68,6 +68,7 @@ support-files = [browser_styleeditor_loading.js] [browser_styleeditor_media_sidebar.js] [browser_styleeditor_media_sidebar_links.js] +skip-if = e10s && debug && os == 'win' # Bug 1252201 - Docshell leak on win 7 debug e10s [browser_styleeditor_media_sidebar_sourcemaps.js] [browser_styleeditor_missing_stylesheet.js] [browser_styleeditor_navigate.js] From 07af925c039c57096b1d7c11767c51cbd2c68cb5 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Tue, 1 Mar 2016 18:48:37 +0100 Subject: [PATCH 11/31] Bug 828008 - Fix browser_styleeditor_media_sidebar_links.js by properly waiting for contentResize. r=jryans MozReview-Commit-ID: 4rT9F3u1c6F --- ...browser_styleeditor_media_sidebar_links.js | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js index 8701015f609..32f422840c3 100644 --- a/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js +++ b/devtools/client/styleeditor/test/browser_styleeditor_media_sidebar_links.js @@ -47,19 +47,17 @@ function* testMediaLink(editor, tab, ui, itemIndex, type, value) { let conditions = sidebar.querySelectorAll(".media-rule-condition"); let onMediaChange = once("media-list-changed", ui); - let ruiEvent = !ResponsiveUIManager.isActiveForTab(tab) ? - once("on", ResponsiveUIManager) : - once("contentResize", ResponsiveUIManager); + let onContentResize = waitForResizeTo(ResponsiveUIManager, type, value); info("Launching responsive mode"); conditions[itemIndex].querySelector(responsiveModeToggleClass).click(); - info("Waiting for the @media list to update"); - yield ruiEvent; - yield onMediaChange; - ResponsiveUIManager.getResponsiveUIForTab(tab).transitionsEnabled = false; + info("Waiting for the @media list to update"); + yield onMediaChange; + yield onContentResize; + ok(ResponsiveUIManager.isActiveForTab(tab), "Responsive mode should be active."); conditions = sidebar.querySelectorAll(".media-rule-condition"); @@ -91,6 +89,21 @@ function doFinalChecks(editor) { } /* Helpers */ +function waitForResizeTo(manager, type, value) { + return new Promise(resolve => { + let onResize = (_, data) => { + if (data[type] != value) { + return; + } + manager.off("contentResize", onResize); + info(`Got contentResize to a ${type} of ${value}`); + resolve(); + }; + info(`Waiting for contentResize to a ${type} of ${value}`); + manager.on("contentResize", onResize); + }); +} + function* getSizing() { let browser = gBrowser.selectedBrowser; let sizing = yield ContentTask.spawn(browser, {}, function*() { From 6c9c2f096ce894a0883cb412540bd57bee04f2ba Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Fri, 26 Feb 2016 16:23:28 -0800 Subject: [PATCH 12/31] Bug 1249384 - Support configuring switchboard server endpoint through intent extra. r=sebastian MozReview-Commit-ID: 4fY94tZebip --- .../java/org/mozilla/gecko/BrowserApp.java | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java index ae233bd41e4..deb69af4965 100644 --- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -153,6 +153,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; import java.net.URLEncoder; import java.util.EnumSet; import java.util.HashSet; @@ -179,9 +181,13 @@ public class BrowserApp extends GeckoApp private static final int TABS_ANIMATION_DURATION = 450; - private static final String ADD_SHORTCUT_TOAST = "add_shortcut_toast"; public static final String GUEST_BROWSING_ARG = "--guest"; - public static final String INTENT_KEY_SWITCHBOARD_UUID = "switchboard-uuid"; + + // Intent String extras used to specify custom Switchboard configurations. + private static final String INTENT_KEY_SWITCHBOARD_UUID = "switchboard-uuid"; + private static final String INTENT_KEY_SWITCHBOARD_HOST = "switchboard-host"; + + private static final String DEFAULT_SWITCHBOARD_HOST = "switchboard.services.mozilla.com"; private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding"; @@ -587,20 +593,7 @@ public class BrowserApp extends GeckoApp final Context appContext = getApplicationContext(); - if (!Experiments.isDisabled(new SafeIntent(intent)) && AppConstants.MOZ_SWITCHBOARD) { - // Initializes the default URLs the first time. - SwitchBoard.initDefaultServerUrls("https://switchboard.services.mozilla.com/urls", "https://switchboard.services.mozilla.com/v1", true); - - final String switchboardUUID = ContextUtils.getStringExtra(intent, INTENT_KEY_SWITCHBOARD_UUID); - SwitchBoard.setUUIDFromExtra(switchboardUUID); - - // Looks at the server if there are changes in the server URL that should be used in the future - new AsyncConfigLoader(this, AsyncConfigLoader.UPDATE_SERVER, switchboardUUID).execute(); - - // Loads the actual config. This can be done on app start or on app onResume() depending - // how often you want to update the config. - new AsyncConfigLoader(this, AsyncConfigLoader.CONFIG_SERVER, switchboardUUID).execute(); - } + initSwitchboard(intent); mBrowserChrome = (ViewGroup) findViewById(R.id.browser_chrome); mActionBarFlipper = (ViewFlipper) findViewById(R.id.browser_actionbar); @@ -781,6 +774,41 @@ public class BrowserApp extends GeckoApp } } + /** + * Initializes the default Switchboard URLs the first time. + * @param intent + */ + private void initSwitchboard(Intent intent) { + if (Experiments.isDisabled(new SafeIntent(intent)) || !AppConstants.MOZ_SWITCHBOARD) { + return; + } + + final String hostExtra = ContextUtils.getStringExtra(intent, INTENT_KEY_SWITCHBOARD_HOST); + final String host = TextUtils.isEmpty(hostExtra) ? DEFAULT_SWITCHBOARD_HOST : hostExtra; + + final String configServerUpdateUrl; + final String configServerUrl; + try { + configServerUpdateUrl = new URL("https", host, "urls").toString(); + configServerUrl = new URL("https", host, "v1").toString(); + } catch (MalformedURLException e) { + Log.e(LOGTAG, "Error creating Switchboard server URL", e); + return; + } + + SwitchBoard.initDefaultServerUrls(configServerUpdateUrl, configServerUrl, true); + + final String switchboardUUID = ContextUtils.getStringExtra(intent, INTENT_KEY_SWITCHBOARD_UUID); + SwitchBoard.setUUIDFromExtra(switchboardUUID); + + // Looks at the server if there are changes in the server URL that should be used in the future + new AsyncConfigLoader(this, AsyncConfigLoader.UPDATE_SERVER, switchboardUUID).execute(); + + // Loads the actual config. This can be done on app start or on app onResume() depending + // how often you want to update the config. + new AsyncConfigLoader(this, AsyncConfigLoader.CONFIG_SERVER, switchboardUUID).execute(); + } + private void showUpdaterPermissionSnackbar() { SnackbarHelper.SnackbarCallback allowCallback = new SnackbarHelper.SnackbarCallback() { @Override From 661a0805ea4e84f39502f777fc0c18c2df9ee36b Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 29 Feb 2016 21:23:05 -0600 Subject: [PATCH 13/31] Bug 1252346 - Some DevTools files missing Services. r=ochameau MozReview-Commit-ID: F1SjhiXNZSj --- devtools/client/framework/target.js | 1 + devtools/client/netmonitor/har/toolbox-overlay.js | 1 + devtools/client/webide/modules/simulators.js | 1 + devtools/shared/touch/simulator.js | 1 + 4 files changed, 4 insertions(+) diff --git a/devtools/client/framework/target.js b/devtools/client/framework/target.js index 9311ed88771..b8f96b69ecf 100644 --- a/devtools/client/framework/target.js +++ b/devtools/client/framework/target.js @@ -9,6 +9,7 @@ const { Ci, Cu } = require("chrome"); const promise = require("promise"); const EventEmitter = require("devtools/shared/event-emitter"); +const Services = require("Services"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true); diff --git a/devtools/client/netmonitor/har/toolbox-overlay.js b/devtools/client/netmonitor/har/toolbox-overlay.js index 3d6bf2e6696..83df63d398f 100644 --- a/devtools/client/netmonitor/har/toolbox-overlay.js +++ b/devtools/client/netmonitor/har/toolbox-overlay.js @@ -4,6 +4,7 @@ "use strict"; const { Cu, Ci } = require("chrome"); +const Services = require("Services"); loader.lazyRequireGetter(this, "HarAutomation", "devtools/client/netmonitor/har/har-automation", true); diff --git a/devtools/client/webide/modules/simulators.js b/devtools/client/webide/modules/simulators.js index 89628f4c716..826b8e992fc 100644 --- a/devtools/client/webide/modules/simulators.js +++ b/devtools/client/webide/modules/simulators.js @@ -12,6 +12,7 @@ loader.lazyRequireGetter(this, "CustomSimulatorProcess", "devtools/client/webide const asyncStorage = require("devtools/shared/async-storage"); const EventEmitter = require("devtools/shared/event-emitter"); const promise = require("promise"); +const Services = require("Services"); const SimulatorRegExp = new RegExp(Services.prefs.getCharPref("devtools.webide.simulatorAddonRegExp")); const LocaleCompare = (a, b) => { diff --git a/devtools/shared/touch/simulator.js b/devtools/shared/touch/simulator.js index d5b0e631b66..f5b251db3a4 100644 --- a/devtools/shared/touch/simulator.js +++ b/devtools/shared/touch/simulator.js @@ -5,6 +5,7 @@ var { Ci } = require("chrome"); var promise = require("promise"); +var Services = require("Services"); const FRAME_SCRIPT = "resource://devtools/shared/touch/simulator-content.js"; From 7fecf3b7127da2dacdb13e6d99914580b346e880 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Tue, 1 Mar 2016 13:58:54 -0600 Subject: [PATCH 14/31] Bug 1252346 - Fixes missing Services definition or tweaked eslint to figure out where does it comes from. r=jryans --- devtools/client/commandline/test/helpers.js | 1 + devtools/client/debugger/content/actions/sources.js | 1 + devtools/client/debugger/content/views/sources-view.js | 1 + devtools/client/debugger/utils.js | 1 + devtools/client/framework/test/shared-redux-head.js | 2 +- devtools/client/inspector/fonts/fonts.js | 1 + devtools/client/inspector/markup/markup.js | 1 + devtools/client/inspector/rules/views/rule-editor.js | 1 + devtools/client/jsonview/test/doc_frame_script.js | 2 ++ devtools/client/jsonview/utils.js | 1 + devtools/client/responsivedesign/responsivedesign-child.js | 2 +- devtools/client/webaudioeditor/views/context.js | 2 ++ devtools/client/webaudioeditor/views/inspector.js | 2 ++ devtools/shared/gcli/commands/highlight.js | 1 + devtools/shared/webconsole/network-helper.js | 1 + 15 files changed, 18 insertions(+), 2 deletions(-) diff --git a/devtools/client/commandline/test/helpers.js b/devtools/client/commandline/test/helpers.js index 554e3dba934..4dd2f8fee5a 100644 --- a/devtools/client/commandline/test/helpers.js +++ b/devtools/client/commandline/test/helpers.js @@ -24,6 +24,7 @@ var helpers = {}; var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); var { TargetFactory } = require("devtools/client/framework/target"); +var Services = require("Services"); var assert = { ok: ok, is: is, log: info }; var util = require('gcli/util/util'); diff --git a/devtools/client/debugger/content/actions/sources.js b/devtools/client/debugger/content/actions/sources.js index ee5943453b7..5abb494f9ba 100644 --- a/devtools/client/debugger/content/actions/sources.js +++ b/devtools/client/debugger/content/actions/sources.js @@ -5,6 +5,7 @@ const constants = require('../constants'); const promise = require('promise'); +const Services = require('Services'); const { dumpn } = require("devtools/shared/DevToolsUtils"); const { PROMISE, HISTOGRAM_ID } = require('devtools/client/shared/redux/middleware/promise'); const { getSource, getSourceText } = require('../queries'); diff --git a/devtools/client/debugger/content/views/sources-view.js b/devtools/client/debugger/content/views/sources-view.js index b5d829b1e06..88b7eb7cc72 100644 --- a/devtools/client/debugger/content/views/sources-view.js +++ b/devtools/client/debugger/content/views/sources-view.js @@ -1,6 +1,7 @@ /* 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 ../../debugger-controller.js */ "use strict"; const utils = require('../utils'); diff --git a/devtools/client/debugger/utils.js b/devtools/client/debugger/utils.js index 2841ca04524..e212c1cdba3 100644 --- a/devtools/client/debugger/utils.js +++ b/devtools/client/debugger/utils.js @@ -4,6 +4,7 @@ * 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/. */ /* globals document, window */ +/* import-globals-from ./debugger-controller.js */ "use strict"; // Maps known URLs to friendly source group names and put them at the diff --git a/devtools/client/framework/test/shared-redux-head.js b/devtools/client/framework/test/shared-redux-head.js index d2ab50ff19e..a8ff8c8b472 100644 --- a/devtools/client/framework/test/shared-redux-head.js +++ b/devtools/client/framework/test/shared-redux-head.js @@ -6,8 +6,8 @@ "use strict"; /* eslint no-unused-vars: [2, {"vars": "local"}] */ +/* import-globals-from ./shared-head.js */ // Currently this file expects "promise" to be imported into scope. -/* globals promise */ // Common utility functions for working with Redux stores. The file is meant // to be safe to load in both mochitest and xpcshell environments. diff --git a/devtools/client/inspector/fonts/fonts.js b/devtools/client/inspector/fonts/fonts.js index 8284135ebb5..b55a7eaeaf7 100644 --- a/devtools/client/inspector/fonts/fonts.js +++ b/devtools/client/inspector/fonts/fonts.js @@ -10,6 +10,7 @@ const {Cu} = require("chrome"); const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {}); const {gDevTools} = require("devtools/client/framework/devtools"); +const Services = require("Services"); const DEFAULT_PREVIEW_TEXT = "Abc"; const PREVIEW_UPDATE_DELAY = 150; diff --git a/devtools/client/inspector/markup/markup.js b/devtools/client/inspector/markup/markup.js index e01caf6a425..a3160ba586a 100644 --- a/devtools/client/inspector/markup/markup.js +++ b/devtools/client/inspector/markup/markup.js @@ -28,6 +28,7 @@ const {editableField, InplaceEditor} = require("devtools/client/shared/inplace-editor"); const {HTMLEditor} = require("devtools/client/inspector/markup/html-editor"); const promise = require("promise"); +const Services = require("Services"); const {Tooltip} = require("devtools/client/shared/widgets/Tooltip"); const EventEmitter = require("devtools/shared/event-emitter"); const Heritage = require("sdk/core/heritage"); diff --git a/devtools/client/inspector/rules/views/rule-editor.js b/devtools/client/inspector/rules/views/rule-editor.js index c6e2c04fd65..a857fcabc36 100644 --- a/devtools/client/inspector/rules/views/rule-editor.js +++ b/devtools/client/inspector/rules/views/rule-editor.js @@ -26,6 +26,7 @@ const { SELECTOR_PSEUDO_CLASS } = require("devtools/client/shared/css-parsing-utils"); const promise = require("promise"); +const Services = require("Services"); const EventEmitter = require("devtools/shared/event-emitter"); XPCOMUtils.defineLazyGetter(this, "_strings", function() { diff --git a/devtools/client/jsonview/test/doc_frame_script.js b/devtools/client/jsonview/test/doc_frame_script.js index abf6440fe37..106f1aa7402 100644 --- a/devtools/client/jsonview/test/doc_frame_script.js +++ b/devtools/client/jsonview/test/doc_frame_script.js @@ -4,6 +4,8 @@ "use strict"; +/* globals Services, sendAsyncMessage, addMessageListener */ + // XXX Some helper API could go to testing/mochitest/tests/SimpleTest/AsyncContentUtils.js // (or at least to share test API in devtools) diff --git a/devtools/client/jsonview/utils.js b/devtools/client/jsonview/utils.js index 299b932ee29..1fac7bffd27 100644 --- a/devtools/client/jsonview/utils.js +++ b/devtools/client/jsonview/utils.js @@ -7,6 +7,7 @@ "use strict"; const {Cu, Cc, Ci} = require("chrome"); +const Services = require("Services"); const {getMostRecentBrowserWindow} = require("sdk/window/utils"); const OPEN_FLAGS = { diff --git a/devtools/client/responsivedesign/responsivedesign-child.js b/devtools/client/responsivedesign/responsivedesign-child.js index de070c6e48f..b4284991285 100644 --- a/devtools/client/responsivedesign/responsivedesign-child.js +++ b/devtools/client/responsivedesign/responsivedesign-child.js @@ -5,7 +5,7 @@ "use strict"; /* global content, docShell, addEventListener, addMessageListener, - removeEventListener, removeMessageListener, sendAsyncMessage */ + removeEventListener, removeMessageListener, sendAsyncMessage, Services */ var global = this; diff --git a/devtools/client/webaudioeditor/views/context.js b/devtools/client/webaudioeditor/views/context.js index efd65bbb42b..a9e0637d016 100644 --- a/devtools/client/webaudioeditor/views/context.js +++ b/devtools/client/webaudioeditor/views/context.js @@ -3,6 +3,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; +/* import-globals-from ../includes.js */ + const { debounce } = require("sdk/lang/functional"); // Globals for d3 stuff diff --git a/devtools/client/webaudioeditor/views/inspector.js b/devtools/client/webaudioeditor/views/inspector.js index adbb68d220e..1f50bb13790 100644 --- a/devtools/client/webaudioeditor/views/inspector.js +++ b/devtools/client/webaudioeditor/views/inspector.js @@ -3,6 +3,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; +/* import-globals-from ../includes.js */ + const MIN_INSPECTOR_WIDTH = 300; // Strings for rendering diff --git a/devtools/shared/gcli/commands/highlight.js b/devtools/shared/gcli/commands/highlight.js index 2a49506b8a7..adf9749772d 100644 --- a/devtools/shared/gcli/commands/highlight.js +++ b/devtools/shared/gcli/commands/highlight.js @@ -5,6 +5,7 @@ "use strict"; const l10n = require("gcli/l10n"); +const Services = require("Services"); require("devtools/server/actors/inspector"); const { BoxModelHighlighter, diff --git a/devtools/shared/webconsole/network-helper.js b/devtools/shared/webconsole/network-helper.js index c6bc763ae8e..702ed1e877d 100644 --- a/devtools/shared/webconsole/network-helper.js +++ b/devtools/shared/webconsole/network-helper.js @@ -57,6 +57,7 @@ const {components, Cc, Ci, Cu} = require("chrome"); loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const Services = require("Services"); // The cache used in the `nsIURL` function. const gNSURLStore = new Map(); From abe7e825e297141b5f444b7556ecfda482a61f27 Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Tue, 1 Mar 2016 09:12:21 -0800 Subject: [PATCH 15/31] Bug 1252264 - Use MatrixCursor + MergeCursor to show the desktop bookmarks folder r=mcomella MozReview-Commit-ID: 3qleedRWjKb --- .../org/mozilla/gecko/db/LocalBrowserDB.java | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java index 4bc1894c1de..8f146130100 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java @@ -765,6 +765,25 @@ public class LocalBrowserDB implements BrowserDB { } } + private void assertDefaultBookmarkColumnOrdering() { + // We need to insert MatrixCursor values in a specific order - in order to protect against changes + // in DEFAULT_BOOKMARK_COLUMNS we can just assert that we're using the correct ordering. + // Alternatively we could use RowBuilder.add(columnName, value) but that needs api >= 19, + // or we could iterate over DEFAULT_BOOKMARK_COLUMNS, but that gets messy once we need + // to add more than one artificial folder. + if (!((DEFAULT_BOOKMARK_COLUMNS[0].equals(Bookmarks._ID)) && + (DEFAULT_BOOKMARK_COLUMNS[1].equals(Bookmarks.GUID)) && + (DEFAULT_BOOKMARK_COLUMNS[2].equals(Bookmarks.URL)) && + (DEFAULT_BOOKMARK_COLUMNS[3].equals(Bookmarks.TITLE)) && + (DEFAULT_BOOKMARK_COLUMNS[4].equals(Bookmarks.TYPE)) && + (DEFAULT_BOOKMARK_COLUMNS[5].equals(Bookmarks.PARENT)) && + (DEFAULT_BOOKMARK_COLUMNS.length == 6))) { + // If DEFAULT_BOOKMARK_COLUMNS changes we need to update all the MatrixCursor rows + // to contain appropriate data. + throw new IllegalStateException("Fake folder MatrixCursor creation code must be updated to match DEFAULT_BOOKMARK_COLUMNS"); + } + } + @Override @RobocopTarget public Cursor getBookmarksInFolder(ContentResolver cr, long folderId) { @@ -809,8 +828,19 @@ public class LocalBrowserDB implements BrowserDB { } if (addDesktopFolder) { - // Wrap cursor to add fake desktop bookmarks and reading list folders - return new SpecialFoldersCursorWrapper(c, addDesktopFolder); + MatrixCursor desktopFolderCursor = new MatrixCursor(DEFAULT_BOOKMARK_COLUMNS); + + assertDefaultBookmarkColumnOrdering(); + + desktopFolderCursor.addRow( + new Object[] { Bookmarks.FAKE_DESKTOP_FOLDER_ID, + Bookmarks.FAKE_DESKTOP_FOLDER_GUID, + "", + "", // Title localisation is done later, in the UI layer (BookmarksListAdapter) + Bookmarks.TYPE_FOLDER, + Bookmarks.FIXED_ROOT_ID + }); + return new MergeCursor(new Cursor[] { desktopFolderCursor, c }); } return c; From 0a96918d0300400b6e87c72dc93576a4e18e7fbb Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Mon, 29 Feb 2016 14:21:34 -0800 Subject: [PATCH 16/31] Bug 1252264 - Post: Remove the now-unused SpecialFoldersCursorWrapper r=me This is the last java based CursorWrapper! MozReview-Commit-ID: 5oaPO4uvWU8 --- .../org/mozilla/gecko/db/LocalBrowserDB.java | 79 ------------------- 1 file changed, 79 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java index 8f146130100..52acab9be9b 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java @@ -48,7 +48,6 @@ import android.content.ContentValues; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; -import android.database.CursorWrapper; import android.database.MatrixCursor; import android.database.MergeCursor; import android.graphics.Bitmap; @@ -1495,84 +1494,6 @@ public class LocalBrowserDB implements BrowserDB { operations.add(builder.build()); } - // This wrapper adds a fake "Desktop Bookmarks" folder entry to the - // beginning of the cursor's data set. - private static class SpecialFoldersCursorWrapper extends CursorWrapper { - private int mIndexOffset; - - private int mDesktopBookmarksIndex = -1; - - private boolean mAtDesktopBookmarksPosition; - - public SpecialFoldersCursorWrapper(Cursor c, boolean showDesktopBookmarks) { - super(c); - - if (showDesktopBookmarks) { - mDesktopBookmarksIndex = mIndexOffset; - mIndexOffset++; - } - } - - @Override - public int getCount() { - return super.getCount() + mIndexOffset; - } - - @Override - public boolean moveToPosition(int position) { - mAtDesktopBookmarksPosition = (mDesktopBookmarksIndex == position); - - if (mAtDesktopBookmarksPosition) { - return true; - } - - return super.moveToPosition(position - mIndexOffset); - } - - @Override - public long getLong(int columnIndex) { - if (!mAtDesktopBookmarksPosition) { - return super.getLong(columnIndex); - } - - if (columnIndex == getColumnIndex(Bookmarks.PARENT)) { - return Bookmarks.FIXED_ROOT_ID; - } - - return -1; - } - - @Override - public int getInt(int columnIndex) { - if (!mAtDesktopBookmarksPosition) { - return super.getInt(columnIndex); - } - - if (columnIndex == getColumnIndex(Bookmarks._ID) && mAtDesktopBookmarksPosition) { - return Bookmarks.FAKE_DESKTOP_FOLDER_ID; - } - - if (columnIndex == getColumnIndex(Bookmarks.TYPE)) { - return Bookmarks.TYPE_FOLDER; - } - - return -1; - } - - @Override - public String getString(int columnIndex) { - if (!mAtDesktopBookmarksPosition) { - return super.getString(columnIndex); - } - - if (columnIndex == getColumnIndex(Bookmarks.GUID) && mAtDesktopBookmarksPosition) { - return Bookmarks.FAKE_DESKTOP_FOLDER_GUID; - } - - return ""; - } - } - @Override public void pinSite(ContentResolver cr, String url, String title, int position) { ContentValues values = new ContentValues(); From 4b601293c3a2b99f20448906697a97302fd93355 Mon Sep 17 00:00:00 2001 From: malayaleecoder Date: Tue, 16 Feb 2016 00:06:23 +0530 Subject: [PATCH 17/31] Bug 1219323 - Remove unnecessary table created check from BrowserDatabaseHelper. r=vivek,nalexander MozReview-Commit-ID: GDPQ8LQ24bP --- .../gecko/db/BrowserDatabaseHelper.java | 22 ------------------- .../org/mozilla/gecko/db/TabsProvider.java | 1 - 2 files changed, 23 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java index a100428dc6d..d5ffddf48ae 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java @@ -193,7 +193,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { ");"); } - private boolean didCreateTabsTable = false; private void createTabsTable(SQLiteDatabase db, final String tableName) { debug("Creating tabs.db: " + db.getPath()); debug("Creating " + tableName + " table"); @@ -345,7 +344,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { createClientsTable(db); createLocalClient(db); createTabsTable(db, TABLE_TABS); - didCreateTabsTable = true; createTabsTableIndices(db, TABLE_TABS); @@ -359,7 +357,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { createOrUpdateAllSpecialFolders(db); createSearchHistoryTable(db); createReadingListTable(db, TABLE_READING_LIST); - didCreateCurrentReadingListTable = true; // Mostly correct, in the absence of transactions. createReadingListIndices(db, TABLE_READING_LIST); createUrlAnnotationsTable(db); createNumbersTable(db); @@ -374,7 +371,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { public void copyTabsDB(File tabsDBFile, SQLiteDatabase destinationDB) { createClientsTable(destinationDB); createTabsTable(destinationDB, TABLE_TABS); - didCreateTabsTable = true; createTabsTableIndices(destinationDB, TABLE_TABS); SQLiteDatabase oldTabsDB = null; @@ -410,7 +406,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { SearchHistory.TABLE_NAME + "(" + SearchHistory.DATE_LAST_VISITED + ")"); } - private boolean didCreateCurrentReadingListTable = false; private void createReadingListTable(final SQLiteDatabase db, final String tableName) { debug("Creating " + TABLE_READING_LIST + " table"); @@ -914,7 +909,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { // Done. db.setTransactionSuccessful(); - didCreateCurrentReadingListTable = true; } catch (SQLException e) { Log.e(LOGTAG, "Error migrating reading list items", e); @@ -941,11 +935,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { } private void upgradeDatabaseFrom21to22(SQLiteDatabase db) { - if (didCreateCurrentReadingListTable) { - debug("No need to add CONTENT_STATUS to reading list; we just created with the current schema."); - return; - } - debug("Adding CONTENT_STATUS column to reading list table."); try { @@ -963,11 +952,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { } private void upgradeDatabaseFrom22to23(SQLiteDatabase db) { - if (didCreateCurrentReadingListTable) { - debug("No need to rev reading list schema; we just created with the current schema."); - return; - } - debug("Rewriting reading list table."); createReadingListTable(db, "tmp_rl"); @@ -1037,11 +1021,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { } private void upgradeDatabaseFrom24to25(SQLiteDatabase db) { - if (didCreateTabsTable) { - debug("No need to rev tabs schema; foreign key constraint exists."); - return; - } - debug("Rewriting tabs table."); createTabsTable(db, "tmp_tabs"); @@ -1068,7 +1047,6 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper { db.execSQL("DROP TABLE " + TABLE_TABS); db.execSQL("ALTER TABLE tmp_tabs RENAME TO " + TABLE_TABS); createTabsTableIndices(db, TABLE_TABS); - didCreateTabsTable =true; } private void upgradeDatabaseFrom25to26(SQLiteDatabase db) { diff --git a/mobile/android/base/java/org/mozilla/gecko/db/TabsProvider.java b/mobile/android/base/java/org/mozilla/gecko/db/TabsProvider.java index a39d2439307..6a0d3f50d18 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/TabsProvider.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/TabsProvider.java @@ -167,7 +167,6 @@ public class TabsProvider extends SharedBrowserDatabaseProvider { // fall through case CLIENTS: trace("Delete on CLIENTS: " + uri); - // Delete from both TABLE_TABS and TABLE_CLIENTS. deleted = deleteValues(uri, selection, selectionArgs, TABLE_CLIENTS); break; From 3c2efbc0cff30781470194f2666a0cc91cbec440 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Tue, 1 Mar 2016 13:49:48 -0800 Subject: [PATCH 18/31] Bug 1248855 - [webext] Follow-up: Add matching "array-bracket-spacing" rule. r=me MozReview-Commit-ID: HALx5dbLxpj --- toolkit/components/extensions/.eslintrc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/toolkit/components/extensions/.eslintrc b/toolkit/components/extensions/.eslintrc index 083998bd031..363589d87fd 100644 --- a/toolkit/components/extensions/.eslintrc +++ b/toolkit/components/extensions/.eslintrc @@ -43,9 +43,15 @@ // Always require spacing around a single line block "block-spacing": 1, + // Forbid spaces inside the square brackets of array literals. + "array-bracket-spacing": [2, "never"], + // Forbid spaces inside the curly brackets of object literals. "object-curly-spacing": [2, "never"], + // No space padding in parentheses + "space-in-parens": [2, "never"], + // Enforce one true brace style (opening brace on the same line) and avoid // start and end braces on the same line. "brace-style": [2, "1tbs", {"allowSingleLine": true}], @@ -203,13 +209,9 @@ // Require spaces before finally, catch, etc. "space-before-keywords": [2, "always"], - // No space padding in parentheses - "space-in-parens": [2, "never"], - // Require spaces around operators, except for a|0. "space-infix-ops": [2, {"int32Hint": true}], - // Require spaces after return, throw and case "space-return-throw-case": 2, From 5ce116ce229350b1f7203d57badbcf8a23bf068e Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Tue, 1 Mar 2016 09:32:16 -0800 Subject: [PATCH 19/31] Bug 1252499 - only insert blank tile when really needed r=liuche MozReview-Commit-ID: 5O7gUBDl1D4 --- .../android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java index 52acab9be9b..da20ee63daf 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java @@ -1631,7 +1631,7 @@ public class LocalBrowserDB implements BrowserDB { // that inside out topsites SQL query would be difficult given the other processing we're already doing there). final int blanksRequired = suggestedRangeLimit - topSitesCursor.getCount(); - if (blanksRequired < 0) { + if (blanksRequired <= 0) { return topSitesCursor; } From e8ef7d0a50474301f3cd30c1c1f986045aff38ae Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Tue, 1 Mar 2016 10:10:47 -0800 Subject: [PATCH 20/31] Bug 1252500 - Provide all the necessary columns when retrieving topsites r=rnewman MozReview-Commit-ID: IYeQ3Z6LLPy --- .../java/org/mozilla/gecko/db/BrowserProvider.java | 8 ++++++++ .../java/org/mozilla/gecko/db/LocalBrowserDB.java | 12 ++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java index d5f9fa861e9..cef62da8154 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java @@ -804,6 +804,8 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider { db.execSQL("CREATE TEMP TABLE " + TABLE_TOPSITES + " AS" + " SELECT " + Bookmarks._ID + ", " + + Combined.BOOKMARK_ID + ", " + + Combined.HISTORY_ID + ", " + Bookmarks.URL + ", " + Bookmarks.TITLE + ", " + Combined.HISTORY_ID + ", " + @@ -822,6 +824,8 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider { // Hence the weird SELECT * FROM (SELECT ...relevant suggested sites... LIMIT ?) " SELECT * FROM (SELECT " + Bookmarks._ID + ", " + + Bookmarks._ID + " AS " + Combined.BOOKMARK_ID + ", " + + " -1 AS " + Combined.HISTORY_ID + ", " + Bookmarks.URL + ", " + Bookmarks.TITLE + ", " + "NULL AS " + Combined.HISTORY_ID + ", " + @@ -840,6 +844,8 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider { final SQLiteCursor c = (SQLiteCursor) db.rawQuery( "SELECT " + Bookmarks._ID + ", " + + TopSites.BOOKMARK_ID + ", " + + TopSites.HISTORY_ID + ", " + Bookmarks.URL + ", " + Bookmarks.TITLE + ", " + Bookmarks.POSITION + ", " + @@ -855,6 +861,8 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider { "SELECT " + Bookmarks._ID + ", " + + Bookmarks._ID + " AS " + TopSites.BOOKMARK_ID + ", " + + " -1 AS " + TopSites.HISTORY_ID + ", " + Bookmarks.URL + ", " + Bookmarks.TITLE + ", " + Bookmarks.POSITION + ", " + diff --git a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java index da20ee63daf..d27eebb55d4 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java @@ -1636,13 +1636,17 @@ public class LocalBrowserDB implements BrowserDB { } MatrixCursor blanksCursor = new MatrixCursor(new String[] { - Bookmarks._ID, - Bookmarks.URL, - Bookmarks.TITLE, - Bookmarks.TYPE}); + TopSites._ID, + TopSites.BOOKMARK_ID, + TopSites.HISTORY_ID, + TopSites.URL, + TopSites.TITLE, + TopSites.TYPE}); final MatrixCursor.RowBuilder rb = blanksCursor.newRow(); rb.add(-1); + rb.add(-1); + rb.add(-1); rb.add(""); rb.add(""); rb.add(TopSites.TYPE_BLANK); From 5296861e2e9d072bf7cb120d4f54bf89d4d265c4 Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Tue, 1 Mar 2016 13:54:24 -0800 Subject: [PATCH 21/31] Bug 1252610 - Don't insert suggested sites into topsites table unless they actually exist r=rnewman MozReview-Commit-ID: 8MPLvxQ0FWu --- .../org/mozilla/gecko/db/BrowserProvider.java | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java index cef62da8154..cc11cd058f6 100644 --- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java +++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java @@ -763,7 +763,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider { String[] suggestedSiteArgs = new String[0]; - boolean firstClause = true; + boolean hasProcessedAnySuggestedSites = true; final int idColumnIndex = suggestedSitesCursor.getColumnIndexOrThrow(Bookmarks._ID); final int urlColumnIndex = suggestedSitesCursor.getColumnIndexOrThrow(Bookmarks.URL); @@ -771,10 +771,10 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider { while (suggestedSitesCursor.moveToNext()) { // We'll be using this as a subquery, hence we need to avoid the preceding UNION ALL - if (!firstClause) { + if (!hasProcessedAnySuggestedSites) { suggestedSitesBuilder.append(" UNION ALL"); } else { - firstClause = false; + hasProcessedAnySuggestedSites = false; } suggestedSitesBuilder.append(" SELECT" + " ? AS " + Bookmarks._ID + "," + @@ -818,28 +818,30 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider { DBUtils.appendSelectionArgs(ignoreForTopSitesArgs, totalLimitArgs)); - db.execSQL("INSERT INTO " + TABLE_TOPSITES + - // We need to LIMIT _after_ selecting the relevant suggested sites, which requires us to - // use an additional internal subquery, since we cannot LIMIT a subquery that is part of UNION ALL. - // Hence the weird SELECT * FROM (SELECT ...relevant suggested sites... LIMIT ?) - " SELECT * FROM (SELECT " + - Bookmarks._ID + ", " + - Bookmarks._ID + " AS " + Combined.BOOKMARK_ID + ", " + - " -1 AS " + Combined.HISTORY_ID + ", " + - Bookmarks.URL + ", " + - Bookmarks.TITLE + ", " + - "NULL AS " + Combined.HISTORY_ID + ", " + - TopSites.TYPE_SUGGESTED + " as " + TopSites.TYPE + - " FROM ( " + suggestedSitesBuilder.toString() + " )" + - " WHERE " + - Bookmarks.URL + " NOT IN (SELECT url FROM " + TABLE_TOPSITES + ")" + - " AND " + - Bookmarks.URL + " NOT IN (SELECT url " + pinnedSitesFromClause + ")" + - suggestedLimitClause + " )", + if (!hasProcessedAnySuggestedSites) { + db.execSQL("INSERT INTO " + TABLE_TOPSITES + + // We need to LIMIT _after_ selecting the relevant suggested sites, which requires us to + // use an additional internal subquery, since we cannot LIMIT a subquery that is part of UNION ALL. + // Hence the weird SELECT * FROM (SELECT ...relevant suggested sites... LIMIT ?) + " SELECT * FROM (SELECT " + + Bookmarks._ID + ", " + + Bookmarks._ID + " AS " + Combined.BOOKMARK_ID + ", " + + " -1 AS " + Combined.HISTORY_ID + ", " + + Bookmarks.URL + ", " + + Bookmarks.TITLE + ", " + + "NULL AS " + Combined.HISTORY_ID + ", " + + TopSites.TYPE_SUGGESTED + " as " + TopSites.TYPE + + " FROM ( " + suggestedSitesBuilder.toString() + " )" + + " WHERE " + + Bookmarks.URL + " NOT IN (SELECT url FROM " + TABLE_TOPSITES + ")" + + " AND " + + Bookmarks.URL + " NOT IN (SELECT url " + pinnedSitesFromClause + ")" + + suggestedLimitClause + " )", - DBUtils.concatenateSelectionArgs(suggestedSiteArgs, - pinnedSitesArgs, - suggestedLimitArgs)); + DBUtils.concatenateSelectionArgs(suggestedSiteArgs, + pinnedSitesArgs, + suggestedLimitArgs)); + } final SQLiteCursor c = (SQLiteCursor) db.rawQuery( "SELECT " + From 74f6cb73ca29693528ff08ac1ed6f16e35201d5e Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Tue, 1 Mar 2016 14:26:23 -0800 Subject: [PATCH 22/31] Bug 1252519 - specify compatible version range in runtime error about Firefox not being found; r=marco --- webapprt/gtk/webapprt.cpp | 4 ++-- webapprt/mac/webapprt.mm | 2 +- webapprt/win/webapprt.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapprt/gtk/webapprt.cpp b/webapprt/gtk/webapprt.cpp index 08a293bf545..69503c23daa 100644 --- a/webapprt/gtk/webapprt.cpp +++ b/webapprt/gtk/webapprt.cpp @@ -384,8 +384,8 @@ int main(int argc, char *argv[]) snprintf(appIniPath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_INI); if (NS_FAILED(parser.Init(appIniPath))) { - ErrorDialog("This app requires that Firefox version 16 or above is installed." - " Firefox 16+ has not been detected."); + ErrorDialog("This app requires a Firefox version between 16 and 47 to be installed." + " No compatible version of Firefox has been detected."); return 255; } diff --git a/webapprt/mac/webapprt.mm b/webapprt/mac/webapprt.mm index f2b6fc20d1f..0d69bd0d3a1 100644 --- a/webapprt/mac/webapprt.mm +++ b/webapprt/mac/webapprt.mm @@ -381,7 +381,7 @@ NSString } NSLog(@"unable to find a valid webrt path"); - @throw MakeException(@"This App requires that Firefox version 16 or above is installed.", @"Firefox 16+ has not been detected."); + @throw MakeException(@"This app requires a Firefox version between 16 and 47 to be installed.", @"No compatible version of Firefox has been detected."); return nil; } diff --git a/webapprt/win/webapprt.cpp b/webapprt/win/webapprt.cpp index 9c9e733a7e4..dd23f949ce2 100644 --- a/webapprt/win/webapprt.cpp +++ b/webapprt/win/webapprt.cpp @@ -524,7 +524,7 @@ main(int argc, char* argv[]) } // We've done all we know how to do to try to find and launch FF - Output("This app requires that Firefox version 16 or above is installed." - " Firefox 16+ has not been detected."); + Output("This app requires a Firefox version between 16 and 47 to be installed." + " No compatible version of Firefox has been detected."); return 255; } From 5407b0e605e42ecd0e8f333fe56e2b6cfdd924ca Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Tue, 1 Mar 2016 14:29:41 -0800 Subject: [PATCH 23/31] Bug 1245204 - warn user that runtime will be disabled; r=marco --- webapprt/Startup.jsm | 38 +++++++++++++++++++ .../locales/en-US/webapprt/webapp.properties | 6 +++ 2 files changed, 44 insertions(+) diff --git a/webapprt/Startup.jsm b/webapprt/Startup.jsm index 4b893d2224f..a6ad8a9a7e6 100644 --- a/webapprt/Startup.jsm +++ b/webapprt/Startup.jsm @@ -9,6 +9,7 @@ this.EXPORTED_SYMBOLS = ["startup"]; +const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; @@ -103,6 +104,43 @@ this.startup = function(window) { appUpdated = yield WebappRT.applyUpdate(); } + const SHOW_DISABLE_WARNING_PREF = "webapprt.showDisableWarning"; + let checkState = { value: Services.prefs.prefHasUserValue(SHOW_DISABLE_WARNING_PREF) ? + Services.prefs.getBoolPref(SHOW_DISABLE_WARNING_PREF) : true }; + if (checkState.value) { + const webappBundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties"); + + let windowTitle = webappBundle.GetStringFromName("disable-warning.title"); + let windowText = webappBundle.GetStringFromName("disable-warning.description"); + let infoLabel = webappBundle.GetStringFromName("disable-warning.info.label"); + let infoURL = webappBundle.GetStringFromName("disable-warning.info.url"); + let showAgainLabel = webappBundle.GetStringFromName("disable-warning.show-again"); + + let buttonFlags = (Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_OK) + + (Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_IS_STRING) + + Services.prompt.BUTTON_POS_0_DEFAULT; + + let rv = Services.prompt.confirmEx( + window, + windowTitle, + windowText, + buttonFlags, + null, + infoLabel, + null, + showAgainLabel, + checkState + ); + if (rv === 1) { + var uri = Services.io.newURI(infoURL, null, null); + Cc["@mozilla.org/uriloader/external-protocol-service;1"]. + getService(Ci.nsIExternalProtocolService). + getProtocolHandlerInfo(uri.scheme). + launchWithURI(uri); + } + } + Services.prefs.setBoolPref(SHOW_DISABLE_WARNING_PREF, checkState.value); + yield WebappRT.configPromise; let appData = WebappRT.config.app; diff --git a/webapprt/locales/en-US/webapprt/webapp.properties b/webapprt/locales/en-US/webapprt/webapp.properties index 0b4018b8553..89f6990bffd 100644 --- a/webapprt/locales/en-US/webapprt/webapp.properties +++ b/webapprt/locales/en-US/webapprt/webapp.properties @@ -51,3 +51,9 @@ webapps.uninstall.dontuninstall=Don't Uninstall paymentDialog.title=Payment paymentDialog.message=Which payment provider do you want to use? + +disable-warning.title=Warning: App Will Be Disabled +disable-warning.description=This app will be disabled after you upgrade to a newer version of Firefox, which no longer includes a feature to run web apps separately from the browser. +disable-warning.info.label=More information… +disable-warning.info.url=https://support.mozilla.org/kb/runtime +disable-warning.show-again=Show this warning the next time I start this app From 7adb935017d6397b0adc9b77037a0a3be691ac91 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Tue, 1 Mar 2016 14:30:20 -0800 Subject: [PATCH 24/31] Bug 1252635 - confirm mozApps is defined before checking for updates; r=marco --- webapprt/WebappRT.jsm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webapprt/WebappRT.jsm b/webapprt/WebappRT.jsm index d4c5f186f90..50f589e87a3 100644 --- a/webapprt/WebappRT.jsm +++ b/webapprt/WebappRT.jsm @@ -102,6 +102,10 @@ this.WebappRT = { return; } + if (!window.navigator.mozApps) { + return; + } + // Check for updates once a day. let timerManager = Cc["@mozilla.org/updates/timer-manager;1"]. getService(Ci.nsIUpdateTimerManager); From 73e0d5d671e85a35d64556a645d2e2a77d278bd7 Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Fri, 26 Feb 2016 09:36:43 -0800 Subject: [PATCH 25/31] Bug 1251084 - Remove WebConsoleUtils.abbreviateSourceURL with source-utils function. r=linclark --- .../locales/en-US/webconsole.properties | 5 - .../memory/components/shortest-paths.js | 7 +- .../performance/modules/logic/marker-utils.js | 4 +- devtools/client/shared/components/frame.js | 11 +-- devtools/client/shared/source-utils.js | 70 ++++++++++++-- .../shared/test/unit/test_source-utils.js | 94 ++++++++++++++++--- .../client/shared/widgets/VariablesView.jsm | 8 +- devtools/client/webconsole/console-output.js | 5 +- devtools/client/webconsole/test/browser.ini | 1 - ...rowser_webconsole_abbreviate_source_url.js | 25 ----- .../test-console-output-dom-elements.html | 12 ++- devtools/client/webconsole/webconsole.js | 8 +- devtools/shared/webconsole/utils.js | 52 ---------- 13 files changed, 169 insertions(+), 133 deletions(-) delete mode 100644 devtools/client/webconsole/test/browser_webconsole_abbreviate_source_url.js diff --git a/devtools/client/locales/en-US/webconsole.properties b/devtools/client/locales/en-US/webconsole.properties index 13448d21426..17ace43a74b 100644 --- a/devtools/client/locales/en-US/webconsole.properties +++ b/devtools/client/locales/en-US/webconsole.properties @@ -98,11 +98,6 @@ stacktrace.anonymousFunction= # %S is the "Async Cause" of the frame. stacktrace.asyncStack=(Async: %S) -# LOCALIZATION NOTE (unknownLocation): this string is used to -# display messages with sources that have an unknown location, eg. from -# console.trace() calls. -unknownLocation= - # LOCALIZATION NOTE (timerStarted): this string is used to display the result # of the console.time() call. Parameters: %S is the name of the timer. timerStarted=%S: timer started diff --git a/devtools/client/memory/components/shortest-paths.js b/devtools/client/memory/components/shortest-paths.js index 4b154f73378..ee984cee5e9 100644 --- a/devtools/client/memory/components/shortest-paths.js +++ b/devtools/client/memory/components/shortest-paths.js @@ -12,11 +12,6 @@ const { isSavedFrame } = require("devtools/shared/DevToolsUtils"); const { getSourceNames } = require("devtools/client/shared/source-utils"); const { L10N } = require("../utils"); -const { ViewHelpers } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); -const COMPONENTS_STRINGS_URI = "chrome://devtools/locale/components.properties"; -const componentsL10N = new ViewHelpers.L10N(COMPONENTS_STRINGS_URI); -const UNKNOWN_SOURCE_STRING = componentsL10N.getStr("frame.unknownSource"); - const GRAPH_DEFAULTS = { translate: [20, 20], scale: 1 @@ -33,7 +28,7 @@ function stringifyLabel(label, id) { const piece = label[i]; if (isSavedFrame(piece)) { - const { short } = getSourceNames(piece.source, UNKNOWN_SOURCE_STRING); + const { short } = getSourceNames(piece.source); sanitized[i] = `${piece.functionDisplayName} @ ${short}:${piece.line}:${piece.column}`; } else if (piece === NO_STACK) { sanitized[i] = L10N.getStr("tree-item.nostack"); diff --git a/devtools/client/performance/modules/logic/marker-utils.js b/devtools/client/performance/modules/logic/marker-utils.js index 0edf5c292ae..b2a67bc62f4 100644 --- a/devtools/client/performance/modules/logic/marker-utils.js +++ b/devtools/client/performance/modules/logic/marker-utils.js @@ -13,7 +13,7 @@ const { Cu, Ci } = require("chrome"); const Services = require("Services"); const { L10N } = require("devtools/client/performance/modules/global"); const { TIMELINE_BLUEPRINT } = require("devtools/client/performance/modules/markers"); -const WebConsoleUtils = require("devtools/shared/webconsole/utils"); +const { getSourceNames } = require("devtools/client/shared/source-utils"); const SHOW_TRIGGER_FOR_GC_TYPES_PREF = "devtools.performance.ui.show-triggers-for-gc-types"; /** @@ -259,7 +259,7 @@ const DOM = { let urlNode = doc.createElement("label"); urlNode.className = "filename"; - urlNode.setAttribute("value", WebConsoleUtils.Utils.abbreviateSourceURL(url)); + urlNode.setAttribute("value", getSourceNames(url).short); let lineNode = doc.createElement("label"); lineNode.className = "line-number"; lineNode.setAttribute("value", `:${line}`); diff --git a/devtools/client/shared/components/frame.js b/devtools/client/shared/components/frame.js index b3d3e5f0a7c..334689df8ff 100644 --- a/devtools/client/shared/components/frame.js +++ b/devtools/client/shared/components/frame.js @@ -2,13 +2,10 @@ * 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/. */ -const { Cu } = require("chrome"); -Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); -const STRINGS_URI = "chrome://devtools/locale/components.properties"; -const L10N = new ViewHelpers.L10N(STRINGS_URI); const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react"); const { getSourceNames } = require("devtools/client/shared/source-utils"); -const UNKNOWN_SOURCE_STRING = L10N.getStr("frame.unknownSource"); +const { L10N } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm").ViewHelpers; +const l10n = new L10N("chrome://devtools/locale/components.properties"); const Frame = module.exports = createClass({ displayName: "Frame", @@ -39,7 +36,7 @@ const Frame = module.exports = createClass({ render() { let { onClick, frame, showFunctionName, showHost } = this.props; - const { short, long, host } = getSourceNames(frame.source, UNKNOWN_SOURCE_STRING); + const { short, long, host } = getSourceNames(frame.source); let tooltip = `${long}:${frame.line}`; if (frame.column) { @@ -51,7 +48,7 @@ const Frame = module.exports = createClass({ sourceString += `:${frame.column}`; } - let onClickTooltipString = L10N.getFormatStr("frame.viewsourceindebugger", sourceString); + let onClickTooltipString = l10n.getFormatStr("frame.viewsourceindebugger", sourceString); let fields = [ dom.a({ diff --git a/devtools/client/shared/source-utils.js b/devtools/client/shared/source-utils.js index 740116852c1..575dcbe7103 100644 --- a/devtools/client/shared/source-utils.js +++ b/devtools/client/shared/source-utils.js @@ -4,10 +4,16 @@ "use strict"; const { URL } = require("sdk/url"); +const { Cu } = require("chrome"); +Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); +const STRINGS_URI = "chrome://devtools/locale/components.properties"; +const L10N = new ViewHelpers.L10N(STRINGS_URI); +const UNKNOWN_SOURCE_STRING = L10N.getStr("frame.unknownSource"); // Character codes used in various parsing helper functions. const CHAR_CODE_A = "a".charCodeAt(0); const CHAR_CODE_C = "c".charCodeAt(0); +const CHAR_CODE_D = "d".charCodeAt(0); const CHAR_CODE_E = "e".charCodeAt(0); const CHAR_CODE_F = "f".charCodeAt(0); const CHAR_CODE_H = "h".charCodeAt(0); @@ -26,6 +32,8 @@ const CHAR_CODE_SLASH = "/".charCodeAt(0); // The cache used in the `nsIURL` function. const gURLStore = new Map(); +// The cache used in the `getSourceNames` function. +const gSourceNamesStore = new Map(); /** * Takes a string and returns an object containing all the properties @@ -81,17 +89,38 @@ function parseURL(location) { * * @param {String} source * The source to parse. Can be a URI or names like "(eval)" or "self-hosted". - * @param {String} unknownSourceString - * The string to use if no valid source name can be generated. * @return {Object} * An object with the following properties: * - {String} short: A short name for the source. - * - {String} long: The full, long name for the source. + * - "http://page.com/test.js#go?q=query" -> "test.js" + * - {String} long: The full, long name for the source, with hash/query stripped. + * - "http://page.com/test.js#go?q=query" -> "http://page.com/test.js" * - {String?} host: If available, the host name for the source. + * - "http://page.com/test.js#go?q=query" -> "page.com" */ -function getSourceNames (source, unknownSourceString) { +function getSourceNames (source) { + let data = gSourceNamesStore.get(source); + + if (data) { + return data; + } + let short, long, host; const sourceStr = source ? String(source) : ""; + + // If `data:...` uri + if (isDataScheme(sourceStr)) { + let commaIndex = sourceStr.indexOf(","); + if (commaIndex > -1) { + // The `short` name for a data URI becomes `data:` followed by the actual + // encoded content, omitting the MIME type, and charset. + let short = `data:${sourceStr.substring(commaIndex + 1)}`.slice(0, 100); + let result = { short, long: sourceStr }; + gSourceNamesStore.set(source, result); + return result; + } + } + const parsedUrl = parseURL(sourceStr); if (!parsedUrl) { @@ -99,19 +128,35 @@ function getSourceNames (source, unknownSourceString) { long = sourceStr; short = sourceStr.slice(0, 100); } else { - short = parsedUrl.fileName; - long = parsedUrl.href; host = parsedUrl.host; + + long = parsedUrl.href; + if (parsedUrl.hash) { + long = long.replace(parsedUrl.hash, ""); + } + if (parsedUrl.search) { + long = long.replace(parsedUrl.search, ""); + } + + short = parsedUrl.fileName; + // If `short` is just a slash, and we actually have a path, + // strip the slash and parse again to get a more useful short name. + // e.g. "http://foo.com/bar/" -> "bar", rather than "/" + if (short === "/" && parsedUrl.pathname !== "/") { + short = parseURL(long.replace(/\/$/, "")).fileName; + } } if (!short) { if (!long) { - long = unknownSourceString; + long = UNKNOWN_SOURCE_STRING; } short = long.slice(0, 100); } - return { short, long, host }; + let result = { short, long, host }; + gSourceNamesStore.set(source, result); + return result; } // For the functions below, we assume that we will never access the location @@ -126,6 +171,14 @@ function isColonSlashSlash(location, i=0) { location.charCodeAt(++i) === CHAR_CODE_SLASH; } +function isDataScheme(location, i=0) { + return location.charCodeAt(i) === CHAR_CODE_D && + location.charCodeAt(++i) === CHAR_CODE_A && + location.charCodeAt(++i) === CHAR_CODE_T && + location.charCodeAt(++i) === CHAR_CODE_A && + location.charCodeAt(++i) === CHAR_CODE_COLON; +} + function isContentScheme(location, i=0) { let firstChar = location.charCodeAt(i); @@ -208,3 +261,4 @@ exports.parseURL = parseURL; exports.getSourceNames = getSourceNames; exports.isChromeScheme = isChromeScheme; exports.isContentScheme = isContentScheme; +exports.isDataScheme = isDataScheme; diff --git a/devtools/client/shared/test/unit/test_source-utils.js b/devtools/client/shared/test/unit/test_source-utils.js index ea92ce3d159..c31af830b50 100644 --- a/devtools/client/shared/test/unit/test_source-utils.js +++ b/devtools/client/shared/test/unit/test_source-utils.js @@ -57,21 +57,89 @@ add_task(function* () { } }); +// Test `sourceUtils.isDataScheme`. +add_task(function* () { + let dataURI = "data:text/html;charset=utf-8,"; + ok(sourceUtils.isDataScheme(dataURI), `${dataURI} correctly identified as data scheme`); + + for (let url of CHROME_URLS) { + ok(!sourceUtils.isDataScheme(url), `${url} correctly identified as not data scheme`); + } + for (let url of CONTENT_URLS) { + ok(!sourceUtils.isDataScheme(url), `${url} correctly identified as not data scheme`); + } +}); + // Test `sourceUtils.getSourceNames`. add_task(function* () { - const url = "http://example.com:8888/foo/bar/baz.js"; - let results = sourceUtils.getSourceNames(url); - equal(results.short, "baz.js"); - equal(results.long, url); - equal(results.host, "example.com:8888"); - results = sourceUtils.getSourceNames("self-hosted"); - equal(results.short, "self-hosted"); - equal(results.long, "self-hosted"); - equal(results.host, undefined); + // Check length + let longMalformedURL = `example.com${new Array(100).fill("/a").join("")}/file.js`; + ok(sourceUtils.getSourceNames(longMalformedURL).short.length <= 100, + "`short` names are capped at 100 characters"); - results = sourceUtils.getSourceNames("", ""); - equal(results.short, ""); - equal(results.long, ""); - equal(results.host, undefined); + testAbbreviation("self-hosted", "self-hosted", "self-hosted"); + testAbbreviation("", "(unknown)", "(unknown)"); + + // Test shortening data URIs, stripping mime/charset + testAbbreviation("data:text/html;charset=utf-8,", + "data:", + "data:text/html;charset=utf-8,"); + + let longDataURI = `data:image/png;base64,${new Array(100).fill("a").join("")}`; + let longDataURIShort = sourceUtils.getSourceNames(longDataURI).short; + + // Test shortening data URIs and that the `short` result is capped + ok(longDataURIShort.length <= 100, + "`short` names are capped at 100 characters for data URIs"); + equal(longDataURIShort.substr(0, 10), "data:aaaaa", + "truncated data URI short names still have `data:...`"); + + testAbbreviation("http://example.com/foo/bar/baz/boo.js", + "boo.js", + "http://example.com/foo/bar/baz/boo.js", + "example.com"); + + // Check query and hash and port + testAbbreviation("http://example.com:8888/foo/bar/baz.js?q=query#go", + "baz.js", + "http://example.com:8888/foo/bar/baz.js", + "example.com:8888"); + + // Trailing "/" with nothing beyond host + testAbbreviation("http://example.com/", + "/", + "http://example.com/", + "example.com"); + + // Trailing "/" + testAbbreviation("http://example.com/foo/bar/", + "bar", + "http://example.com/foo/bar/", + "example.com"); + + // Non-extension ending + testAbbreviation("http://example.com/bar", + "bar", + "http://example.com/bar", + "example.com"); + + // Check query + testAbbreviation("http://example.com/foo.js?bar=1&baz=2", + "foo.js", + "http://example.com/foo.js", + "example.com"); + + // Check query with trailing slash + testAbbreviation("http://example.com/foo/?bar=1&baz=2", + "foo", + "http://example.com/foo/", + "example.com"); + + function testAbbreviation(source, short, long, host) { + let results = sourceUtils.getSourceNames(source); + equal(results.short, short, `${source} has correct "short" name`); + equal(results.long, long, `${source} has correct "long" name`); + equal(results.host, host, `${source} has correct "host" name`); + } }); diff --git a/devtools/client/shared/widgets/VariablesView.jsm b/devtools/client/shared/widgets/VariablesView.jsm index 7dfdafe0cc1..e8b79ac5681 100644 --- a/devtools/client/shared/widgets/VariablesView.jsm +++ b/devtools/client/shared/widgets/VariablesView.jsm @@ -24,6 +24,7 @@ Cu.import("resource://gre/modules/Task.jsm"); const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); const Services = require("Services"); +const { getSourceNames } = require("devtools/client/shared/source-utils"); const promise = require("promise"); XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", @@ -3611,8 +3612,7 @@ VariablesView.stringifiers.byObjectKind = { let result = aGrip.class; let url = aGrip.preview.url; if (!VariablesView.isFalsy({ value: url })) { - result += " \u2192 " + WebConsoleUtils.abbreviateSourceURL(url, - { onlyCropQuery: !concise }); + result += ` \u2192 ${getSourceNames(url)[concise ? "short" : "long"]}`; } return result; }, @@ -3749,9 +3749,7 @@ VariablesView.stringifiers.byObjectKind = { case Ci.nsIDOMNode.DOCUMENT_NODE: { let result = aGrip.class; if (preview.location) { - let location = WebConsoleUtils.abbreviateSourceURL(preview.location, - { onlyCropQuery: !concise }); - result += " \u2192 " + location; + result += ` \u2192 ${getSourceNames(preview.location)[concise ? "short" : "long"]}`; } return result; diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js index 9b8c90f6d13..d1318e1ff31 100644 --- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -27,6 +27,7 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; const STRINGS_URI = "chrome://devtools/locale/webconsole.properties"; const WebConsoleUtils = require("devtools/shared/webconsole/utils").Utils; +const { getSourceNames } = require("devtools/client/shared/source-utils"); const l10n = new WebConsoleUtils.L10n(STRINGS_URI); const MAX_STRING_GRIP_LENGTH = 36; @@ -2948,9 +2949,7 @@ Widgets.ObjectRenderers.add({ if (!VariablesView.isFalsy({ value: url })) { this._text(" \u2192 ", container); - let shortUrl = WebConsoleUtils.abbreviateSourceURL(url, { - onlyCropQuery: !this.options.concise - }); + let shortUrl = getSourceNames(url)[this.options.concise ? "short" : "long"]; this._anchor(shortUrl, { href: url, appendTo: container }); } diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini index 4963863309c..58efd929b87 100644 --- a/devtools/client/webconsole/test/browser.ini +++ b/devtools/client/webconsole/test/browser.ini @@ -194,7 +194,6 @@ skip-if = e10s && debug && os == 'win' [browser_repeated_messages_accuracy.js] [browser_result_format_as_string.js] [browser_warn_user_about_replaced_api.js] -[browser_webconsole_abbreviate_source_url.js] [browser_webconsole_allow_mixedcontent_securityerrors.js] tags = mcb [browser_webconsole_assert.js] diff --git a/devtools/client/webconsole/test/browser_webconsole_abbreviate_source_url.js b/devtools/client/webconsole/test/browser_webconsole_abbreviate_source_url.js deleted file mode 100644 index 67a49fba1e7..00000000000 --- a/devtools/client/webconsole/test/browser_webconsole_abbreviate_source_url.js +++ /dev/null @@ -1,25 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Tests that source URLs are abbreviated properly for display on the right- -// hand side of the Web Console. - -"use strict"; - -function test() { - testAbbreviation("http://example.com/x.js", "x.js"); - testAbbreviation("http://example.com/foo/bar/baz/boo.js", "boo.js"); - testAbbreviation("http://example.com/foo/bar/", "bar"); - testAbbreviation("http://example.com/foo.js?bar=1&baz=2", "foo.js"); - testAbbreviation("http://example.com/foo/?bar=1&baz=2", "foo"); - - finishTest(); -} - -function testAbbreviation(aFullURL, aAbbreviatedURL) { - is(WebConsoleUtils.abbreviateSourceURL(aFullURL), aAbbreviatedURL, aFullURL + - " is abbreviated to " + aAbbreviatedURL); -} - diff --git a/devtools/client/webconsole/test/test-console-output-dom-elements.html b/devtools/client/webconsole/test/test-console-output-dom-elements.html index c0605ae3c4e..8046e59185d 100644 --- a/devtools/client/webconsole/test/test-console-output-dom-elements.html +++ b/devtools/client/webconsole/test/test-console-output-dom-elements.html @@ -2,7 +2,7 @@ - Test the web console output - 05 + Test the web console output - dom elements +
- - - -
-
-        
-        
-    
- - diff --git a/devtools/client/memory/test/unit/head.js b/devtools/client/memory/test/unit/head.js index 15d2b4536ea..a019020bb37 100644 --- a/devtools/client/memory/test/unit/head.js +++ b/devtools/client/memory/test/unit/head.js @@ -73,21 +73,6 @@ function waitUntilSnapshotState (store, expected) { return waitUntilState(store, predicate); } -function isBreakdownType (report, type) { - // Little sanity check, all reports should have at least a children array. - if (!report || !Array.isArray(report.children)) { - return false; - } - switch (type) { - case "coarseType": - return report.children.find(c => c.name === "objects"); - case "allocationStack": - return report.children.find(c => c.name === "noStack"); - default: - throw new Error(`isBreakdownType does not yet support ${type}`); - } -} - function *createTempFile () { let file = FileUtils.getFile("TmpD", ["tmp.fxsnapshot"]); file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); diff --git a/devtools/client/memory/test/unit/test_action-filter-02.js b/devtools/client/memory/test/unit/test_action-filter-02.js index 7709d22a3a4..2160330fd5d 100644 --- a/devtools/client/memory/test/unit/test_action-filter-02.js +++ b/devtools/client/memory/test/unit/test_action-filter-02.js @@ -1,9 +1,10 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; // Test that changing filter state properly refreshes the selected census. -let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants"); +let { snapshotState: states } = require("devtools/client/memory/constants"); let { setFilterStringAndRefresh } = require("devtools/client/memory/actions/filter"); let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot"); @@ -11,7 +12,7 @@ function run_test() { run_next_test(); } -add_task(function *() { +add_task(function*() { let front = new StubbedMemoryFront(); let heapWorker = new HeapAnalysesClient(); yield front.attach(); diff --git a/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js b/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js index 2ca8d02fec7..e52e433466e 100644 --- a/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js +++ b/devtools/client/memory/test/unit/test_action-import-snapshot-and-census.js @@ -1,5 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; /** * Tests the task creator `importSnapshotAndCensus()` for the whole flow of @@ -7,7 +8,6 @@ */ let { actions, snapshotState: states } = require("devtools/client/memory/constants"); -let { breakdownEquals } = require("devtools/client/memory/utils"); let { exportSnapshot, importSnapshotAndCensus } = require("devtools/client/memory/actions/io"); let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); @@ -61,8 +61,8 @@ add_task(function *() { let snapshot1 = getState().snapshots[0]; let snapshot2 = getState().snapshots[1]; - ok(breakdownEquals(snapshot1.breakdown, snapshot2.breakdown), - "imported snapshot has correct breakdown"); + equal(snapshot1.display, snapshot2.display, + "imported snapshot has correct display"); // Clone the census data so we can destructively remove the ID/parents to compare // equal census data diff --git a/devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-01.js b/devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-01.js deleted file mode 100644 index 831cd8a96c6..00000000000 --- a/devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-01.js +++ /dev/null @@ -1,98 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the task creator `setBreakdownAndRefreshAndRefresh()` for breakdown changing. - * We test this rather than `setBreakdownAndRefresh` directly, as we use the refresh action - * in the app itself composed from `setBreakdownAndRefresh` - */ - -let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants"); -let { breakdownEquals } = require("devtools/client/memory/utils"); -let { setBreakdownAndRefresh } = require("devtools/client/memory/actions/breakdown"); -let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot"); - -function run_test() { - run_next_test(); -} - -add_task(function *() { - let front = new StubbedMemoryFront(); - let heapWorker = new HeapAnalysesClient(); - yield front.attach(); - let store = Store(); - let { getState, dispatch } = store; - - // Test default breakdown with no snapshots - equal(getState().breakdown.by, "coarseType", "default coarseType breakdown selected at start."); - dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown)); - equal(getState().breakdown.by, "allocationStack", "breakdown changed with no snapshots"); - - // Test invalid breakdowns - ok(getState().errors.length === 0, "No error actions in the queue."); - dispatch(setBreakdownAndRefresh(heapWorker, {})); - yield waitUntilState(store, () => getState().errors.length === 1); - ok(true, "Emits an error action when passing in an invalid breakdown object"); - - equal(getState().breakdown.by, "allocationStack", - "current breakdown unchanged when passing invalid breakdown"); - - // Test new snapshots - dispatch(takeSnapshotAndCensus(front, heapWorker)); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); - ok(isBreakdownType(getState().snapshots[0].census.report, "allocationStack"), - "New snapshots use the current, non-default breakdown"); - - - // Updates when changing breakdown during `SAVING` - dispatch(takeSnapshotAndCensus(front, heapWorker)); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING]); - dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.coarseType.breakdown)); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS]); - - ok(isBreakdownType(getState().snapshots[1].census.report, "coarseType"), - "Breakdown can be changed while saving snapshots, uses updated breakdown in census"); - - - // Updates when changing breakdown during `SAVING_CENSUS` - dispatch(takeSnapshotAndCensus(front, heapWorker)); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVING_CENSUS]); - dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown)); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]); - - ok(breakdownEquals(getState().snapshots[2].census.breakdown, breakdowns.allocationStack.breakdown), - "Breakdown can be changed while saving census, stores updated breakdown in snapshot"); - ok(isBreakdownType(getState().snapshots[2].census.report, "allocationStack"), - "Breakdown can be changed while saving census, uses updated breakdown in census"); - - // Updates census on currently selected snapshot when changing breakdown - ok(getState().snapshots[2].selected, "Third snapshot currently selected"); - dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.coarseType.breakdown)); - yield waitUntilState(store, () => isBreakdownType(getState().snapshots[2].census.report, "coarseType")); - ok(isBreakdownType(getState().snapshots[2].census.report, "coarseType"), - "Snapshot census updated when changing breakdowns after already generating one census"); - - dispatch(setBreakdownAndRefresh(heapWorker, breakdowns.allocationStack.breakdown)); - yield waitUntilState(store, () => isBreakdownType(getState().snapshots[2].census.report, "allocationStack")); - - // Does not update unselected censuses - ok(!getState().snapshots[1].selected, "Second snapshot unselected currently"); - ok(breakdownEquals(getState().snapshots[1].census.breakdown, breakdowns.coarseType.breakdown), - "Second snapshot using `coarseType` breakdown still and not yet updated to correct breakdown"); - ok(isBreakdownType(getState().snapshots[1].census.report, "coarseType"), - "Second snapshot using `coarseType` still for census and not yet updated to correct breakdown"); - - // Updates to current breakdown when switching to stale snapshot - dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id)); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING_CENSUS, states.SAVED_CENSUS]); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]); - - ok(getState().snapshots[1].selected, "Second snapshot selected currently"); - ok(breakdownEquals(getState().snapshots[1].census.breakdown, breakdowns.allocationStack.breakdown), - "Second snapshot using `allocationStack` breakdown and updated to correct breakdown"); - ok(isBreakdownType(getState().snapshots[1].census.report, "allocationStack"), - "Second snapshot using `allocationStack` for census and updated to correct breakdown"); - - heapWorker.destroy(); - yield front.detach(); -}); diff --git a/devtools/client/memory/test/unit/test_action-set-breakdown.js b/devtools/client/memory/test/unit/test_action-set-breakdown.js deleted file mode 100644 index fd692cd39c0..00000000000 --- a/devtools/client/memory/test/unit/test_action-set-breakdown.js +++ /dev/null @@ -1,45 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -/** - * Tests the action creator `setBreakdown()` for breakdown changing. - * Does not test refreshing the census information, check `setBreakdownAndRefresh` action - * for that. - */ - -let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants"); -let { setBreakdown } = require("devtools/client/memory/actions/breakdown"); -let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); - -function run_test() { - run_next_test(); -} - -add_task(function *() { - let front = new StubbedMemoryFront(); - let heapWorker = new HeapAnalysesClient(); - yield front.attach(); - let store = Store(); - let { getState, dispatch } = store; - - // Test default breakdown with no snapshots - equal(getState().breakdown.by, "coarseType", "default coarseType breakdown selected at start."); - dispatch(setBreakdown(breakdowns.allocationStack.breakdown)); - equal(getState().breakdown.by, "allocationStack", "breakdown changed with no snapshots"); - - // Test invalid breakdowns - try { - dispatch(setBreakdown({})); - ok(false, "Throws when passing in an invalid breakdown object"); - } catch (e) { - ok(true, "Throws when passing in an invalid breakdown object"); - } - equal(getState().breakdown.by, "allocationStack", - "current breakdown unchanged when passing invalid breakdown"); - - // Test new snapshots - dispatch(takeSnapshotAndCensus(front, heapWorker)); - yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); - ok(isBreakdownType(getState().snapshots[0].census.report, "allocationStack"), - "New snapshots use the current, non-default breakdown"); -}); diff --git a/devtools/client/memory/test/unit/test_action-set-display-and-refresh-01.js b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-01.js new file mode 100644 index 00000000000..918eb06fefb --- /dev/null +++ b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-01.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for display + * changing. We test this rather than `setCensusDisplayAndRefresh` directly, as + * we use the refresh action in the app itself composed from + * `setCensusDisplayAndRefresh`. + */ + +let { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants"); +let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display"); +let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot"); + +function run_test() { + run_next_test(); +} + +add_task(function *() { + let front = new StubbedMemoryFront(); + let heapWorker = new HeapAnalysesClient(); + yield front.attach(); + let store = Store(); + let { getState, dispatch } = store; + + // Test default display with no snapshots + equal(getState().censusDisplay.breakdown.by, "coarseType", + "default coarseType display selected at start."); + dispatch(setCensusDisplayAndRefresh(heapWorker, + censusDisplays.allocationStack)); + equal(getState().censusDisplay.breakdown.by, "allocationStack", + "display changed with no snapshots"); + + // Test invalid displays + ok(getState().errors.length === 0, "No error actions in the queue."); + dispatch(setCensusDisplayAndRefresh(heapWorker, {})); + yield waitUntilState(store, () => getState().errors.length === 1); + ok(true, "Emits an error action when passing in an invalid display object"); + + equal(getState().censusDisplay.breakdown.by, "allocationStack", + "current display unchanged when passing invalid display"); + + // Test new snapshots + dispatch(takeSnapshotAndCensus(front, heapWorker)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); + + + // Updates when changing display during `SAVING` + dispatch(takeSnapshotAndCensus(front, heapWorker)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING]); + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS]); + + + // Updates when changing display during `SAVING_CENSUS` + dispatch(takeSnapshotAndCensus(front, heapWorker)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVING_CENSUS]); + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]); + + equal(getState().snapshots[2].census.display, censusDisplays.allocationStack, + "Display can be changed while saving census, stores updated display in snapshot"); + + // Updates census on currently selected snapshot when changing display + ok(getState().snapshots[2].selected, "Third snapshot currently selected"); + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.coarseType)); + yield waitUntilState(store, state => state.snapshots[2].state === states.SAVED_CENSUS); + equal(getState().snapshots[2].census.display, censusDisplays.coarseType, + "Snapshot census updated when changing displays after already generating one census"); + + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack)); + yield waitUntilState(store, state => state.snapshots[2].state === states.SAVED_CENSUS); + equal(getState().snapshots[2].census.display, censusDisplays.allocationStack, + "Snapshot census updated when changing displays after already generating one census"); + + // Does not update unselected censuses + ok(!getState().snapshots[1].selected, "Second snapshot unselected currently"); + equal(getState().snapshots[1].census.display, censusDisplays.coarseType, + "Second snapshot using `coarseType` display still and not yet updated to correct display"); + + // Updates to current display when switching to stale snapshot + dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVING_CENSUS, states.SAVED_CENSUS]); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]); + + ok(getState().snapshots[1].selected, "Second snapshot selected currently"); + equal(getState().snapshots[1].census.display, censusDisplays.allocationStack, + "Second snapshot using `allocationStack` display and updated to correct display"); + + heapWorker.destroy(); + yield front.detach(); +}); diff --git a/devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-02.js b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js similarity index 56% rename from devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-02.js rename to devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js index 0170bb977aa..43b66b543b6 100644 --- a/devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-02.js +++ b/devtools/client/memory/test/unit/test_action-set-display-and-refresh-02.js @@ -1,16 +1,25 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; /** - * Tests the task creator `setBreakdownAndRefreshAndRefresh()` for custom - * breakdowns. + * Tests the task creator `setCensusDisplayAndRefreshAndRefresh()` for custom + * displays. */ let { snapshotState: states } = require("devtools/client/memory/constants"); -let { breakdownEquals } = require("devtools/client/memory/utils"); -let { setBreakdownAndRefresh } = require("devtools/client/memory/actions/breakdown"); +let { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display"); let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); -let custom = { by: "internalType", then: { by: "count", bytes: true, count: false }}; + +let CUSTOM = { + displayName: "Custom", + tooltip: "Custom tooltip", + inverted: false, + breakdown: { + by: "internalType", + then: { by: "count", bytes: true, count: false } + } +}; function run_test() { run_next_test(); @@ -23,20 +32,20 @@ add_task(function *() { let store = Store(); let { getState, dispatch } = store; - dispatch(setBreakdownAndRefresh(heapWorker, custom)); - ok(breakdownEquals(getState().breakdown, custom), - "Custom breakdown stored in breakdown state."); + dispatch(setCensusDisplayAndRefresh(heapWorker, CUSTOM)); + equal(getState().censusDisplay, CUSTOM, + "CUSTOM display stored in display state."); dispatch(takeSnapshotAndCensus(front, heapWorker)); yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); - ok(breakdownEquals(getState().snapshots[0].census.breakdown, custom), - "New snapshot stored custom breakdown when done taking census"); + equal(getState().snapshots[0].census.display, CUSTOM, + "New snapshot stored CUSTOM display when done taking census"); ok(getState().snapshots[0].census.report.children.length, "Census has some children"); // Ensure we don't have `count` in any results ok(getState().snapshots[0].census.report.children.every(c => !c.count), - "Census used custom breakdown without counts"); + "Census used CUSTOM display without counts"); // Ensure we do have `bytes` in the results ok(getState().snapshots[0].census.report.children.every(c => typeof c.bytes === "number"), - "Census used custom breakdown with bytes"); + "Census used CUSTOM display with bytes"); }); diff --git a/devtools/client/memory/test/unit/test_action-set-display.js b/devtools/client/memory/test/unit/test_action-set-display.js new file mode 100644 index 00000000000..6a4f452968d --- /dev/null +++ b/devtools/client/memory/test/unit/test_action-set-display.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * Tests the action creator `setCensusDisplay()` for display changing. Does not + * test refreshing the census information, check `setCensusDisplayAndRefresh` + * action for that. + */ + +let { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants"); +let { setCensusDisplay } = require("devtools/client/memory/actions/census-display"); +let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); + +function run_test() { + run_next_test(); +} + +add_task(function*() { + let front = new StubbedMemoryFront(); + let heapWorker = new HeapAnalysesClient(); + yield front.attach(); + let store = Store(); + let { getState, dispatch } = store; + + // Test default display with no snapshots + equal(getState().censusDisplay.breakdown.by, "coarseType", + "default coarseType display selected at start."); + + dispatch(setCensusDisplay(censusDisplays.allocationStack)); + equal(getState().censusDisplay.breakdown.by, "allocationStack", + "display changed with no snapshots"); + + // Test invalid displays + try { + dispatch(setCensusDisplay({})); + ok(false, "Throws when passing in an invalid display object"); + } catch (e) { + ok(true, "Throws when passing in an invalid display object"); + } + equal(getState().censusDisplay.breakdown.by, "allocationStack", + "current display unchanged when passing invalid display"); + + // Test new snapshots + dispatch(takeSnapshotAndCensus(front, heapWorker)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); + equal(getState().snapshots[0].census.display, censusDisplays.allocationStack, + "New snapshots use the current, non-default display"); +}); diff --git a/devtools/client/memory/test/unit/test_action-take-census.js b/devtools/client/memory/test/unit/test_action-take-census.js index 2f65aacf50a..7bf7b181c60 100644 --- a/devtools/client/memory/test/unit/test_action-take-census.js +++ b/devtools/client/memory/test/unit/test_action-take-census.js @@ -5,9 +5,7 @@ * Tests the async reducer responding to the action `takeCensus(heapWorker, snapshot)` */ -var { snapshotState: states, breakdowns } = require("devtools/client/memory/constants"); -var { breakdownEquals } = require("devtools/client/memory/utils"); -var { ERROR_TYPE } = require("devtools/client/shared/redux/middleware/task"); +var { snapshotState: states, censusDisplays } = require("devtools/client/memory/constants"); var actions = require("devtools/client/memory/actions/snapshot"); function run_test() { @@ -45,8 +43,6 @@ add_task(function *() { snapshot = store.getState().snapshots[0]; ok(snapshot.census, "Snapshot has census after saved census"); ok(snapshot.census.report.children.length, "Census is in tree node form"); - ok(isBreakdownType(snapshot.census.report, "coarseType"), - "Census is in tree node form with the default breakdown"); - ok(breakdownEquals(snapshot.census.breakdown, breakdowns.coarseType.breakdown), - "Snapshot stored correct breakdown used for the census"); + equal(snapshot.census.display, censusDisplays.coarseType, + "Snapshot stored correct display used for the census"); }); diff --git a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js index 37c6414ff32..c5885a56fdc 100644 --- a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js +++ b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-01.js @@ -1,11 +1,20 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -// Test that changing inverted state properly refreshes the selected census. +// Test that changing displays with different inverted state properly +// refreshes the selected census. -let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants"); -let { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted"); -let { takeSnapshotAndCensus, selectSnapshotAndRefresh } = require("devtools/client/memory/actions/snapshot"); +const { + censusDisplays, + snapshotState: states, +} = require("devtools/client/memory/constants"); +const { + setCensusDisplayAndRefresh +} = require("devtools/client/memory/actions/census-display"); +const { + takeSnapshotAndCensus, + selectSnapshotAndRefresh, +} = require("devtools/client/memory/actions/snapshot"); function run_test() { run_next_test(); @@ -18,7 +27,9 @@ add_task(function *() { let store = Store(); let { getState, dispatch } = store; - equal(getState().inverted, false, "not inverted by default"); + // Select a non-inverted display. + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack)); + equal(getState().censusDisplay.inverted, false, "not inverted by default"); dispatch(takeSnapshotAndCensus(front, heapWorker)); dispatch(takeSnapshotAndCensus(front, heapWorker)); @@ -29,21 +40,23 @@ add_task(function *() { states.SAVED_CENSUS]); ok(true, "saved 3 snapshots and took a census of each of them"); - dispatch(toggleInvertedAndRefresh(heapWorker)); + // Select an inverted display. + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack)); + yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVING_CENSUS]); ok(true, "toggling inverted should recompute the selected snapshot's census"); - equal(getState().inverted, true, "now inverted"); + equal(getState().censusDisplay.inverted, true, "now inverted"); yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, states.SAVED_CENSUS, states.SAVED_CENSUS]); - equal(getState().snapshots[0].census.inverted, false); - equal(getState().snapshots[1].census.inverted, false); - equal(getState().snapshots[2].census.inverted, true); + equal(getState().snapshots[0].census.display.inverted, false); + equal(getState().snapshots[1].census.display.inverted, false); + equal(getState().snapshots[2].census.display.inverted, true); dispatch(selectSnapshotAndRefresh(heapWorker, getState().snapshots[1].id)); yield waitUntilSnapshotState(store, [states.SAVED_CENSUS, @@ -55,9 +68,9 @@ add_task(function *() { states.SAVED_CENSUS, states.SAVED_CENSUS]); - equal(getState().snapshots[0].census.inverted, false); - equal(getState().snapshots[1].census.inverted, true); - equal(getState().snapshots[2].census.inverted, true); + equal(getState().snapshots[0].census.display.inverted, false); + equal(getState().snapshots[1].census.display.inverted, true); + equal(getState().snapshots[2].census.display.inverted, true); heapWorker.destroy(); yield front.detach(); diff --git a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js index 2054f174573..82eaf75349d 100644 --- a/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js +++ b/devtools/client/memory/test/unit/test_action-toggle-inverted-and-refresh-02.js @@ -1,12 +1,13 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; // Test that changing inverted state in the middle of taking a snapshot results // in an inverted census. -let { snapshotState: states } = require("devtools/client/memory/constants"); -let { toggleInverted, toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted"); -let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); +const { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants"); +const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); +const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display"); function run_test() { run_next_test(); @@ -19,26 +20,29 @@ add_task(function *() { let store = Store(); let { getState, dispatch } = store; + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack)); + equal(getState().censusDisplay.inverted, false); + dispatch(takeSnapshotAndCensus(front, heapWorker)); yield waitUntilSnapshotState(store, [states.SAVING]); - dispatch(toggleInverted()); + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack)); yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); - ok(getState().inverted, + ok(getState().censusDisplay.inverted, "should want inverted trees"); - ok(getState().snapshots[0].census.inverted, + ok(getState().snapshots[0].census.display.inverted, "snapshot-we-were-in-the-middle-of-saving's census should be inverted"); - dispatch(toggleInvertedAndRefresh(heapWorker)); + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.allocationStack)); yield waitUntilSnapshotState(store, [states.SAVING_CENSUS]); ok(true, "toggling inverted retriggers census"); - ok(!getState().inverted, "no long inverted"); + ok(!getState().censusDisplay.inverted, "no longer inverted"); - dispatch(toggleInverted()); + dispatch(setCensusDisplayAndRefresh(heapWorker, censusDisplays.invertedAllocationStack)); yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); - ok(getState().inverted, "inverted again"); - ok(getState().snapshots[0].census.inverted, + ok(getState().censusDisplay.inverted, "inverted again"); + ok(getState().snapshots[0].census.display.inverted, "census-we-were-in-the-middle-of-recomputing should be inverted again"); heapWorker.destroy(); diff --git a/devtools/client/memory/test/unit/test_action-toggle-inverted.js b/devtools/client/memory/test/unit/test_action-toggle-inverted.js index ab901731a0a..6408ae09999 100644 --- a/devtools/client/memory/test/unit/test_action-toggle-inverted.js +++ b/devtools/client/memory/test/unit/test_action-toggle-inverted.js @@ -1,29 +1,28 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; // Test toggling the top level inversion state of the tree. -let { toggleInverted } = require("devtools/client/memory/actions/inverted"); +const { censusDisplays } = require("devtools/client/memory/constants"); +const { setCensusDisplay } = require("devtools/client/memory/actions/census-display"); function run_test() { run_next_test(); } -add_task(function *() { - let front = new StubbedMemoryFront(); - let heapWorker = new HeapAnalysesClient(); - yield front.attach(); +add_task(function*() { let store = Store(); const { getState, dispatch } = store; - equal(getState().inverted, false, "not inverted by default"); + dispatch(setCensusDisplay(censusDisplays.allocationStack)); + equal(getState().censusDisplay.inverted, false, + "not inverted initially"); - dispatch(toggleInverted()); - equal(getState().inverted, true, "now inverted after toggling"); + dispatch(setCensusDisplay(censusDisplays.invertedAllocationStack)); + equal(getState().censusDisplay.inverted, true, "now inverted after toggling"); - dispatch(toggleInverted()); - equal(getState().inverted, false, "not inverted again after toggling again"); - - heapWorker.destroy(); - yield front.detach(); + dispatch(setCensusDisplay(censusDisplays.allocationStack)); + equal(getState().censusDisplay.inverted, false, + "not inverted again after toggling again"); }); diff --git a/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js b/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js index f615f555add..94699b1d20b 100644 --- a/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js +++ b/devtools/client/memory/test/unit/test_action-toggle-recording-allocations.js @@ -1,10 +1,9 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; /** - * Tests the action creator `setBreakdown()` for breakdown changing. - * Does not test refreshing the census information, check `setBreakdownAndRefresh` action - * for that. + * Test toggling the recording of allocation stacks. */ let { toggleRecordingAllocationStacks } = require("devtools/client/memory/actions/allocations"); @@ -15,7 +14,6 @@ function run_test() { add_task(function *() { let front = new StubbedMemoryFront(); - let heapWorker = new HeapAnalysesClient(); yield front.attach(); let store = Store(); const { getState, dispatch } = store; @@ -39,4 +37,6 @@ add_task(function *() { equal(getState().allocations.recording, false, "now we are not recording"); ok(front.recordingAllocations, "front is not recording anymore"); + + yield front.detach(); }); diff --git a/devtools/client/memory/test/unit/test_action_diffing_04.js b/devtools/client/memory/test/unit/test_action_diffing_04.js index f58b479cc82..e3f0c9cba02 100644 --- a/devtools/client/memory/test/unit/test_action_diffing_04.js +++ b/devtools/client/memory/test/unit/test_action_diffing_04.js @@ -15,7 +15,6 @@ const { takeSnapshot, readSnapshot } = require("devtools/client/memory/actions/snapshot"); -const { breakdownEquals } = require("devtools/client/memory/utils"); function run_test() { run_next_test(); @@ -63,11 +62,12 @@ add_task(function *() { ok(true, "And then the diff should complete."); ok(getState().diffing.census, "And we should have a census."); ok(getState().diffing.census.report, "And that census should have a report."); - ok(breakdownEquals(getState().diffing.census.breakdown, getState().breakdown), - "And that census should have the correct breakdown"); + equal(getState().diffing.census.display, getState().censusDisplay, + "And that census should have the correct display"); equal(getState().diffing.census.filter, getState().filter, "And that census should have the correct filter"); - equal(getState().diffing.census.inverted, getState().inverted, + equal(getState().diffing.census.display.inverted, + getState().censusDisplay.inverted, "And that census should have the correct inversion"); heapWorker.destroy(); diff --git a/devtools/client/memory/test/unit/test_action_diffing_05.js b/devtools/client/memory/test/unit/test_action_diffing_05.js index d664058f03d..04da1756c5a 100644 --- a/devtools/client/memory/test/unit/test_action_diffing_05.js +++ b/devtools/client/memory/test/unit/test_action_diffing_05.js @@ -6,11 +6,11 @@ const { diffingState, snapshotState, - breakdowns, + censusDisplays, } = require("devtools/client/memory/constants"); const { - setBreakdownAndRefresh, -} = require("devtools/client/memory/actions/breakdown"); + setCensusDisplayAndRefresh, +} = require("devtools/client/memory/actions/census-display"); const { toggleDiffing, selectSnapshotForDiffingAndRefresh, @@ -18,14 +18,10 @@ const { const { setFilterStringAndRefresh, } = require("devtools/client/memory/actions/filter"); -const { - toggleInvertedAndRefresh, -} = require("devtools/client/memory/actions/inverted"); const { takeSnapshot, readSnapshot, } = require("devtools/client/memory/actions/snapshot"); -const { breakdownEquals } = require("devtools/client/memory/utils"); function run_test() { run_next_test(); @@ -38,6 +34,11 @@ add_task(function *() { let store = Store(); const { getState, dispatch } = store; + yield dispatch(setCensusDisplayAndRefresh(heapWorker, + censusDisplays.allocationStack)); + equal(getState().censusDisplay.inverted, false, + "not inverted at start"); + equal(getState().diffing, null, "not diffing by default"); const s1 = yield dispatch(takeSnapshot(front, heapWorker)); @@ -61,17 +62,19 @@ add_task(function *() { const shouldTriggerRecompute = [ { name: "toggling inversion", - func: () => dispatch(toggleInvertedAndRefresh(heapWorker)) + func: () => dispatch(setCensusDisplayAndRefresh( + heapWorker, + censusDisplays.invertedAllocationStack)) }, { name: "filtering", func: () => dispatch(setFilterStringAndRefresh("scr", heapWorker)) }, { - name: "changing breakdowns", + name: "changing displays", func: () => - dispatch(setBreakdownAndRefresh(heapWorker, - breakdowns.allocationStack.breakdown)) + dispatch(setCensusDisplayAndRefresh(heapWorker, + censusDisplays.coarseType)) } ]; @@ -91,12 +94,13 @@ add_task(function *() { ok(getState().diffing.census, "And we should have a census."); ok(getState().diffing.census.report, "And that census should have a report."); - ok(breakdownEquals(getState().diffing.census.breakdown, - getState().breakdown), - "And that census should have the correct breakdown"); + equal(getState().diffing.census.display, + getState().censusDisplay, + "And that census should have the correct display"); equal(getState().diffing.census.filter, getState().filter, "And that census should have the correct filter"); - equal(getState().diffing.census.inverted, getState().inverted, + equal(getState().diffing.census.display.inverted, + getState().censusDisplay.inverted, "And that census should have the correct inversion"); } diff --git a/devtools/client/memory/test/unit/test_dominator_trees_08.js b/devtools/client/memory/test/unit/test_dominator_trees_08.js index 22e8f5446e8..c5eafbcc1ab 100644 --- a/devtools/client/memory/test/unit/test_dominator_trees_08.js +++ b/devtools/client/memory/test/unit/test_dominator_trees_08.js @@ -1,18 +1,18 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -// Test that we can change the breakdown with which we describe a dominator tree +// Test that we can change the display with which we describe a dominator tree // and that the dominator tree is re-fetched. const { snapshotState: states, dominatorTreeState, viewState, - dominatorTreeBreakdowns, + dominatorTreeDisplays, } = require("devtools/client/memory/constants"); const { - setDominatorTreeBreakdownAndRefresh -} = require("devtools/client/memory/actions/dominatorTreeBreakdown"); + setDominatorTreeDisplayAndRefresh +} = require("devtools/client/memory/actions/dominator-tree-display"); const { changeView, } = require("devtools/client/memory/actions/view"); @@ -46,34 +46,34 @@ add_task(function *() { state.snapshots[0].dominatorTree && state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED); - ok(getState().dominatorTreeBreakdown, - "We have a default breakdown for describing nodes in a dominator tree"); - equal(getState().dominatorTreeBreakdown, - dominatorTreeBreakdowns.coarseType.breakdown, + ok(getState().dominatorTreeDisplay, + "We have a default display for describing nodes in a dominator tree"); + equal(getState().dominatorTreeDisplay, + dominatorTreeDisplays.coarseType, "and the default is coarse type"); - equal(getState().dominatorTreeBreakdown, - getState().snapshots[0].dominatorTree.breakdown, - "and the newly computed dominator tree has that breakdown"); + equal(getState().dominatorTreeDisplay, + getState().snapshots[0].dominatorTree.display, + "and the newly computed dominator tree has that display"); - // Switch to the allocationStack breakdown. - dispatch(setDominatorTreeBreakdownAndRefresh( + // Switch to the allocationStack display. + dispatch(setDominatorTreeDisplayAndRefresh( heapWorker, - dominatorTreeBreakdowns.allocationStack.breakdown)); + dominatorTreeDisplays.allocationStack)); yield waitUntilState(store, state => state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING); ok(true, - "switching breakdown types caused the dominator tree to be fetched " + + "switching display types caused the dominator tree to be fetched " + "again."); yield waitUntilState(store, state => state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED); - equal(getState().snapshots[0].dominatorTree.breakdown, - dominatorTreeBreakdowns.allocationStack.breakdown, - "The new dominator tree's breakdown is allocationStack"); - equal(getState().dominatorTreeBreakdown, - dominatorTreeBreakdowns.allocationStack.breakdown, - "as is our requested dominator tree breakdown"); + equal(getState().snapshots[0].dominatorTree.display, + dominatorTreeDisplays.allocationStack, + "The new dominator tree's display is allocationStack"); + equal(getState().dominatorTreeDisplay, + dominatorTreeDisplays.allocationStack, + "as is our requested dominator tree display"); heapWorker.destroy(); yield front.detach(); diff --git a/devtools/client/memory/test/unit/test_dominator_trees_09.js b/devtools/client/memory/test/unit/test_dominator_trees_09.js index 799fa3632f9..f5cfd8c7e46 100644 --- a/devtools/client/memory/test/unit/test_dominator_trees_09.js +++ b/devtools/client/memory/test/unit/test_dominator_trees_09.js @@ -1,18 +1,18 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -// Test that we can change the breakdown with which we describe a dominator tree +// Test that we can change the display with which we describe a dominator tree // while the dominator tree is in the middle of being fetched. const { snapshotState: states, dominatorTreeState, viewState, - dominatorTreeBreakdowns, + dominatorTreeDisplays, } = require("devtools/client/memory/constants"); const { - setDominatorTreeBreakdownAndRefresh -} = require("devtools/client/memory/actions/dominatorTreeBreakdown"); + setDominatorTreeDisplayAndRefresh +} = require("devtools/client/memory/actions/dominator-tree-display"); const { changeView, } = require("devtools/client/memory/actions/view"); @@ -46,31 +46,31 @@ add_task(function *() { state.snapshots[0].dominatorTree && state.snapshots[0].dominatorTree.state === dominatorTreeState.FETCHING); - ok(getState().dominatorTreeBreakdown, - "We have a default breakdown for describing nodes in a dominator tree"); - equal(getState().dominatorTreeBreakdown, - dominatorTreeBreakdowns.coarseType.breakdown, + ok(getState().dominatorTreeDisplay, + "We have a default display for describing nodes in a dominator tree"); + equal(getState().dominatorTreeDisplay, + dominatorTreeDisplays.coarseType, "and the default is coarse type"); - equal(getState().dominatorTreeBreakdown, - getState().snapshots[0].dominatorTree.breakdown, - "and the newly computed dominator tree has that breakdown"); + equal(getState().dominatorTreeDisplay, + getState().snapshots[0].dominatorTree.display, + "and the newly computed dominator tree has that display"); - // Switch to the allocationStack breakdown while we are still fetching the + // Switch to the allocationStack display while we are still fetching the // dominator tree. - dispatch(setDominatorTreeBreakdownAndRefresh( + dispatch(setDominatorTreeDisplayAndRefresh( heapWorker, - dominatorTreeBreakdowns.allocationStack.breakdown)); + dominatorTreeDisplays.allocationStack)); // Wait for the dominator tree to finish being fetched. yield waitUntilState(store, state => state.snapshots[0].dominatorTree.state === dominatorTreeState.LOADED); - equal(getState().snapshots[0].dominatorTree.breakdown, - dominatorTreeBreakdowns.allocationStack.breakdown, - "The new dominator tree's breakdown is allocationStack"); - equal(getState().dominatorTreeBreakdown, - dominatorTreeBreakdowns.allocationStack.breakdown, - "as is our requested dominator tree breakdown"); + equal(getState().snapshots[0].dominatorTree.display, + dominatorTreeDisplays.allocationStack, + "The new dominator tree's display is allocationStack"); + equal(getState().dominatorTreeDisplay, + dominatorTreeDisplays.allocationStack, + "as is our requested dominator tree display"); heapWorker.destroy(); yield front.detach(); diff --git a/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js b/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js index a87c14a73db..eb088acaaa2 100644 --- a/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js +++ b/devtools/client/memory/test/unit/test_utils-get-snapshot-totals.js @@ -1,15 +1,16 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; /** * Tests that we use the correct snapshot aggregate value * in `utils.getSnapshotTotals(snapshot)` */ -let { breakdowns, snapshotState: states } = require("devtools/client/memory/constants"); -let { getSnapshotTotals, breakdownEquals } = require("devtools/client/memory/utils"); -let { toggleInvertedAndRefresh } = require("devtools/client/memory/actions/inverted"); -let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); +const { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants"); +const { getSnapshotTotals } = require("devtools/client/memory/utils"); +const { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot"); +const { setCensusDisplayAndRefresh } = require("devtools/client/memory/actions/census-display"); function run_test() { run_next_test(); @@ -22,12 +23,13 @@ add_task(function *() { let store = Store(); let { getState, dispatch } = store; + yield dispatch(setCensusDisplayAndRefresh(heapWorker, + censusDisplays.allocationStack)); + dispatch(takeSnapshotAndCensus(front, heapWorker)); yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); - ok(!getState().snapshots[0].census.inverted, "Snapshot is not inverted"); - ok(isBreakdownType(getState().snapshots[0].census.report, "coarseType"), - "Snapshot using `coarseType` breakdown"); + ok(!getState().snapshots[0].census.display.inverted, "Snapshot is not inverted"); let census = getState().snapshots[0].census; let result = aggregate(census.report); @@ -41,14 +43,17 @@ add_task(function *() { equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes"); equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count"); - dispatch(toggleInvertedAndRefresh(heapWorker)); + dispatch(setCensusDisplayAndRefresh(heapWorker, + censusDisplays.invertedAllocationStack)); yield waitUntilSnapshotState(store, [states.SAVING_CENSUS]); yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]); - ok(getState().snapshots[0].census.inverted, "Snapshot is inverted"); + ok(getState().snapshots[0].census.display.inverted, "Snapshot is inverted"); result = getSnapshotTotals(getState().snapshots[0].census); - equal(totalBytes, result.bytes, "getSnapshotTotals reuslted in correct bytes when inverted"); - equal(totalCount, result.count, "getSnapshotTotals reuslted in correct count when inverted"); + equal(totalBytes, result.bytes, + "getSnapshotTotals reuslted in correct bytes when inverted"); + equal(totalCount, result.count, + "getSnapshotTotals reuslted in correct count when inverted"); }); function aggregate (report) { diff --git a/devtools/client/memory/test/unit/test_utils.js b/devtools/client/memory/test/unit/test_utils.js index 9f26b3b6f0b..d6b650d6621 100644 --- a/devtools/client/memory/test/unit/test_utils.js +++ b/devtools/client/memory/test/unit/test_utils.js @@ -1,5 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; /** * Tests the task creator `takeSnapshotAndCensus()` for the whole flow of @@ -8,7 +9,7 @@ */ let utils = require("devtools/client/memory/utils"); -let { snapshotState: states, breakdowns } = require("devtools/client/memory/constants"); +let { snapshotState: states, censusDisplays } = require("devtools/client/memory/constants"); let { Preferences } = require("resource://gre/modules/Preferences.jsm"); function run_test() { @@ -16,39 +17,16 @@ function run_test() { } add_task(function *() { - ok(utils.breakdownEquals(breakdowns.allocationStack.breakdown, { - by: "allocationStack", - then: { by: "count", count: true, bytes: true }, - noStack: { by: "count", count: true, bytes: true }, - }), "utils.breakdownEquals() passes with preset"), - - ok(!utils.breakdownEquals(breakdowns.allocationStack.breakdown, { - by: "allocationStack", - then: { by: "count", count: false, bytes: true }, - noStack: { by: "count", count: true, bytes: true }, - }), "utils.breakdownEquals() fails when deep properties do not match"); - - ok(!utils.breakdownEquals(breakdowns.allocationStack.breakdown, { - by: "allocationStack", - then: { by: "count", bytes: true }, - noStack: { by: "count", count: true, bytes: true }, - }), "utils.breakdownEquals() fails when deep properties are missing."); - let s1 = utils.createSnapshot({}); let s2 = utils.createSnapshot({}); equal(s1.state, states.SAVING, "utils.createSnapshot() creates snapshot in saving state"); ok(s1.id !== s2.id, "utils.createSnapshot() creates snapshot with unique ids"); - ok(utils.breakdownEquals(utils.breakdownNameToSpec("coarseType"), breakdowns.coarseType.breakdown), - "utils.breakdownNameToSpec() works for presets"); - ok(utils.breakdownEquals(utils.breakdownNameToSpec("coarseType"), breakdowns.coarseType.breakdown), - "utils.breakdownNameToSpec() works for presets"); - let custom = { by: "internalType", then: { by: "count", bytes: true }}; - Preferences.set("devtools.memory.custom-breakdowns", JSON.stringify({ "My Breakdown": custom })); + Preferences.set("devtools.memory.custom-census-displays", JSON.stringify({ "My Display": custom })); - ok(utils.breakdownEquals(utils.getCustomBreakdowns()["My Breakdown"], custom), - "utils.getCustomBreakdowns() returns custom breakdowns"); + equal(utils.getCustomCensusDisplays()["My Display"].by, custom.by, + "utils.getCustomCensusDisplays() returns custom displays"); ok(true, "test formatNumber util functions"); equal(utils.formatNumber(12), "12", "formatNumber returns 12 for 12"); diff --git a/devtools/client/memory/test/unit/xpcshell.ini b/devtools/client/memory/test/unit/xpcshell.ini index ec744e47745..4e37adc85b2 100644 --- a/devtools/client/memory/test/unit/xpcshell.ini +++ b/devtools/client/memory/test/unit/xpcshell.ini @@ -21,9 +21,9 @@ skip-if = toolkit == 'android' || toolkit == 'gonk' [test_action-filter-03.js] [test_action-import-snapshot-and-census.js] [test_action-select-snapshot.js] -[test_action-set-breakdown.js] -[test_action-set-breakdown-and-refresh-01.js] -[test_action-set-breakdown-and-refresh-02.js] +[test_action-set-display.js] +[test_action-set-display-and-refresh-01.js] +[test_action-set-display-and-refresh-02.js] [test_action-take-census.js] [test_action-take-snapshot.js] [test_action-take-snapshot-and-census.js] diff --git a/devtools/client/memory/utils.js b/devtools/client/memory/utils.js index b122e1f5c5b..771257bb0e5 100644 --- a/devtools/client/memory/utils.js +++ b/devtools/client/memory/utils.js @@ -11,20 +11,20 @@ const L10N = exports.L10N = new ViewHelpers.L10N(STRINGS_URI); const { OS } = require("resource://gre/modules/osfile.jsm"); const { assert } = require("devtools/shared/DevToolsUtils"); const { Preferences } = require("resource://gre/modules/Preferences.jsm"); -const CUSTOM_BREAKDOWN_PREF = "devtools.memory.custom-breakdowns"; -const CUSTOM_DOMINATOR_TREE_BREAKDOWN_PREF = "devtools.memory.custom-dominator-tree-breakdowns"; +const CUSTOM_CENSUS_DISPLAY_PREF = "devtools.memory.custom-census-displays"; +const CUSTOM_DOMINATOR_TREE_DISPLAY_PREF = "devtools.memory.custom-dominator-tree-displays"; const DevToolsUtils = require("devtools/shared/DevToolsUtils"); const { snapshotState: states, diffingState, - breakdowns, - dominatorTreeBreakdowns, + censusDisplays, + dominatorTreeDisplays, dominatorTreeState } = require("./constants"); /** - * Takes a snapshot object and returns the - * localized form of its timestamp to be used as a title. + * Takes a snapshot object and returns the localized form of its timestamp to be + * used as a title. * * @param {Snapshot} snapshot * @return {String} @@ -48,160 +48,35 @@ exports.getSnapshotTitle = function (snapshot) { }); }; -/** - * Returns an array of objects with the unique key `name` - * and `displayName` for each breakdown. - * - * @return {Object{name, displayName}} - */ -exports.getBreakdownDisplayData = function () { - return exports.getBreakdownNames().map(({ name, tooltip }) => { - // If it's a preset use the display name value - let preset = breakdowns[name]; - let displayName = name; - if (preset && preset.displayName) { - displayName = preset.displayName; - } - return { name, tooltip, displayName }; - }); -}; - -/** - * Returns an array of the unique names and tooltips for each breakdown in - * presets and custom pref. - * - * @return {Array} - */ -exports.getBreakdownNames = function () { - let custom = exports.getCustomBreakdowns(); - return Object.keys(Object.assign({}, breakdowns, custom)) - .map(key => { - return breakdowns[key] - ? { name: key, tooltip: breakdowns[key].tooltip } - : { name: key }; - }); -}; - -/** - * Returns custom breakdowns defined in `devtools.memory.custom-breakdowns` pref. - * - * @return {Object} - */ -exports.getCustomBreakdowns = function () { - let customBreakdowns = Object.create(null); +function getCustomDisplaysHelper(pref) { + let customDisplays = Object.create(null); try { - customBreakdowns = JSON.parse(Preferences.get(CUSTOM_BREAKDOWN_PREF)) || Object.create(null); + customDisplays = JSON.parse(Preferences.get(pref)) || Object.create(null); } catch (e) { DevToolsUtils.reportException( - `String stored in "${CUSTOM_BREAKDOWN_PREF}" pref cannot be parsed by \`JSON.parse()\`.`); + `String stored in "${pref}" pref cannot be parsed by \`JSON.parse()\`.`); } - return customBreakdowns; + return Object.freeze(customDisplays); } /** - * Converts a breakdown preset name, like "allocationStack", and returns the - * spec for the breakdown. Also checks properties of keys in the `devtools.memory.custom-breakdowns` - * pref. If not found, returns an empty object. - * - * @param {String} name - * @return {Object} - */ -exports.breakdownNameToSpec = function (name) { - let customBreakdowns = exports.getCustomBreakdowns(); - - // If breakdown is already a breakdown, use it - if (typeof name === "object") { - return name; - } - // If it's in our custom breakdowns, use it - else if (name in customBreakdowns) { - return customBreakdowns[name]; - } - // If breakdown name is in our presets, use that - else if (name in breakdowns) { - return breakdowns[name].breakdown; - } - return Object.create(null); -}; - -/** - * Returns an array of objects with the unique key `name` and `displayName` for - * each breakdown for dominator trees. - * - * @return {Array} - */ -exports.getDominatorTreeBreakdownDisplayData = function () { - return exports.getDominatorTreeBreakdownNames().map(({ name, tooltip }) => { - // If it's a preset use the display name value - let preset = dominatorTreeBreakdowns[name]; - let displayName = name; - if (preset && preset.displayName) { - displayName = preset.displayName; - } - return { name, tooltip, displayName }; - }); -}; - -/** - * Returns an array of the unique names for each breakdown in - * presets and custom pref. - * - * @return {Array} - */ -exports.getDominatorTreeBreakdownNames = function () { - let custom = exports.getCustomDominatorTreeBreakdowns(); - return Object.keys(Object.assign({}, dominatorTreeBreakdowns, custom)) - .map(key => { - return dominatorTreeBreakdowns[key] - ? { name: key, tooltip: dominatorTreeBreakdowns[key].tooltip } - : { name: key }; - }); -}; - -/** - * Returns custom breakdowns defined in `devtools.memory.custom-dominator-tree-breakdowns` pref. + * Returns custom displays defined in `devtools.memory.custom-census-displays` + * pref. * * @return {Object} */ -exports.getCustomDominatorTreeBreakdowns = function () { - let customBreakdowns = Object.create(null); - try { - customBreakdowns = JSON.parse(Preferences.get(CUSTOM_DOMINATOR_TREE_BREAKDOWN_PREF)) || Object.create(null); - } catch (e) { - DevToolsUtils.reportException( - `String stored in "${CUSTOM_BREAKDOWN_PREF}" pref cannot be parsed by \`JSON.parse()\`.`); - } - return customBreakdowns; +exports.getCustomCensusDisplays = function () { + return getCustomDisplaysHelper(CUSTOM_CENSUS_DISPLAY_PREF); }; /** - * Converts a dominator tree breakdown preset name, like "allocationStack", and - * returns the spec for the breakdown. Also checks properties of keys in the - * `devtools.memory.custom-breakdowns` pref. If not found, returns an empty - * object. + * Returns custom displays defined in + * `devtools.memory.custom-dominator-tree-displays` pref. * - * @param {String} name * @return {Object} */ -exports.dominatorTreeBreakdownNameToSpec = function (name) { - let customBreakdowns = exports.getCustomDominatorTreeBreakdowns(); - - // If breakdown is already a breakdown, use it. - if (typeof name === "object") { - return name; - } - - // If it's in our custom breakdowns, use it. - if (name in customBreakdowns) { - return customBreakdowns[name]; - } - - // If breakdown name is in our presets, use that. - if (name in dominatorTreeBreakdowns) { - return dominatorTreeBreakdowns[name].breakdown; - } - - return Object.create(null); +exports.getCustomDominatorTreeDisplays = function () { + return getCustomDisplaysHelper(CUSTOM_DOMINATOR_TREE_DISPLAY_PREF); }; /** @@ -389,61 +264,19 @@ exports.createSnapshot = function createSnapshot(state) { }; /** - * Takes two objects and compares them deeply, returning - * a boolean indicating if they're equal or not. Used for breakdown - * comparison. + * Return true if the census is up to date with regards to the current filtering + * and requested display, false otherwise. * - * @param {Any} obj1 - * @param {Any} obj2 - * @return {Boolean} - */ -const breakdownEquals = exports.breakdownEquals = function (obj1, obj2) { - let type1 = typeof obj1; - let type2 = typeof obj2; - - // Quick checks - if (type1 !== type2 || (Array.isArray(obj1) !== Array.isArray(obj2))) { - return false; - } - - if (obj1 === obj2) { - return true; - } - - if (Array.isArray(obj1)) { - if (obj1.length !== obj2.length) { return false; } - return obj1.every((_, i) => exports.breakdownEquals(obj[1], obj2[i])); - } - else if (type1 === "object") { - let k1 = Object.keys(obj1); - let k2 = Object.keys(obj2); - - if (k1.length !== k2.length) { - return false; - } - - return k1.every(k => exports.breakdownEquals(obj1[k], obj2[k])); - } - - return false; -}; - -/** - * Return true if the census is up to date with regards to the current - * inversion/filtering/breakdown, false otherwise. - * - * @param {Boolean} inverted * @param {String} filter - * @param {Object} breakdown + * @param {censusDisplayModel} display * @param {censusModel} census * * @returns {Boolean} */ -exports.censusIsUpToDate = function (inverted, filter, breakdown, census) { +exports.censusIsUpToDate = function (filter, display, census) { return census - && inverted === census.inverted && filter === census.filter - && breakdownEquals(breakdown, census.breakdown); + && display === census.display; }; /** diff --git a/devtools/client/preferences/devtools.js b/devtools/client/preferences/devtools.js index ac149f5cd1a..86ca66d77e4 100644 --- a/devtools/client/preferences/devtools.js +++ b/devtools/client/preferences/devtools.js @@ -108,8 +108,8 @@ pref("devtools.debugger.ui.variables-searchbox-visible", false); // Enable the Memory tools pref("devtools.memory.enabled", false); -pref("devtools.memory.custom-breakdowns", "{}"); -pref("devtools.memory.custom-dominator-tree-breakdowns", "{}"); +pref("devtools.memory.custom-census-displays", "{}"); +pref("devtools.memory.custom-dominator-tree-displays", "{}"); // Enable the Performance tools pref("devtools.performance.enabled", true); diff --git a/devtools/client/themes/memory.css b/devtools/client/themes/memory.css index 7704c00bdcf..0db99ae7e3d 100644 --- a/devtools/client/themes/memory.css +++ b/devtools/client/themes/memory.css @@ -87,7 +87,7 @@ html, body, #app, #memory-tool { margin-inline-end: 5px; } -.devtools-toolbar > .toolbar-group > label.breakdown-by > span { +.devtools-toolbar > .toolbar-group > label.display-by > span { margin-inline-end: 5px; } From bcef82f6285652fb6b722f306db22d1987b0e3f5 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Tue, 1 Mar 2016 10:40:19 -0800 Subject: [PATCH 28/31] Bug 1252557 - Add ignore for HardcodedDebugMode. r=nalexander MozReview-Commit-ID: JxPAavBCqN1 --- mobile/android/app/lint.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mobile/android/app/lint.xml b/mobile/android/app/lint.xml index cab774f661c..0d0c2f24d90 100644 --- a/mobile/android/app/lint.xml +++ b/mobile/android/app/lint.xml @@ -6,6 +6,12 @@ + + + + From 13b0748aa136bd6be1e4ca106827f476a5f189d2 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Tue, 1 Mar 2016 11:10:20 -0800 Subject: [PATCH 30/31] Bug 1252557 - Suppress ParcelCreator in AndroidFxAccount. r=nalexander MozReview-Commit-ID: B2YEmQNBWTz --- .../org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java index fdfe39a811e..3160f6e04ad 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.fxa.authenticator; import android.accounts.Account; import android.accounts.AccountManager; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; @@ -772,6 +773,7 @@ public class AndroidFxAccount { }); } + @SuppressLint("ParcelCreator") // The CREATOR field is defined in the super class. private class ProfileResultReceiver extends ResultReceiver { public ProfileResultReceiver(Handler handler) { super(handler); From b79fa59841693e4ad47f31966d3c3a7a970369c6 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Tue, 1 Mar 2016 11:44:53 -0800 Subject: [PATCH 31/31] Bug 1249092 - Log profile path in debug mode. r=rnewman MozReview-Commit-ID: BhK3YFbglee --- mobile/android/base/java/org/mozilla/gecko/GeckoProfile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoProfile.java b/mobile/android/base/java/org/mozilla/gecko/GeckoProfile.java index fa06b8779f6..5fb85a82598 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoProfile.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoProfile.java @@ -265,7 +265,7 @@ public final class GeckoProfile { // We're unable to do anything sane here. throw new RuntimeException(e); } - } else { + } else if (AppConstants.DEBUG_BUILD) { Log.v(LOGTAG, "Fetching profile: '" + profileName + "', '" + profileDir + "'"); }