From d34264c0d517f3c5be7a3ca34663615360b2af41 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Fri, 25 Apr 2014 15:03:00 +0200 Subject: [PATCH 1/8] Bug 997328 - Add Scope enum to describe SharedPreferences. r=nalexander --- .../android/base/SharedPreferencesHelper.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/mobile/android/base/SharedPreferencesHelper.java b/mobile/android/base/SharedPreferencesHelper.java index da81f12b3706..e19d7ecc3153 100644 --- a/mobile/android/base/SharedPreferencesHelper.java +++ b/mobile/android/base/SharedPreferencesHelper.java @@ -27,6 +27,28 @@ public final class SharedPreferencesHelper { public static final String LOGTAG = "GeckoAndSharedPrefs"; + private enum Scope { + APP("app"), + PROFILE("profile"), + GLOBAL("global"); + + public final String key; + + private Scope(String key) { + this.key = key; + } + + public static Scope forKey(String key) { + for (Scope scope : values()) { + if (scope.key.equals(key)) { + return scope; + } + } + + throw new IllegalStateException("SharedPreferences scope must be valid."); + } + } + protected final Context mContext; // mListeners is not synchronized because it is only updated in From 2ebe5cdffaddb07ba54f93ad1de0a4dfd5393713 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Fri, 2 May 2014 15:24:00 +0200 Subject: [PATCH 2/8] Bug 997328 - Update SharedPreferences.jsm to use scopes and mirror GeckoSharedPrefs. r=nalexander --- mobile/android/base/GeckoSharedPrefs.java | 6 +- .../android/base/SharedPreferencesHelper.java | 87 +++++++++++++------ .../base/tests/testSharedPreferences.js | 35 ++++++-- .../chrome/content/aboutHealthReport.js | 4 +- mobile/android/modules/Home.jsm | 2 +- mobile/android/modules/SharedPreferences.jsm | 69 ++++++++++++--- 6 files changed, 150 insertions(+), 53 deletions(-) diff --git a/mobile/android/base/GeckoSharedPrefs.java b/mobile/android/base/GeckoSharedPrefs.java index 9aca110cfe89..dd0bfe4fd191 100644 --- a/mobile/android/base/GeckoSharedPrefs.java +++ b/mobile/android/base/GeckoSharedPrefs.java @@ -50,6 +50,9 @@ public final class GeckoSharedPrefs { // Name for app-scoped prefs public static final String APP_PREFS_NAME = "GeckoApp"; + // Used when fetching profile-scoped prefs. + public static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-"; + // The prefs key that holds the current migration private static final String PREFS_VERSION_KEY = "gecko_shared_prefs_migration"; @@ -73,9 +76,6 @@ public final class GeckoSharedPrefs { DISABLE_MIGRATIONS } - // Used when fetching profile-scoped prefs. - private static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-"; - public static SharedPreferences forApp(Context context) { return forApp(context, EnumSet.noneOf(Flags.class)); } diff --git a/mobile/android/base/SharedPreferencesHelper.java b/mobile/android/base/SharedPreferencesHelper.java index e19d7ecc3153..8c67a614eec7 100644 --- a/mobile/android/base/SharedPreferencesHelper.java +++ b/mobile/android/base/SharedPreferencesHelper.java @@ -83,12 +83,46 @@ public final class SharedPreferencesHelper "SharedPreferences:Observe"); } - private SharedPreferences getSharedPreferences(String branch) { - if (branch == null) { - return GeckoSharedPrefs.forApp(mContext); - } else { - return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE); + private SharedPreferences getSharedPreferences(JSONObject message) throws JSONException { + final Scope scope = Scope.forKey(message.getString("scope")); + switch (scope) { + case APP: + return GeckoSharedPrefs.forApp(mContext); + case PROFILE: + final String profileName = message.optString("profileName", null); + if (profileName == null) { + return GeckoSharedPrefs.forProfile(mContext); + } else { + return GeckoSharedPrefs.forProfileName(mContext, profileName); + } + case GLOBAL: + final String branch = message.optString("branch", null); + if (branch == null) { + Log.e(LOGTAG, "No branch specified for SharedPreferences; aborting."); + return null; + } else { + return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE); + } } + + return null; + } + + private String getBranch(Scope scope, String profileName, String branch) { + switch (scope) { + case APP: + return GeckoSharedPrefs.APP_PREFS_NAME; + case PROFILE: + if (profileName == null) { + profileName = GeckoProfile.get(mContext).getName(); + } + + return GeckoSharedPrefs.PROFILE_PREFS_NAME_PREFIX + profileName; + case GLOBAL: + return branch; + } + + return null; } /** @@ -101,13 +135,7 @@ public final class SharedPreferencesHelper * and an Object value. */ private void handleSet(JSONObject message) throws JSONException { - if (!message.has("branch")) { - Log.e(LOGTAG, "No branch specified for SharedPreference:Set; aborting."); - return; - } - - String branch = message.isNull("branch") ? null : message.getString("branch"); - SharedPreferences.Editor editor = getSharedPreferences(branch).edit(); + SharedPreferences.Editor editor = getSharedPreferences(message).edit(); JSONArray jsonPrefs = message.getJSONArray("preferences"); @@ -138,13 +166,7 @@ public final class SharedPreferencesHelper * "string"]. */ private JSONArray handleGet(JSONObject message) throws JSONException { - if (!message.has("branch")) { - Log.e(LOGTAG, "No branch specified for SharedPreference:Get; aborting."); - return null; - } - - String branch = message.isNull("branch") ? null : message.getString("branch"); - SharedPreferences prefs = getSharedPreferences(branch); + SharedPreferences prefs = getSharedPreferences(message); JSONArray jsonPrefs = message.getJSONArray("preferences"); JSONArray jsonValues = new JSONArray(); @@ -181,10 +203,14 @@ public final class SharedPreferencesHelper private static class ChangeListener implements SharedPreferences.OnSharedPreferenceChangeListener { + public final Scope scope; public final String branch; + public final String profileName; - public ChangeListener(final String branch) { + public ChangeListener(final Scope scope, final String branch, final String profileName) { + this.scope = scope; this.branch = branch; + this.profileName = profileName; } @Override @@ -194,7 +220,9 @@ public final class SharedPreferencesHelper } try { final JSONObject msg = new JSONObject(); + msg.put("scope", this.scope.key); msg.put("branch", this.branch); + msg.put("profileName", this.profileName); msg.put("key", key); // Truly, this is awful, but the API impedence is strong: there @@ -219,24 +247,29 @@ public final class SharedPreferencesHelper * disable listening. */ private void handleObserve(JSONObject message) throws JSONException { - if (!message.has("branch")) { + final SharedPreferences prefs = getSharedPreferences(message); + final boolean enable = message.getBoolean("enable"); + + final Scope scope = Scope.forKey(message.getString("scope")); + final String profileName = message.optString("profileName", null); + final String branch = getBranch(scope, profileName, message.optString("branch", null)); + + if (branch == null) { Log.e(LOGTAG, "No branch specified for SharedPreference:Observe; aborting."); return; } - String branch = message.isNull("branch") ? null : message.getString("branch"); - SharedPreferences prefs = getSharedPreferences(branch); - boolean enable = message.getBoolean("enable"); - // mListeners is only modified in this one observer, which is called // from Gecko serially. if (enable && !this.mListeners.containsKey(branch)) { - SharedPreferences.OnSharedPreferenceChangeListener listener = new ChangeListener(branch); + SharedPreferences.OnSharedPreferenceChangeListener listener + = new ChangeListener(scope, branch, profileName); this.mListeners.put(branch, listener); prefs.registerOnSharedPreferenceChangeListener(listener); } if (!enable && this.mListeners.containsKey(branch)) { - SharedPreferences.OnSharedPreferenceChangeListener listener = this.mListeners.remove(branch); + SharedPreferences.OnSharedPreferenceChangeListener listener + = this.mListeners.remove(branch); prefs.unregisterOnSharedPreferenceChangeListener(listener); } } diff --git a/mobile/android/base/tests/testSharedPreferences.js b/mobile/android/base/tests/testSharedPreferences.js index 373f2f17fc15..0e0ac5f75f6a 100644 --- a/mobile/android/base/tests/testSharedPreferences.js +++ b/mobile/android/base/tests/testSharedPreferences.js @@ -28,7 +28,7 @@ function makeObserver() { }; add_task(function test_get_set() { - let branch = new SharedPreferences("test"); + let branch = SharedPreferences.forAndroid("test"); branch.setBoolPref("boolKey", true); branch.setCharPref("charKey", "string value"); @@ -52,7 +52,7 @@ add_task(function test_get_set() { }); add_task(function test_default() { - let branch = new SharedPreferences(); + let branch = SharedPreferences.forAndroid(); branch.setBoolPref("boolKey", true); branch.setCharPref("charKey", "string value"); @@ -76,8 +76,8 @@ add_task(function test_default() { }); add_task(function test_multiple_branches() { - let branch1 = new SharedPreferences("test1"); - let branch2 = new SharedPreferences("test2"); + let branch1 = SharedPreferences.forAndroid("test1"); + let branch2 = SharedPreferences.forAndroid("test2"); branch1.setBoolPref("boolKey", true); branch2.setBoolPref("boolKey", false); @@ -93,7 +93,7 @@ add_task(function test_multiple_branches() { }); add_task(function test_add_remove_observer() { - let branch = new SharedPreferences("test"); + let branch = SharedPreferences.forAndroid("test"); branch.setBoolPref("boolKey", false); do_check_eq(branch.getBoolPref("boolKey"), false); @@ -145,7 +145,7 @@ add_task(function test_add_remove_observer() { }); add_task(function test_observer_ignores() { - let branch = new SharedPreferences("test"); + let branch = SharedPreferences.forAndroid("test"); branch.setCharPref("charKey", "first value"); do_check_eq(branch.getCharPref("charKey"), "first value"); @@ -176,7 +176,7 @@ add_task(function test_observer_ignores() { }); add_task(function test_observer_ignores_branches() { - let branch = new SharedPreferences("test"); + let branch = SharedPreferences.forAndroid("test"); branch.setCharPref("charKey", "first value"); do_check_eq(branch.getCharPref("charKey"), "first value"); @@ -186,9 +186,9 @@ add_task(function test_observer_ignores_branches() { try { // These should all be ignored. - let branch2 = new SharedPreferences("test2"); + let branch2 = SharedPreferences.forAndroid("test2"); branch2.setCharPref("charKey", "a wrong value"); - let branch3 = new SharedPreferences("test.2"); + let branch3 = SharedPreferences.forAndroid("test.2"); branch3.setCharPref("charKey", "a different wrong value"); // This should not be ignored. @@ -208,4 +208,21 @@ add_task(function test_observer_ignores_branches() { } }); +add_task(function test_scopes() { + let forApp = SharedPreferences.forApp(); + let forProfile = SharedPreferences.forProfile(); + let forProfileName = SharedPreferences.forProfileName("testProfile"); + let forAndroid = SharedPreferences.forAndroid(); + + forApp.setCharPref("charKey", "forApp"); + forProfile.setCharPref("charKey", "forProfile"); + forProfileName.setCharPref("charKey", "forProfileName"); + forAndroid.setCharPref("charKey", "forAndroid"); + + do_check_eq(forApp.getCharPref("charKey"), "forApp"); + do_check_eq(forProfile.getCharPref("charKey"), "forProfile"); + do_check_eq(forProfileName.getCharPref("charKey"), "forProfileName"); + do_check_eq(forAndroid.getCharPref("charKey"), "forAndroid"); +}); + run_next_test(); diff --git a/mobile/android/chrome/content/aboutHealthReport.js b/mobile/android/chrome/content/aboutHealthReport.js index 55b886176f57..d1835fedd03d 100644 --- a/mobile/android/chrome/content/aboutHealthReport.js +++ b/mobile/android/chrome/content/aboutHealthReport.js @@ -27,7 +27,7 @@ const EVENT_HEALTH_RESPONSE = "HealthReport:Response"; // about:healthreport prefs are stored in Firefox's default Android // SharedPreferences. -let sharedPrefs = new SharedPreferences(); +let sharedPrefs = SharedPreferences.forApp(); let healthReportWrapper = { init: function () { @@ -190,4 +190,4 @@ let healthReportWrapper = { }; window.addEventListener("load", healthReportWrapper.init.bind(healthReportWrapper), false); -window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false); \ No newline at end of file +window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false); diff --git a/mobile/android/modules/Home.jsm b/mobile/android/modules/Home.jsm index 7fe4ccd303ec..74df4ff0f0e7 100644 --- a/mobile/android/modules/Home.jsm +++ b/mobile/android/modules/Home.jsm @@ -457,7 +457,7 @@ let HomePanels = (function () { _assertPanelExists(id); let authKey = PREFS_PANEL_AUTH_PREFIX + id; - let sharedPrefs = new SharedPreferences(); + let sharedPrefs = SharedPreferences.forProfile(); sharedPrefs.setBoolPref(authKey, isAuthenticated); } }); diff --git a/mobile/android/modules/SharedPreferences.jsm b/mobile/android/modules/SharedPreferences.jsm index c820718e3b82..7f92c083467d 100644 --- a/mobile/android/modules/SharedPreferences.jsm +++ b/mobile/android/modules/SharedPreferences.jsm @@ -13,26 +13,65 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Messaging.jsm"); +let Scope = Object.freeze({ + APP: "app", + PROFILE: "profile", + GLOBAL: "global" +}); + +/** + * Public API to getting a SharedPreferencesImpl instance. These scopes mirror GeckoSharedPrefs. + */ +let SharedPreferences = { + forApp: function() { + return new SharedPreferencesImpl({ scope: Scope.APP }); + }, + + forProfile: function() { + return new SharedPreferencesImpl({ scope: Scope.PROFILE }); + }, + + forProfileName: function(profileName) { + return new SharedPreferencesImpl({ scope: Scope.PROFILE, profileName: profileName }); + }, + + forAndroid: function(branch) { + return new SharedPreferencesImpl({ scope: Scope.GLOBAL, branch: branch }); + } +} + /** * Create an interface to an Android SharedPreferences branch. * - * branch {String} should be a string describing a preferences branch, - * like "UpdateService" or "background.data", or null to access the - * default preferences branch for the application. + * options {Object} with the following valid keys: + * - scope {String} (required) specifies the scope of preferences that should be accessed. + * - branch {String} (only when using Scope.GLOBAL) should be a string describing a preferences branch, + * like "UpdateService" or "background.data", or null to access the + * default preferences branch for the application. + * - profileName {String} (optional, only valid when using Scope.PROFILE) */ -function SharedPreferences(branch) { - if (!(this instanceof SharedPreferences)) { - return new SharedPreferences(branch); +function SharedPreferencesImpl(options = {}) { + if (!(this instanceof SharedPreferencesImpl)) { + return new SharedPreferencesImpl(level); } - this._branch = branch || null; - this._observers = {}; -}; -SharedPreferences.prototype = Object.freeze({ + if (options.scope == null || options.scope == undefined) { + throw "Shared Preferences must specifiy a scope."; + } + + this._scope = options.scope; + this._profileName = options.profileName; + this._branch = options.branch; + this._observers = {}; +} + +SharedPreferencesImpl.prototype = Object.freeze({ _set: function _set(prefs) { sendMessageToJava({ type: "SharedPreferences:Set", preferences: prefs, + scope: this._scope, + profileName: this._profileName, branch: this._branch, }); }, @@ -64,6 +103,8 @@ SharedPreferences.prototype = Object.freeze({ sendMessageToJava({ type: "SharedPreferences:Get", preferences: prefs, + scope: this._scope, + profileName: this._profileName, branch: this._branch, }, (data) => { result = data.values; @@ -159,6 +200,8 @@ SharedPreferences.prototype = Object.freeze({ sendMessageToJava({ type: "SharedPreferences:Observe", enable: true, + scope: this._scope, + profileName: this._profileName, branch: this._branch, }); }, @@ -169,7 +212,9 @@ SharedPreferences.prototype = Object.freeze({ } let msg = JSON.parse(data); - if (msg.branch != this._branch) { + if (msg.scope !== this._scope || + (this._scope === Scope.PROFILE) && (msg.profileName !== this._profileName) || + (this._scope === Scope.GLOBAL) && (msg.branch !== this._branch)) { return; } @@ -192,6 +237,8 @@ SharedPreferences.prototype = Object.freeze({ sendMessageToJava({ type: "SharedPreferences:Observe", enable: false, + scope: this._scope, + profileName: this._profileName, branch: this._branch, }); }, From c13435e43bda93b140c091e04cd9fd6a5ae0db7e Mon Sep 17 00:00:00 2001 From: Heather Arthur Date: Fri, 2 May 2014 11:05:00 +0200 Subject: [PATCH 3/8] Bug 985234 - add media query for when box model tab is too short. r=bgrins --- browser/devtools/layoutview/view.css | 105 ++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 19 deletions(-) diff --git a/browser/devtools/layoutview/view.css b/browser/devtools/layoutview/view.css index 6e5948727f64..f68ab5c222a3 100644 --- a/browser/devtools/layoutview/view.css +++ b/browser/devtools/layoutview/view.css @@ -2,21 +2,17 @@ * 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/. */ -body, html { - height: 100%; - width: 100%; - overflow: hidden; -} - body { - margin: 0; + max-width: 320px; + position: relative; + margin: 0px auto; padding: 0; } #header { box-sizing: border-box; width: 100%; - padding: 6px 9px; + padding: 4px 13px; display: -moz-box; vertical-align: top; } @@ -38,22 +34,13 @@ body { } #main { - margin: 0 10px 10px 10px; + margin: 0 14px 10px 14px; box-sizing: border-box; - width: calc(100% - 2 * 10px); + width: calc(100% - 2 * 14px); position: absolute; border-width: 1px; } -@media (min-width: 320px) { - body { - position: absolute; - width: 320px; - left: -160px; - margin-left: 50%; - } -} - #content, #borders { border-width: 1px; @@ -194,3 +181,83 @@ body.dim > #main > p, body.dim > #main > .tooltip { visibility: hidden; } + +@media (max-height: 228px) { + #header { + padding-top: 0; + padding-bottom: 0; + margin-top: 10px; + margin-bottom: 8px; + } + + #margins, + #padding { + border-width: 21px; + } + #borders { + padding: 21px; + } + + #content { + height: 21px; + } + + .padding.top { + top: 46px; + } + + .padding.bottom { + bottom: 46px; + } + + .border.top { + top: 25px; + } + + .border.bottom { + bottom: 25px; + } + + .margin.top { + top: 4px; + } + + .margin.bottom { + bottom: 4px; + } + + .size, + .margin.left, + .margin.right, + .border.left, + .border.right, + .padding.left, + .padding.right { + line-height: 106px; + } + + .margin.right, + .margin.left, + .border.left, + .border.right, + .padding.right, + .padding.left { + width: 21px; + } + + .padding.left { + left: 43px; + } + + .padding.right { + right: 43px; + } + + .border.left { + left: 22px; + } + + .border.right { + right: 22px; + } +} From 0ed4227730bec9ec3a4877c0c546278526e8242c Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 5 May 2014 12:50:02 +0200 Subject: [PATCH 4/8] Backed out changeset 2b5863a3f0ae (bug 997328) --- mobile/android/base/GeckoSharedPrefs.java | 6 +- .../android/base/SharedPreferencesHelper.java | 87 ++++++------------- .../base/tests/testSharedPreferences.js | 35 ++------ .../chrome/content/aboutHealthReport.js | 4 +- mobile/android/modules/Home.jsm | 2 +- mobile/android/modules/SharedPreferences.jsm | 67 +++----------- 6 files changed, 52 insertions(+), 149 deletions(-) diff --git a/mobile/android/base/GeckoSharedPrefs.java b/mobile/android/base/GeckoSharedPrefs.java index dd0bfe4fd191..9aca110cfe89 100644 --- a/mobile/android/base/GeckoSharedPrefs.java +++ b/mobile/android/base/GeckoSharedPrefs.java @@ -50,9 +50,6 @@ public final class GeckoSharedPrefs { // Name for app-scoped prefs public static final String APP_PREFS_NAME = "GeckoApp"; - // Used when fetching profile-scoped prefs. - public static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-"; - // The prefs key that holds the current migration private static final String PREFS_VERSION_KEY = "gecko_shared_prefs_migration"; @@ -76,6 +73,9 @@ public final class GeckoSharedPrefs { DISABLE_MIGRATIONS } + // Used when fetching profile-scoped prefs. + private static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-"; + public static SharedPreferences forApp(Context context) { return forApp(context, EnumSet.noneOf(Flags.class)); } diff --git a/mobile/android/base/SharedPreferencesHelper.java b/mobile/android/base/SharedPreferencesHelper.java index 8c67a614eec7..e19d7ecc3153 100644 --- a/mobile/android/base/SharedPreferencesHelper.java +++ b/mobile/android/base/SharedPreferencesHelper.java @@ -83,46 +83,12 @@ public final class SharedPreferencesHelper "SharedPreferences:Observe"); } - private SharedPreferences getSharedPreferences(JSONObject message) throws JSONException { - final Scope scope = Scope.forKey(message.getString("scope")); - switch (scope) { - case APP: - return GeckoSharedPrefs.forApp(mContext); - case PROFILE: - final String profileName = message.optString("profileName", null); - if (profileName == null) { - return GeckoSharedPrefs.forProfile(mContext); - } else { - return GeckoSharedPrefs.forProfileName(mContext, profileName); - } - case GLOBAL: - final String branch = message.optString("branch", null); - if (branch == null) { - Log.e(LOGTAG, "No branch specified for SharedPreferences; aborting."); - return null; - } else { - return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE); - } + private SharedPreferences getSharedPreferences(String branch) { + if (branch == null) { + return GeckoSharedPrefs.forApp(mContext); + } else { + return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE); } - - return null; - } - - private String getBranch(Scope scope, String profileName, String branch) { - switch (scope) { - case APP: - return GeckoSharedPrefs.APP_PREFS_NAME; - case PROFILE: - if (profileName == null) { - profileName = GeckoProfile.get(mContext).getName(); - } - - return GeckoSharedPrefs.PROFILE_PREFS_NAME_PREFIX + profileName; - case GLOBAL: - return branch; - } - - return null; } /** @@ -135,7 +101,13 @@ public final class SharedPreferencesHelper * and an Object value. */ private void handleSet(JSONObject message) throws JSONException { - SharedPreferences.Editor editor = getSharedPreferences(message).edit(); + if (!message.has("branch")) { + Log.e(LOGTAG, "No branch specified for SharedPreference:Set; aborting."); + return; + } + + String branch = message.isNull("branch") ? null : message.getString("branch"); + SharedPreferences.Editor editor = getSharedPreferences(branch).edit(); JSONArray jsonPrefs = message.getJSONArray("preferences"); @@ -166,7 +138,13 @@ public final class SharedPreferencesHelper * "string"]. */ private JSONArray handleGet(JSONObject message) throws JSONException { - SharedPreferences prefs = getSharedPreferences(message); + if (!message.has("branch")) { + Log.e(LOGTAG, "No branch specified for SharedPreference:Get; aborting."); + return null; + } + + String branch = message.isNull("branch") ? null : message.getString("branch"); + SharedPreferences prefs = getSharedPreferences(branch); JSONArray jsonPrefs = message.getJSONArray("preferences"); JSONArray jsonValues = new JSONArray(); @@ -203,14 +181,10 @@ public final class SharedPreferencesHelper private static class ChangeListener implements SharedPreferences.OnSharedPreferenceChangeListener { - public final Scope scope; public final String branch; - public final String profileName; - public ChangeListener(final Scope scope, final String branch, final String profileName) { - this.scope = scope; + public ChangeListener(final String branch) { this.branch = branch; - this.profileName = profileName; } @Override @@ -220,9 +194,7 @@ public final class SharedPreferencesHelper } try { final JSONObject msg = new JSONObject(); - msg.put("scope", this.scope.key); msg.put("branch", this.branch); - msg.put("profileName", this.profileName); msg.put("key", key); // Truly, this is awful, but the API impedence is strong: there @@ -247,29 +219,24 @@ public final class SharedPreferencesHelper * disable listening. */ private void handleObserve(JSONObject message) throws JSONException { - final SharedPreferences prefs = getSharedPreferences(message); - final boolean enable = message.getBoolean("enable"); - - final Scope scope = Scope.forKey(message.getString("scope")); - final String profileName = message.optString("profileName", null); - final String branch = getBranch(scope, profileName, message.optString("branch", null)); - - if (branch == null) { + if (!message.has("branch")) { Log.e(LOGTAG, "No branch specified for SharedPreference:Observe; aborting."); return; } + String branch = message.isNull("branch") ? null : message.getString("branch"); + SharedPreferences prefs = getSharedPreferences(branch); + boolean enable = message.getBoolean("enable"); + // mListeners is only modified in this one observer, which is called // from Gecko serially. if (enable && !this.mListeners.containsKey(branch)) { - SharedPreferences.OnSharedPreferenceChangeListener listener - = new ChangeListener(scope, branch, profileName); + SharedPreferences.OnSharedPreferenceChangeListener listener = new ChangeListener(branch); this.mListeners.put(branch, listener); prefs.registerOnSharedPreferenceChangeListener(listener); } if (!enable && this.mListeners.containsKey(branch)) { - SharedPreferences.OnSharedPreferenceChangeListener listener - = this.mListeners.remove(branch); + SharedPreferences.OnSharedPreferenceChangeListener listener = this.mListeners.remove(branch); prefs.unregisterOnSharedPreferenceChangeListener(listener); } } diff --git a/mobile/android/base/tests/testSharedPreferences.js b/mobile/android/base/tests/testSharedPreferences.js index 0e0ac5f75f6a..373f2f17fc15 100644 --- a/mobile/android/base/tests/testSharedPreferences.js +++ b/mobile/android/base/tests/testSharedPreferences.js @@ -28,7 +28,7 @@ function makeObserver() { }; add_task(function test_get_set() { - let branch = SharedPreferences.forAndroid("test"); + let branch = new SharedPreferences("test"); branch.setBoolPref("boolKey", true); branch.setCharPref("charKey", "string value"); @@ -52,7 +52,7 @@ add_task(function test_get_set() { }); add_task(function test_default() { - let branch = SharedPreferences.forAndroid(); + let branch = new SharedPreferences(); branch.setBoolPref("boolKey", true); branch.setCharPref("charKey", "string value"); @@ -76,8 +76,8 @@ add_task(function test_default() { }); add_task(function test_multiple_branches() { - let branch1 = SharedPreferences.forAndroid("test1"); - let branch2 = SharedPreferences.forAndroid("test2"); + let branch1 = new SharedPreferences("test1"); + let branch2 = new SharedPreferences("test2"); branch1.setBoolPref("boolKey", true); branch2.setBoolPref("boolKey", false); @@ -93,7 +93,7 @@ add_task(function test_multiple_branches() { }); add_task(function test_add_remove_observer() { - let branch = SharedPreferences.forAndroid("test"); + let branch = new SharedPreferences("test"); branch.setBoolPref("boolKey", false); do_check_eq(branch.getBoolPref("boolKey"), false); @@ -145,7 +145,7 @@ add_task(function test_add_remove_observer() { }); add_task(function test_observer_ignores() { - let branch = SharedPreferences.forAndroid("test"); + let branch = new SharedPreferences("test"); branch.setCharPref("charKey", "first value"); do_check_eq(branch.getCharPref("charKey"), "first value"); @@ -176,7 +176,7 @@ add_task(function test_observer_ignores() { }); add_task(function test_observer_ignores_branches() { - let branch = SharedPreferences.forAndroid("test"); + let branch = new SharedPreferences("test"); branch.setCharPref("charKey", "first value"); do_check_eq(branch.getCharPref("charKey"), "first value"); @@ -186,9 +186,9 @@ add_task(function test_observer_ignores_branches() { try { // These should all be ignored. - let branch2 = SharedPreferences.forAndroid("test2"); + let branch2 = new SharedPreferences("test2"); branch2.setCharPref("charKey", "a wrong value"); - let branch3 = SharedPreferences.forAndroid("test.2"); + let branch3 = new SharedPreferences("test.2"); branch3.setCharPref("charKey", "a different wrong value"); // This should not be ignored. @@ -208,21 +208,4 @@ add_task(function test_observer_ignores_branches() { } }); -add_task(function test_scopes() { - let forApp = SharedPreferences.forApp(); - let forProfile = SharedPreferences.forProfile(); - let forProfileName = SharedPreferences.forProfileName("testProfile"); - let forAndroid = SharedPreferences.forAndroid(); - - forApp.setCharPref("charKey", "forApp"); - forProfile.setCharPref("charKey", "forProfile"); - forProfileName.setCharPref("charKey", "forProfileName"); - forAndroid.setCharPref("charKey", "forAndroid"); - - do_check_eq(forApp.getCharPref("charKey"), "forApp"); - do_check_eq(forProfile.getCharPref("charKey"), "forProfile"); - do_check_eq(forProfileName.getCharPref("charKey"), "forProfileName"); - do_check_eq(forAndroid.getCharPref("charKey"), "forAndroid"); -}); - run_next_test(); diff --git a/mobile/android/chrome/content/aboutHealthReport.js b/mobile/android/chrome/content/aboutHealthReport.js index d1835fedd03d..55b886176f57 100644 --- a/mobile/android/chrome/content/aboutHealthReport.js +++ b/mobile/android/chrome/content/aboutHealthReport.js @@ -27,7 +27,7 @@ const EVENT_HEALTH_RESPONSE = "HealthReport:Response"; // about:healthreport prefs are stored in Firefox's default Android // SharedPreferences. -let sharedPrefs = SharedPreferences.forApp(); +let sharedPrefs = new SharedPreferences(); let healthReportWrapper = { init: function () { @@ -190,4 +190,4 @@ let healthReportWrapper = { }; window.addEventListener("load", healthReportWrapper.init.bind(healthReportWrapper), false); -window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false); +window.addEventListener("unload", healthReportWrapper.uninit.bind(healthReportWrapper), false); \ No newline at end of file diff --git a/mobile/android/modules/Home.jsm b/mobile/android/modules/Home.jsm index 74df4ff0f0e7..7fe4ccd303ec 100644 --- a/mobile/android/modules/Home.jsm +++ b/mobile/android/modules/Home.jsm @@ -457,7 +457,7 @@ let HomePanels = (function () { _assertPanelExists(id); let authKey = PREFS_PANEL_AUTH_PREFIX + id; - let sharedPrefs = SharedPreferences.forProfile(); + let sharedPrefs = new SharedPreferences(); sharedPrefs.setBoolPref(authKey, isAuthenticated); } }); diff --git a/mobile/android/modules/SharedPreferences.jsm b/mobile/android/modules/SharedPreferences.jsm index 7f92c083467d..c820718e3b82 100644 --- a/mobile/android/modules/SharedPreferences.jsm +++ b/mobile/android/modules/SharedPreferences.jsm @@ -13,65 +13,26 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Messaging.jsm"); -let Scope = Object.freeze({ - APP: "app", - PROFILE: "profile", - GLOBAL: "global" -}); - -/** - * Public API to getting a SharedPreferencesImpl instance. These scopes mirror GeckoSharedPrefs. - */ -let SharedPreferences = { - forApp: function() { - return new SharedPreferencesImpl({ scope: Scope.APP }); - }, - - forProfile: function() { - return new SharedPreferencesImpl({ scope: Scope.PROFILE }); - }, - - forProfileName: function(profileName) { - return new SharedPreferencesImpl({ scope: Scope.PROFILE, profileName: profileName }); - }, - - forAndroid: function(branch) { - return new SharedPreferencesImpl({ scope: Scope.GLOBAL, branch: branch }); - } -} - /** * Create an interface to an Android SharedPreferences branch. * - * options {Object} with the following valid keys: - * - scope {String} (required) specifies the scope of preferences that should be accessed. - * - branch {String} (only when using Scope.GLOBAL) should be a string describing a preferences branch, - * like "UpdateService" or "background.data", or null to access the - * default preferences branch for the application. - * - profileName {String} (optional, only valid when using Scope.PROFILE) + * branch {String} should be a string describing a preferences branch, + * like "UpdateService" or "background.data", or null to access the + * default preferences branch for the application. */ -function SharedPreferencesImpl(options = {}) { - if (!(this instanceof SharedPreferencesImpl)) { - return new SharedPreferencesImpl(level); +function SharedPreferences(branch) { + if (!(this instanceof SharedPreferences)) { + return new SharedPreferences(branch); } - - if (options.scope == null || options.scope == undefined) { - throw "Shared Preferences must specifiy a scope."; - } - - this._scope = options.scope; - this._profileName = options.profileName; - this._branch = options.branch; + this._branch = branch || null; this._observers = {}; -} +}; -SharedPreferencesImpl.prototype = Object.freeze({ +SharedPreferences.prototype = Object.freeze({ _set: function _set(prefs) { sendMessageToJava({ type: "SharedPreferences:Set", preferences: prefs, - scope: this._scope, - profileName: this._profileName, branch: this._branch, }); }, @@ -103,8 +64,6 @@ SharedPreferencesImpl.prototype = Object.freeze({ sendMessageToJava({ type: "SharedPreferences:Get", preferences: prefs, - scope: this._scope, - profileName: this._profileName, branch: this._branch, }, (data) => { result = data.values; @@ -200,8 +159,6 @@ SharedPreferencesImpl.prototype = Object.freeze({ sendMessageToJava({ type: "SharedPreferences:Observe", enable: true, - scope: this._scope, - profileName: this._profileName, branch: this._branch, }); }, @@ -212,9 +169,7 @@ SharedPreferencesImpl.prototype = Object.freeze({ } let msg = JSON.parse(data); - if (msg.scope !== this._scope || - (this._scope === Scope.PROFILE) && (msg.profileName !== this._profileName) || - (this._scope === Scope.GLOBAL) && (msg.branch !== this._branch)) { + if (msg.branch != this._branch) { return; } @@ -237,8 +192,6 @@ SharedPreferencesImpl.prototype = Object.freeze({ sendMessageToJava({ type: "SharedPreferences:Observe", enable: false, - scope: this._scope, - profileName: this._profileName, branch: this._branch, }); }, From 6009f6cb2a6a49d2ded58e072b9077db1e03b87f Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Mon, 5 May 2014 12:51:05 +0200 Subject: [PATCH 5/8] Backed out changeset a7b80bdc8a7c (bug 997328) for Android RC testfailures --- .../android/base/SharedPreferencesHelper.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/mobile/android/base/SharedPreferencesHelper.java b/mobile/android/base/SharedPreferencesHelper.java index e19d7ecc3153..da81f12b3706 100644 --- a/mobile/android/base/SharedPreferencesHelper.java +++ b/mobile/android/base/SharedPreferencesHelper.java @@ -27,28 +27,6 @@ public final class SharedPreferencesHelper { public static final String LOGTAG = "GeckoAndSharedPrefs"; - private enum Scope { - APP("app"), - PROFILE("profile"), - GLOBAL("global"); - - public final String key; - - private Scope(String key) { - this.key = key; - } - - public static Scope forKey(String key) { - for (Scope scope : values()) { - if (scope.key.equals(key)) { - return scope; - } - } - - throw new IllegalStateException("SharedPreferences scope must be valid."); - } - } - protected final Context mContext; // mListeners is not synchronized because it is only updated in From 525e1e83ddc1be60a1bbc917759af00a674cde61 Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Mon, 5 May 2014 14:46:11 +0200 Subject: [PATCH 6/8] Bug 971628 - Avoid exceptions when entering invalid property names in the rule-view; r=miker --- .../test/browser_styleinspector_output-parser.js | 9 --------- toolkit/devtools/output-parser.js | 8 ++------ 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js b/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js index abaab6c99507..57ac8b299503 100644 --- a/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js +++ b/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js @@ -94,15 +94,6 @@ function test() { is(fragment.textContent, "#F06"); } }, - { - name: "border-top-left-color", - value: "rgba(14, 255, 20, .5)", - test: fragment => { - is(countAll(fragment), 1); - is(countColors(fragment), 1); - is(fragment.textContent, "rgba(14, 255, 20, .5)"); - } - }, { name: "border", value: "80em dotted pink", diff --git a/toolkit/devtools/output-parser.js b/toolkit/devtools/output-parser.js index 200570ebc203..f02f3f5418bc 100644 --- a/toolkit/devtools/output-parser.js +++ b/toolkit/devtools/output-parser.js @@ -294,16 +294,12 @@ OutputParser.prototype = { let win = Services.appShell.hiddenDOMWindow; let doc = win.document; - name = name.replace(/-\w{1}/g, function(match) { - return match.charAt(1).toUpperCase(); - }); - value = value.replace("!important", ""); let div = doc.createElement("div"); - div.style[name] = value; + div.style.setProperty(name, value); - return !!div.style[name]; + return !!div.style.getPropertyValue(name); }, /** From afd3d320e80aab5a40747f0639286df7919ed6e2 Mon Sep 17 00:00:00 2001 From: Irving Reid Date: Mon, 5 May 2014 08:55:58 -0400 Subject: [PATCH 7/8] Bug 1000695 - Part 1: Rewrite backgroundUpdateCheck() to Task.jsm-based async. r=Unfocused --- toolkit/mozapps/extensions/AddonManager.jsm | 172 ++++++++++---------- 1 file changed, 83 insertions(+), 89 deletions(-) diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm index 81f267068817..528a85f2b7a5 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -67,6 +67,8 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/AsyncShutdown.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", @@ -1132,120 +1134,115 @@ var AddonManagerInternal = { /** * Performs a background update check by starting an update for all add-ons * that can be updated. + * @return Promise{null} resolves when the background update check is complete + * (including all addon installs) */ backgroundUpdateCheck: function AMI_backgroundUpdateCheck() { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); - let hotfixID = this.hotfixID; + return Task.spawn(function* backgroundUpdateTask() { + let hotfixID = this.hotfixID; - let checkHotfix = hotfixID && - Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) && - Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO); + let checkHotfix = hotfixID && + Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) && + Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO); - if (!this.updateEnabled && !checkHotfix) - return; + if (!this.updateEnabled && !checkHotfix) + return; - Services.obs.notifyObservers(null, "addons-background-update-start", null); + Services.obs.notifyObservers(null, "addons-background-update-start", null); - // Start this from one to ensure the whole of this function completes before - // we can send the complete notification. Some parts can in some cases - // complete synchronously before later parts have a chance to increment - // pendingUpdates. - let pendingUpdates = 1; + if (this.updateEnabled) { + let scope = {}; + Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope); + scope.LightweightThemeManager.updateCurrentTheme(); - function notifyComplete() { - if (--pendingUpdates == 0) { - Services.obs.notifyObservers(null, - "addons-background-update-complete", - null); - } - } + let aAddons = yield new Promise((resolve, reject) => this.getAllAddons(resolve)); - if (this.updateEnabled) { - let scope = {}; - Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope); - scope.LightweightThemeManager.updateCurrentTheme(); - - pendingUpdates++; - this.getAllAddons(function getAddonsCallback(aAddons) { // If there is a known hotfix then exclude it from the list of add-ons to update. var ids = [a.id for each (a in aAddons) if (a.id != hotfixID)]; // Repopulate repository cache first, to ensure compatibility overrides // are up to date before checking for addon updates. - AddonRepository.backgroundUpdateCheck( - ids, function BUC_backgroundUpdateCheckCallback() { - pendingUpdates += aAddons.length; - aAddons.forEach(function BUC_forEachCallback(aAddon) { - if (aAddon.id == hotfixID) { - notifyComplete(); - return; - } + yield new Promise((resolve, reject) => AddonRepository.backgroundUpdateCheck(ids, resolve)); - // Check all add-ons for updates so that any compatibility updates will - // be applied + // Keep track of all the async add-on updates happening in parallel + let updates = []; + + for (let aAddon of aAddons) { + if (aAddon.id == hotfixID) { + continue; + } + + // Check all add-ons for updates so that any compatibility updates will + // be applied + updates.push(new Promise((resolve, reject) => { aAddon.findUpdates({ onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) { // Start installing updates when the add-on can be updated and // background updates should be applied. if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE && AddonManager.shouldAutoUpdate(aAddon)) { + // XXX we really should resolve when this install is done, + // not when update-available check completes, no? aInstall.install(); } }, - onUpdateFinished: notifyComplete + onUpdateFinished: resolve }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE); - }); - - notifyComplete(); - }); - }); - } - - if (checkHotfix) { - var hotfixVersion = ""; - try { - hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION); + })); + } + yield Promise.all(updates); } - catch (e) { } - let url = null; - if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING) - url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL); - else - url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL); + if (checkHotfix) { + var hotfixVersion = ""; + try { + hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION); + } + catch (e) { } - // Build the URI from a fake add-on data. - url = AddonManager.escapeAddonURI({ - id: hotfixID, - version: hotfixVersion, - userDisabled: false, - appDisabled: false - }, url); + let url = null; + if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING) + url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL); + else + url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL); - pendingUpdates++; - Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm"); - AddonUpdateChecker.checkForUpdates(hotfixID, null, url, { - onUpdateCheckComplete: function BUC_onUpdateCheckComplete(aUpdates) { - let update = AddonUpdateChecker.getNewestCompatibleUpdate(aUpdates); - if (!update) { - notifyComplete(); - return; - } + // Build the URI from a fake add-on data. + url = AddonManager.escapeAddonURI({ + id: hotfixID, + version: hotfixVersion, + userDisabled: false, + appDisabled: false + }, url); - // If the available version isn't newer than the last installed - // version then ignore it. - if (Services.vc.compare(hotfixVersion, update.version) >= 0) { - notifyComplete(); - return; - } + Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm"); + let update = null; + try { + let foundUpdates = yield new Promise((resolve, reject) => { + AddonUpdateChecker.checkForUpdates(hotfixID, null, url, { + onUpdateCheckComplete: resolve, + onUpdateCheckError: reject + }); + }); + update = AddonUpdateChecker.getNewestCompatibleUpdate(foundUpdates); + } catch (e) { + // AUC.checkForUpdates already logged the error + } + + // Check that we have a hotfix update, and it's newer than the one we already + // have installed (if any) + if (update) { + if (Services.vc.compare(hotfixVersion, update.version) < 0) { + logger.debug("Downloading hotfix version " + update.version); + let aInstall = yield new Promise((resolve, reject) => + AddonManager.getInstallForURL(update.updateURL, resolve, + "application/x-xpinstall", update.updateHash, null, + null, update.version)); - logger.debug("Downloading hotfix version " + update.version); - AddonManager.getInstallForURL(update.updateURL, - function BUC_getInstallForURL(aInstall) { aInstall.addListener({ onDownloadEnded: function BUC_onDownloadEnded(aInstall) { try { @@ -1283,17 +1280,14 @@ var AddonManagerInternal = { }); aInstall.install(); + } + } + } - notifyComplete(); - }, "application/x-xpinstall", update.updateHash, null, - null, update.version); - }, - - onUpdateCheckError: notifyComplete - }); - } - - notifyComplete(); + Services.obs.notifyObservers(null, + "addons-background-update-complete", + null); + }.bind(this)); }, /** From 7ca4333a3ff2d6903ca2f150942a881011c26a90 Mon Sep 17 00:00:00 2001 From: Irving Reid Date: Thu, 1 May 2014 22:49:49 -0400 Subject: [PATCH 8/8] Bug 1000695 - Part 2: Rewrite browser_hotfix.js to Task.jsm and yield to event loop after receiving onInstallEnded. r=Unfocused --- toolkit/mozapps/extensions/AddonManager.jsm | 9 +- .../extensions/internal/XPIProvider.jsm | 8 +- .../test/browser/browser_bug557956.js | 2 +- .../test/browser/browser_bug562992.js | 4 +- .../test/browser/browser_bug572561.js | 5 +- .../test/browser/browser_bug577990.js | 2 +- .../test/browser/browser_bug591663.js | 4 +- .../extensions/test/browser/browser_hotfix.js | 199 ++++++++---------- .../test/browser/browser_manualupdates.js | 2 +- .../test/browser/browser_searching.js | 2 +- .../browser/browser_select_compatoverrides.js | 2 +- 11 files changed, 117 insertions(+), 122 deletions(-) diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm index 528a85f2b7a5..ac5e1511144d 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -464,6 +464,7 @@ var gUpdateEnabled = true; var gAutoUpdateDefault = true; var gHotfixID = null; +var gUpdateCheckInProgress = false; /** * This is the real manager, kept here rather than in AddonManager to keep its * contents hidden from API users. @@ -479,7 +480,6 @@ var AddonManagerInternal = { // Store telemetry details per addon provider telemetryDetails: {}, - // A read-only wrapper around the types dictionary typesProxy: Proxy.create({ getOwnPropertyDescriptor: function typesProxy_getOwnPropertyDescriptor(aName) { @@ -1142,6 +1142,12 @@ var AddonManagerInternal = { throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); + if (gUpdateCheckInProgress) { + throw Components.Exception("Background update check already in progress", + Cr.NS_ERROR_UNEXPECTED); + } + gUpdateCheckInProgress = true; + return Task.spawn(function* backgroundUpdateTask() { let hotfixID = this.hotfixID; @@ -1284,6 +1290,7 @@ var AddonManagerInternal = { } } + gUpdateCheckInProgress = false; Services.obs.notifyObservers(null, "addons-background-update-complete", null); diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 89aa25d356a8..5523867ecd0f 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -3610,7 +3610,13 @@ this.XPIProvider = { * The AddonInstall to remove */ removeActiveInstall: function XPI_removeActiveInstall(aInstall) { - this.installs = this.installs.filter(function installFilter(i) i != aInstall); + let where = this.installs.indexOf(aInstall); + if (where == -1) { + logger.warn("removeActiveInstall: could not find active install for " + + aInstall.sourceURI.spec); + return; + } + this.installs.splice(where, 1); }, /** diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug557956.js b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js index 581639aa5ca1..a39fc7f1e4aa 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_bug557956.js +++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js @@ -75,7 +75,7 @@ function install_test_addons(aCallback) { // Switch to the test update URL Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "browser_bug557956.rdf"); - aCallback(); + executeSoon(aCallback); } } }; diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug562992.js b/toolkit/mozapps/extensions/test/browser/browser_bug562992.js index 10bf329f0a7e..245edb8a5f29 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_bug562992.js +++ b/toolkit/mozapps/extensions/test/browser/browser_bug562992.js @@ -41,7 +41,7 @@ add_test(function () { gInstall = new MockInstall(undefined, undefined, addon); gInstall.addTestListener({ onNewInstall: function () { - run_next_test(); + executeSoon(run_next_test); } }); gProvider.addInstall(gInstall); @@ -65,7 +65,7 @@ add_test(function () { } } ok(false, "Item with correct name was not found"); - run_next_test(); + executeSoon(run_next_test); } }); gInstall.install(); diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug572561.js b/toolkit/mozapps/extensions/test/browser/browser_bug572561.js index 86dad46d886f..97c602c11d49 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_bug572561.js +++ b/toolkit/mozapps/extensions/test/browser/browser_bug572561.js @@ -21,17 +21,18 @@ var gTestInstallListener = { onInstallEnded: function(aInstall) { check_hidden(false); - run_next_test(); + executeSoon(run_next_test); }, onInstallCancelled: function(aInstall) { ok(gExpectedCancel, "Should expect install cancel"); check_hidden(false); - run_next_test(); + executeSoon(run_next_test); }, onInstallFailed: function(aInstall) { ok(false, "Did not expect onInstallFailed"); + executeSoon(run_next_test); } }; diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug577990.js b/toolkit/mozapps/extensions/test/browser/browser_bug577990.js index 9b4ab3f8329d..2c3c7ba5a622 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_bug577990.js +++ b/toolkit/mozapps/extensions/test/browser/browser_bug577990.js @@ -40,7 +40,7 @@ function install_locale(aCallback) { gInstall.addTestListener({ onInstallEnded: function(aInstall) { gInstall.removeTestListener(this); - aCallback(); + executeSoon(aCallback); } }); gInstall.install(); diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug591663.js b/toolkit/mozapps/extensions/test/browser/browser_bug591663.js index 0736aa391919..7a65cc4c553a 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_bug591663.js +++ b/toolkit/mozapps/extensions/test/browser/browser_bug591663.js @@ -97,7 +97,7 @@ add_test(function() { }, onInstallEnded: function() { check_list(gItem); - run_next_test(); + executeSoon(run_next_test); } }); @@ -136,7 +136,7 @@ add_test(function() { onInstallEnded: function() { check_list(null); extension.cancel(); - run_next_test(); + executeSoon(run_next_test); } }); diff --git a/toolkit/mozapps/extensions/test/browser/browser_hotfix.js b/toolkit/mozapps/extensions/test/browser/browser_hotfix.js index ff288898306f..77d3eb430149 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_hotfix.js +++ b/toolkit/mozapps/extensions/test/browser/browser_hotfix.js @@ -15,51 +15,56 @@ const PREF_APP_UPDATE_ENABLED = "app.update.enabled"; const HOTFIX_ID = "hotfix@tests.mozilla.org"; -var gNextTest; - -var SuccessfulInstallListener = { - onDownloadCancelled: function(aInstall) { - ok(false, "Should not have seen the download cancelled"); - is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on"); - - AddonManager.removeInstallListener(this); - gNextTest(); - }, - - onInstallEnded: function(aInstall) { - ok(true, "Should have seen the install complete"); - is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on"); - - AddonManager.removeInstallListener(this); - aInstall.addon.uninstall(); - Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION); - gNextTest(); - } +/* + * Register an addon install listener and return a promise that: + * resolves with the AddonInstall object if the install succeeds + * rejects with the AddonInstall if the install fails + */ +function promiseInstallListener() { + return new Promise((resolve, reject) => { + let listener = { + onInstallEnded: ai => { + AddonManager.removeInstallListener(listener); + resolve(ai); + }, + onDownloadCancelled: ai => { + AddonManager.removeInstallListener(listener); + reject(ai); + } + }; + AddonManager.addInstallListener(listener); + }); } -var FailedInstallListener = { - onDownloadCancelled: function(aInstall) { - ok(true, "Should have seen the download cancelled"); - is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on"); - - AddonManager.removeInstallListener(this); - gNextTest(); - }, - - onInstallEnded: function(aInstall) { - ok(false, "Should not have seen the install complete"); - is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on"); - - AddonManager.removeInstallListener(this); - aInstall.addon.uninstall(); - Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION); - gNextTest(); - } +function promiseSuccessfulInstall() { + return promiseInstallListener().then( + aInstall => { + ok(true, "Should have seen the install complete"); + is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on"); + aInstall.addon.uninstall(); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION); + }, + aInstall => { + ok(false, "Should not have seen the download cancelled"); + is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on"); + }); } -function test() { - waitForExplicitFinish(); +function promiseFailedInstall() { + return promiseInstallListener().then( + aInstall => { + ok(false, "Should not have seen the install complete"); + is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on"); + aInstall.addon.uninstall(); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION); + }, + aInstall => { + ok(true, "Should have seen the download cancelled"); + is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on"); + }); +} +add_task(function setup() { Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true); Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false); Services.prefs.setBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, false); @@ -78,109 +83,85 @@ function test() { var prefs = Services.prefs.getChildList(PREF_EM_HOTFIX_CERTS); prefs.forEach(Services.prefs.clearUserPref); }); - - run_next_test(); -} - -function end_test() { - finish(); -} - -add_test(function check_no_cert_checks() { - Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false); - AddonManager.addInstallListener(SuccessfulInstallListener); - - gNextTest = run_next_test; - - AddonManagerPrivate.backgroundUpdateCheck(); }); -add_test(function check_wrong_cert_fingerprint() { +add_task(function* check_no_cert_checks() { + Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false); + yield Promise.all([ + promiseSuccessfulInstall(), + AddonManagerPrivate.backgroundUpdateCheck() + ]); +}); + +add_task(function* check_wrong_cert_fingerprint() { Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true); Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo"); - AddonManager.addInstallListener(FailedInstallListener); - - gNextTest = function() { - Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); - - run_next_test(); - }; - - AddonManagerPrivate.backgroundUpdateCheck(); + yield Promise.all([ + promiseFailedInstall(), + AddonManagerPrivate.backgroundUpdateCheck() + ]); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); }); -add_test(function check_right_cert_fingerprint() { +add_task(function* check_right_cert_fingerprint() { Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true); Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2"); - AddonManager.addInstallListener(SuccessfulInstallListener); + yield Promise.all([ + promiseSuccessfulInstall(), + AddonManagerPrivate.backgroundUpdateCheck() + ]); - gNextTest = function() { - Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); - - run_next_test(); - }; - - AddonManagerPrivate.backgroundUpdateCheck(); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); }); -add_test(function check_multi_cert_fingerprint_1() { +add_task(function* check_multi_cert_fingerprint_1() { Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true); Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2"); Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "foo"); - AddonManager.addInstallListener(SuccessfulInstallListener); + yield Promise.all([ + promiseSuccessfulInstall(), + AddonManagerPrivate.backgroundUpdateCheck() + ]); - gNextTest = function() { - Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); - Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint"); - - run_next_test(); - }; - - AddonManagerPrivate.backgroundUpdateCheck(); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint"); }); -add_test(function check_multi_cert_fingerprint_2() { +add_task(function* check_multi_cert_fingerprint_2() { Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true); Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo"); Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2"); - AddonManager.addInstallListener(SuccessfulInstallListener); + yield Promise.all([ + promiseSuccessfulInstall(), + AddonManagerPrivate.backgroundUpdateCheck() + ]); - gNextTest = function() { - Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); - Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint"); - - run_next_test(); - }; - - AddonManagerPrivate.backgroundUpdateCheck(); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint"); }); -add_test(function check_no_cert_no_checks() { +add_task(function* check_no_cert_no_checks() { Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false); Services.prefs.setCharPref(PREF_EM_HOTFIX_URL, TESTROOT + "unsigned_hotfix.rdf"); - AddonManager.addInstallListener(SuccessfulInstallListener); - - gNextTest = run_next_test; - - AddonManagerPrivate.backgroundUpdateCheck(); + yield Promise.all([ + promiseSuccessfulInstall(), + AddonManagerPrivate.backgroundUpdateCheck() + ]); }); -add_test(function check_no_cert_cert_fingerprint_check() { +add_task(function* check_no_cert_cert_fingerprint_check() { Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true); Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2"); - AddonManager.addInstallListener(FailedInstallListener); + yield Promise.all([ + promiseFailedInstall(), + AddonManagerPrivate.backgroundUpdateCheck() + ]); - gNextTest = function() { - Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); - - run_next_test(); - }; - - AddonManagerPrivate.backgroundUpdateCheck(); + Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint"); }); diff --git a/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js b/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js index 27a4a6cd801a..3c0de42bc28d 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js +++ b/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js @@ -184,7 +184,7 @@ add_test(function() { is_element_hidden(item._installStatus, "Install progress widget should be hidden"); if (badgeUpdated) - run_next_test(); + executeSoon(run_next_test); else installCompleted = true; } diff --git a/toolkit/mozapps/extensions/test/browser/browser_searching.js b/toolkit/mozapps/extensions/test/browser/browser_searching.js index 9e03e82973d7..7c29ff0fc190 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_searching.js +++ b/toolkit/mozapps/extensions/test/browser/browser_searching.js @@ -566,7 +566,7 @@ add_test(function() { is(installBtn.hidden, true, "Install button should be hidden after install ended"); check_filtered_results(QUERY, "relevancescore", false); - run_next_test(); + executeSoon(run_next_test); } } diff --git a/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js index 7735f1090671..747811e6372e 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js +++ b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js @@ -30,7 +30,7 @@ function install_test_addon(aCallback) { onInstallEnded: function() { AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) { gTestAddon = addon; - aCallback(); + executeSoon(aCallback); }); } };