author | Jim Chen <nchen@mozilla.com> |
Tue, 10 Jan 2017 23:01:28 -0500 | |
changeset 328900 | 4d1775e57bcce9e4475c95552cc41f630b31810b |
parent 328899 | 3a5de4214c9ec365478afce5a32a73009c5b747e |
child 328901 | a365beac4010f8addfb04aa6a37bb40ef1fec118 |
push id | 31191 |
push user | cbook@mozilla.com |
push date | Wed, 11 Jan 2017 15:24:19 +0000 |
treeherder | mozilla-central@63ad56438630 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | sebastian |
bugs | 1328684 |
milestone | 53.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/java/org/mozilla/gecko/SharedPreferencesHelper.java +++ b/mobile/android/base/java/org/mozilla/gecko/SharedPreferencesHelper.java @@ -1,34 +1,33 @@ /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * 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/. */ package org.mozilla.gecko; import org.mozilla.gecko.EventDispatcher; -import org.mozilla.gecko.util.GeckoEventListener; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; +import org.mozilla.gecko.util.BundleEventListener; +import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.GeckoBundle; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; import java.util.Map; import java.util.HashMap; /** * Helper class to get, set, and observe Android Shared Preferences. */ public final class SharedPreferencesHelper - implements GeckoEventListener + implements BundleEventListener { public static final String LOGTAG = "GeckoAndSharedPrefs"; // Calculate this once, at initialization. isLoggable is too expensive to // have in-line in each log call. private static final boolean logVerbose = Log.isLoggable(LOGTAG, Log.VERBOSE); private enum Scope { @@ -72,30 +71,30 @@ public final class SharedPreferencesHelp public synchronized void uninit() { EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "SharedPreferences:Set", "SharedPreferences:Get", "SharedPreferences:Observe"); } - private SharedPreferences getSharedPreferences(JSONObject message) throws JSONException { + private SharedPreferences getSharedPreferences(final GeckoBundle message) { 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); + final String profileName = message.getString("profileName", null); if (profileName == null) { return GeckoSharedPrefs.forProfile(mContext); } else { return GeckoSharedPrefs.forProfileName(mContext, profileName); } case GLOBAL: - final String branch = message.optString("branch", null); + final String branch = message.getString("branch", null); if (branch == null) { return PreferenceManager.getDefaultSharedPreferences(mContext); } else { return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE); } } return null; @@ -122,135 +121,150 @@ public final class SharedPreferencesHelp * Set many SharedPreferences in Android. * * message.branch must exist, and should be a String SharedPreferences * branch name, or null for the default branch. * message.preferences should be an array of preferences. Each preference * must include a String name, a String type in ["bool", "int", "string"], * and an Object value. */ - private void handleSet(JSONObject message) throws JSONException { + private void handleSet(final GeckoBundle message) { SharedPreferences.Editor editor = getSharedPreferences(message).edit(); - JSONArray jsonPrefs = message.getJSONArray("preferences"); + final GeckoBundle[] bundlePrefs = message.getBundleArray("preferences"); - for (int i = 0; i < jsonPrefs.length(); i++) { - JSONObject pref = jsonPrefs.getJSONObject(i); - String name = pref.getString("name"); - String type = pref.getString("type"); + for (int i = 0; i < bundlePrefs.length; i++) { + final GeckoBundle pref = bundlePrefs[i]; + final String name = pref.getString("name"); + final String type = pref.getString("type"); if ("bool".equals(type)) { editor.putBoolean(name, pref.getBoolean("value")); } else if ("int".equals(type)) { editor.putInt(name, pref.getInt("value")); } else if ("string".equals(type)) { editor.putString(name, pref.getString("value")); } else { Log.w(LOGTAG, "Unknown pref value type [" + type + "] for pref [" + name + "]"); } - editor.apply(); } + editor.apply(); } /** * Get many SharedPreferences from Android. * * message.branch must exist, and should be a String SharedPreferences * branch name, or null for the default branch. * message.preferences should be an array of preferences. Each preference * must include a String name, and a String type in ["bool", "int", * "string"]. */ - private JSONArray handleGet(JSONObject message) throws JSONException { - SharedPreferences prefs = getSharedPreferences(message); - JSONArray jsonPrefs = message.getJSONArray("preferences"); - JSONArray jsonValues = new JSONArray(); + private GeckoBundle[] handleGet(final GeckoBundle message) { + final SharedPreferences prefs = getSharedPreferences(message); + final GeckoBundle[] bundlePrefs = message.getBundleArray("preferences"); + final GeckoBundle[] bundleValues = new GeckoBundle[bundlePrefs.length]; - for (int i = 0; i < jsonPrefs.length(); i++) { - JSONObject pref = jsonPrefs.getJSONObject(i); - String name = pref.getString("name"); - String type = pref.getString("type"); - JSONObject jsonValue = new JSONObject(); - jsonValue.put("name", name); - jsonValue.put("type", type); + for (int i = 0; i < bundlePrefs.length; i++) { + final GeckoBundle pref = bundlePrefs[i]; + final String name = pref.getString("name"); + final String type = pref.getString("type"); + final GeckoBundle bundleValue = new GeckoBundle(3); + bundleValue.put("name", name); + bundleValue.put("type", type); try { if ("bool".equals(type)) { - boolean value = prefs.getBoolean(name, false); - jsonValue.put("value", value); + bundleValue.put("value", prefs.getBoolean(name, false)); } else if ("int".equals(type)) { - int value = prefs.getInt(name, 0); - jsonValue.put("value", value); + bundleValue.put("value", prefs.getInt(name, 0)); } else if ("string".equals(type)) { - String value = prefs.getString(name, ""); - jsonValue.put("value", value); + bundleValue.put("value", prefs.getString(name, "")); } else { Log.w(LOGTAG, "Unknown pref value type [" + type + "] for pref [" + name + "]"); } - } catch (ClassCastException e) { + } catch (final ClassCastException e) { // Thrown if there is a preference with the given name that is // not the right type. - Log.w(LOGTAG, "Wrong pref value type [" + type + "] for pref [" + name + "]"); + Log.w(LOGTAG, "Wrong pref value type [" + type + "] for pref [" + name + "]", e); } - jsonValues.put(jsonValue); + bundleValues[i] = bundleValue; } - return jsonValues; + return bundleValues; } 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; this.branch = branch; this.profileName = profileName; } + private static void putSharedPreference(final GeckoBundle msg, + final SharedPreferences sharedPreferences, + final String key) { + // Truly, this is awful, but the API impedance is strong: there is no way to + // get a single untyped value from a SharedPreferences instance. + + try { + msg.putBoolean("value", sharedPreferences.getBoolean(key, false)); + return; + } catch (final ClassCastException e) { + } + + try { + msg.putInt("value", sharedPreferences.getInt(key, 0)); + return; + } catch (final ClassCastException e) { + } + + try { + msg.putString("value", sharedPreferences.getString(key, "")); + return; + } catch (final ClassCastException e) { + } + } + @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, + final String key) { if (logVerbose) { Log.v(LOGTAG, "Got onSharedPreferenceChanged"); } - 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 impedance is strong: there - // is no way to get a single untyped value from a - // SharedPreferences instance. - msg.put("value", sharedPreferences.getAll().get(key)); + final GeckoBundle msg = new GeckoBundle(5); + msg.putString("scope", scope.key); + msg.putString("branch", branch); + msg.putString("profileName", profileName); + msg.putString("key", key); + putSharedPreference(msg, sharedPreferences, key); - GeckoAppShell.notifyObservers("SharedPreferences:Changed", msg.toString()); - } catch (JSONException e) { - Log.e(LOGTAG, "Got exception creating JSON object", e); - return; - } + EventDispatcher.getInstance().dispatch("SharedPreferences:Changed", msg); } } /** * Register or unregister a SharedPreferences.OnSharedPreferenceChangeListener. * * message.branch must exist, and should be a String SharedPreferences * branch name, or null for the default branch. * message.enable should be a boolean: true to enable listening, false to * disable listening. */ - private void handleObserve(JSONObject message) throws JSONException { + private void handleObserve(final GeckoBundle message) { 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)); + final String profileName = message.getString("profileName", null); + final String branch = getBranch(scope, profileName, message.getString("branch", null)); if (branch == null) { Log.e(LOGTAG, "No branch specified for SharedPreference:Observe; aborting."); return; } // mListeners is only modified in this one observer, which is called // from Gecko serially. @@ -262,40 +276,34 @@ public final class SharedPreferencesHelp } if (!enable && this.mListeners.containsKey(branch)) { SharedPreferences.OnSharedPreferenceChangeListener listener = this.mListeners.remove(branch); prefs.unregisterOnSharedPreferenceChangeListener(listener); } } - @Override - public void handleMessage(String event, JSONObject message) { + @Override // BundleEventListener + public void handleMessage(final String event, final GeckoBundle message, + final EventCallback callback) { // Everything here is synchronous and serial, so we need not worry about // overwriting an in-progress response. - try { - if (event.equals("SharedPreferences:Set")) { - if (logVerbose) { - Log.v(LOGTAG, "Got SharedPreferences:Set message."); - } - handleSet(message); - } else if (event.equals("SharedPreferences:Get")) { - if (logVerbose) { - Log.v(LOGTAG, "Got SharedPreferences:Get message."); - } - JSONObject obj = new JSONObject(); - obj.put("values", handleGet(message)); - EventDispatcher.sendResponse(message, obj); - } else if (event.equals("SharedPreferences:Observe")) { - if (logVerbose) { - Log.v(LOGTAG, "Got SharedPreferences:Observe message."); - } - handleObserve(message); - } else { - Log.e(LOGTAG, "SharedPreferencesHelper got unexpected message " + event); - return; + if (event.equals("SharedPreferences:Set")) { + if (logVerbose) { + Log.v(LOGTAG, "Got SharedPreferences:Set message."); + } + handleSet(message); + } else if (event.equals("SharedPreferences:Get")) { + if (logVerbose) { + Log.v(LOGTAG, "Got SharedPreferences:Get message."); } - } catch (JSONException e) { - Log.e(LOGTAG, "Got exception in handleMessage handling event " + event, e); + callback.sendSuccess(handleGet(message)); + } else if (event.equals("SharedPreferences:Observe")) { + if (logVerbose) { + Log.v(LOGTAG, "Got SharedPreferences:Observe message."); + } + handleObserve(message); + } else { + Log.e(LOGTAG, "SharedPreferencesHelper got unexpected message " + event); return; } } }
--- a/mobile/android/modules/SharedPreferences.jsm +++ b/mobile/android/modules/SharedPreferences.jsm @@ -71,17 +71,17 @@ function SharedPreferencesImpl(options = this._scope = options.scope; this._profileName = options.profileName; this._branch = options.branch; this._observers = {}; } SharedPreferencesImpl.prototype = Object.freeze({ _set: function _set(prefs) { - Messaging.sendRequest({ + EventDispatcher.instance.sendRequest({ type: "SharedPreferences:Set", preferences: prefs, scope: this._scope, profileName: this._profileName, branch: this._branch, }); }, @@ -104,39 +104,38 @@ SharedPreferencesImpl.prototype = Object }, setIntPref: function setIntPref(prefName, value) { this._setOne(prefName, value, "int"); }, _get: function _get(prefs, callback) { let result = null; - Messaging.sendRequestForResult({ - type: "SharedPreferences:Get", + + // Use dispatch instead of sendRequestForResult because callbacks for + // Gecko thread events are synchronous when used with dispatch(), so we + // don't have to spin the event loop here to wait for a result. + EventDispatcher.instance.dispatch("SharedPreferences:Get", { preferences: prefs, scope: this._scope, profileName: this._profileName, branch: this._branch, - }).then((data) => { - result = data.values; + }, { + onSuccess: values => { result = values }, + onError: msg => { throw new Error("Cannot get preference: " + msg); }, }); - let thread = Services.tm.currentThread; - while (result == null) - thread.processNextEvent(true); - return result; }, _getOne: function _getOne(prefName, type) { - let prefs = []; - prefs.push({ + let prefs = [{ name: prefName, type: type, - }); + }]; let values = this._get(prefs); if (values.length != 1) { throw new Error("Got too many values: " + values.length); } return values[0].value; }, getBoolPref: function getBoolPref(prefName) { @@ -200,32 +199,32 @@ SharedPreferencesImpl.prototype = Object this._installAndroidListener(); }, _installAndroidListener: function _installAndroidListener() { if (this._listening) return; this._listening = true; - Services.obs.addObserver(this, "SharedPreferences:Changed", false); - Messaging.sendRequest({ + EventDispatcher.instance.registerListener(this, "SharedPreferences:Changed"); + + EventDispatcher.instance.sendRequest({ type: "SharedPreferences:Observe", enable: true, scope: this._scope, profileName: this._profileName, branch: this._branch, }); }, - observe: function observe(subject, topic, data) { - if (topic != "SharedPreferences:Changed") { + onEvent: function _onEvent(event, msg, callback) { + if (event !== "SharedPreferences:Changed") { return; } - 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))) { return; } if (!this._observers.hasOwnProperty(msg.key)) { return; @@ -237,18 +236,19 @@ SharedPreferencesImpl.prototype = Object } }, _uninstallAndroidListener: function _uninstallAndroidListener() { if (!this._listening) return; this._listening = false; - Services.obs.removeObserver(this, "SharedPreferences:Changed"); - Messaging.sendRequest({ + EventDispatcher.instance.unregisterListener(this, "SharedPreferences:Changed"); + + EventDispatcher.instance.sendRequest({ type: "SharedPreferences:Observe", enable: false, scope: this._scope, profileName: this._profileName, branch: this._branch, }); }, });