Bug 906088 - part 5 - send preference requests to JS through JNI and XPConnect rather than JSON; r=blassey,mfinkle
authorNathan Froyd <froydnj@mozilla.com>
Wed, 04 Sep 2013 09:58:23 -0400
changeset 159949 b90df20f637ff2c8ff264d629cfc6d048993892a
parent 159948 d8ccee9586b65fa20c2bf6f0e6791d8817e65b52
child 159950 13b45cdfd6edd30e7ed13b136603e8a8f14da219
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey, mfinkle
bugs906088
milestone26.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
Bug 906088 - part 5 - send preference requests to JS through JNI and XPConnect rather than JSON; r=blassey,mfinkle Small fix to notifyPrefObservers to call getPreferences with the new argument style. Carrying over r+.
mobile/android/base/GeckoEvent.java
mobile/android/base/PrefsHelper.java
mobile/android/chrome/content/browser.js
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsAppShell.cpp
widget/android/nsIAndroidBridge.idl
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -62,17 +62,20 @@ public class GeckoEvent {
         COMPOSITOR_PAUSE(29),
         COMPOSITOR_RESUME(30),
         NATIVE_GESTURE_EVENT(31),
         IME_KEY_EVENT(32),
         CALL_OBSERVER(33),
         REMOVE_OBSERVER(34),
         LOW_MEMORY(35),
         NETWORK_LINK_CHANGE(36),
-        TELEMETRY_HISTOGRAM_ADD(37);
+        TELEMETRY_HISTOGRAM_ADD(37),
+        PREFERENCES_OBSERVE(39),
+        PREFERENCES_GET(40),
+        PREFERENCES_REMOVE_OBSERVERS(41);
 
         public final int value;
 
         private NativeGeckoEvent(int value) {
             this.value = value;
          }
     }
 
@@ -181,16 +184,18 @@ public class GeckoEvent {
 
     private short mScreenOrientation;
 
     private ByteBuffer mBuffer;
 
     private int mWidth;
     private int mHeight;
 
+    private String[] mPrefNames;
+
     private GeckoEvent(NativeGeckoEvent event) {
         mType = event.value;
     }
 
     public static GeckoEvent createAppBackgroundingEvent() {
         return new GeckoEvent(NativeGeckoEvent.APP_BACKGROUNDING);
     }
 
@@ -684,16 +689,36 @@ public class GeckoEvent {
     }
 
     public static GeckoEvent createRemoveObserverEvent(String observerKey) {
         GeckoEvent event = new GeckoEvent(NativeGeckoEvent.REMOVE_OBSERVER);
         event.mCharacters = observerKey;
         return event;
     }
 
+    public static GeckoEvent createPreferencesObserveEvent(int requestId, String[] prefNames) {
+        GeckoEvent event = new GeckoEvent(NativeGeckoEvent.PREFERENCES_OBSERVE);
+        event.mCount = requestId;
+        event.mPrefNames = prefNames;
+        return event;
+    }
+
+    public static GeckoEvent createPreferencesGetEvent(int requestId, String[] prefNames) {
+        GeckoEvent event = new GeckoEvent(NativeGeckoEvent.PREFERENCES_GET);
+        event.mCount = requestId;
+        event.mPrefNames = prefNames;
+        return event;
+    }
+
+    public static GeckoEvent createPreferencesRemoveObserversEvent(int requestId) {
+        GeckoEvent event = new GeckoEvent(NativeGeckoEvent.PREFERENCES_REMOVE_OBSERVERS);
+        event.mCount = requestId;
+        return event;
+    }
+
     public static GeckoEvent createLowMemoryEvent(int level) {
         GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOW_MEMORY);
         event.mMetaState = level;
         return event;
     }
 
     public static GeckoEvent createNetworkLinkChangeEvent(String status) {
         GeckoEvent event = new GeckoEvent(NativeGeckoEvent.NETWORK_LINK_CHANGE);
--- a/mobile/android/base/PrefsHelper.java
+++ b/mobile/android/base/PrefsHelper.java
@@ -23,66 +23,43 @@ import java.util.Map;
 public final class PrefsHelper {
     private static final String LOGTAG = "GeckoPrefsHelper";
 
     private static boolean sRegistered = false;
     private static final Map<Integer, PrefHandler> sCallbacks = new HashMap<Integer, PrefHandler>();
     private static int sUniqueRequestId = 1;
 
     public static int getPref(String prefName, PrefHandler callback) {
-        JSONArray prefs = new JSONArray();
-        prefs.put(prefName);
-        return getPrefs(prefs, callback);
+        return getPrefsInternal(new String[] { prefName }, callback);
     }
 
     public static int getPrefs(String[] prefNames, PrefHandler callback) {
-        JSONArray prefs = new JSONArray();
-        for (String p : prefNames) {
-            prefs.put(p);
-        }
-        return getPrefs(prefs, callback);
+        return getPrefsInternal(prefNames, callback);
     }
 
     public static int getPrefs(ArrayList<String> prefNames, PrefHandler callback) {
-        JSONArray prefs = new JSONArray();
-        for (String p : prefNames) {
-            prefs.put(p);
-        }
-        return getPrefs(prefs, callback);
+        return getPrefsInternal(prefNames.toArray(new String[prefNames.size()]), callback);
     }
 
-    public static int getPrefs(JSONArray prefNames, PrefHandler callback) {
+    private static int getPrefsInternal(String[] prefNames, PrefHandler callback) {
         int requestId;
         synchronized (PrefsHelper.class) {
             ensureRegistered();
 
             requestId = sUniqueRequestId++;
             sCallbacks.put(requestId, callback);
         }
 
         GeckoEvent event;
-        try {
-            JSONObject message = new JSONObject();
-            message.put("requestId", Integer.toString(requestId));
-            message.put("preferences", prefNames);
-            event = GeckoEvent.createBroadcastEvent(callback.isObserver() ?
-                "Preferences:Observe" : "Preferences:Get", message.toString());
-            GeckoAppShell.sendEventToGecko(event);
-        } catch (Exception e) {
-            Log.e(LOGTAG, "Error while composing Preferences:" +
-                  (callback.isObserver() ? "Observe" : "Get") + " message", e);
-
-            // if we failed to send the message, drop our reference to the callback because
-            // otherwise it will leak since we will never get the response
-            synchronized (PrefsHelper.class) {
-                sCallbacks.remove(requestId);
-            }
-
-            return -1;
+        if (callback.isObserver()) {
+            event = GeckoEvent.createPreferencesObserveEvent(requestId, prefNames);
+        } else {
+            event = GeckoEvent.createPreferencesGetEvent(requestId, prefNames);
         }
+        GeckoAppShell.sendEventToGecko(event);
 
         return requestId;
     }
 
     private static void ensureRegistered() {
         if (sRegistered) {
             return;
         }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -274,20 +274,17 @@ var BrowserApp = {
     Services.obs.addObserver(this, "Tab:Closed", false);
     Services.obs.addObserver(this, "Session:Back", false);
     Services.obs.addObserver(this, "Session:ShowHistory", false);
     Services.obs.addObserver(this, "Session:Forward", false);
     Services.obs.addObserver(this, "Session:Reload", false);
     Services.obs.addObserver(this, "Session:Stop", false);
     Services.obs.addObserver(this, "SaveAs:PDF", false);
     Services.obs.addObserver(this, "Browser:Quit", false);
-    Services.obs.addObserver(this, "Preferences:Get", false);
     Services.obs.addObserver(this, "Preferences:Set", false);
-    Services.obs.addObserver(this, "Preferences:Observe", false);
-    Services.obs.addObserver(this, "Preferences:RemoveObservers", false);
     Services.obs.addObserver(this, "ScrollTo:FocusedInput", false);
     Services.obs.addObserver(this, "Sanitize:ClearData", false);
     Services.obs.addObserver(this, "FullScreen:Exit", false);
     Services.obs.addObserver(this, "Viewport:Change", false);
     Services.obs.addObserver(this, "Viewport:Flush", false);
     Services.obs.addObserver(this, "Viewport:FixedMarginsChanged", false);
     Services.obs.addObserver(this, "Passwords:Init", false);
     Services.obs.addObserver(this, "FormHistory:Init", false);
@@ -988,28 +985,20 @@ var BrowserApp = {
                                   Services.io.newFileURI(file), "", mimeInfo,
                                   Date.now() * 1000, null, cancelable, isPrivate);
 
     webBrowserPrint.print(printSettings, download);
   },
 
   notifyPrefObservers: function(aPref) {
     this._prefObservers[aPref].forEach(function(aRequestId) {
-      let request = { requestId : aRequestId,
-                      preferences : [aPref] };
-      this.getPreferences(request);
+      this.getPreferences(aRequestId, [aPref], 1);
     }, this);
   },
 
-  getPreferences: function getPreferences(aPrefsRequest, aListen) {
-    this.handlePreferencesRequest(aPrefsRequest.requestId,
-                                  aPrefsRequest.preferences,
-                                  aListen);
-  },
-
   handlePreferencesRequest: function handlePreferencesRequest(aRequestId,
                                                               aPrefNames,
                                                               aListen) {
 
     let prefs = [];
 
     for (let prefName of aPrefNames) {
       let pref = {
@@ -1129,36 +1118,16 @@ var BrowserApp = {
 
     sendMessageToJava({
       type: "Preferences:Data",
       requestId: aRequestId,    // opaque request identifier, can be any string/int/whatever
       preferences: prefs
     });
   },
 
-  removePreferenceObservers: function removePreferenceObservers(aRequestId) {
-    let newPrefObservers = [];
-    for (let prefName in this._prefObservers) {
-      let requestIds = this._prefObservers[prefName];
-      // Remove the requestID from the preference handlers
-      let i = requestIds.indexOf(aRequestId);
-      if (i >= 0) {
-        requestIds.splice(i, 1);
-      }
-
-      // If there are no more request IDs, remove the observer
-      if (requestIds.length == 0) {
-        Services.prefs.removeObserver(prefName, this);
-      } else {
-        newPrefObservers[prefName] = requestIds;
-      }
-    }
-    this._prefObservers = newPrefObservers;
-  },
-
   setPreferences: function setPreferences(aPref) {
     let json = JSON.parse(aPref);
 
     switch (json.name) {
       // The plugin pref is actually two separate prefs, so
       // we need to handle it differently
       case "plugin.enable":
         PluginHelper.setPluginPreference(json.value);
@@ -1447,32 +1416,20 @@ var BrowserApp = {
       case "Browser:Quit":
         this.quit();
         break;
 
       case "SaveAs:PDF":
         this.saveAsPDF(browser);
         break;
 
-      case "Preferences:Get":
-        this.getPreferences(JSON.parse(aData));
-        break;
-
       case "Preferences:Set":
         this.setPreferences(aData);
         break;
 
-      case "Preferences:Observe":
-        this.getPreferences(JSON.parse(aData), true);
-        break;
-
-      case "Preferences:RemoveObservers":
-        this.removePreferenceObservers(aData);
-        break;
-
       case "ScrollTo:FocusedInput":
         // these messages come from a change in the viewable area and not user interaction
         // we allow scrolling to the selected input, but not zooming the page
         this.scrollToFocusedInput(browser, false);
         break;
 
       case "Sanitize:ClearData":
         this.sanitize(aData);
@@ -1536,16 +1493,44 @@ var BrowserApp = {
     return this.defaultBrowserWidth = width;
   },
 
   // nsIAndroidBrowserApp
   getBrowserTab: function(tabId) {
     return this.getTabForId(tabId);
   },
 
+  getPreferences: function getPreferences(requestId, prefNames, count) {
+    this.handlePreferencesRequest(requestId, prefNames, false);
+  },
+
+  observePreferences: function observePreferences(requestId, prefNames, count) {
+    this.handlePreferencesRequest(requestId, prefNames, true);
+  },
+
+  removePreferenceObservers: function removePreferenceObservers(aRequestId) {
+    let newPrefObservers = [];
+    for (let prefName in this._prefObservers) {
+      let requestIds = this._prefObservers[prefName];
+      // Remove the requestID from the preference handlers
+      let i = requestIds.indexOf(aRequestId);
+      if (i >= 0) {
+        requestIds.splice(i, 1);
+      }
+
+      // If there are no more request IDs, remove the observer
+      if (requestIds.length == 0) {
+        Services.prefs.removeObserver(prefName, this);
+      } else {
+        newPrefObservers[prefName] = requestIds;
+      }
+    }
+    this._prefObservers = newPrefObservers;
+  },
+
   // This method will print a list from fromIndex to toIndex, optionally
   // selecting selIndex(if fromIndex<=selIndex<=toIndex)
   showHistory: function(fromIndex, toIndex, selIndex) {
     let browser = this.selectedBrowser;
     let hist = browser.sessionHistory;
     let listitems = [];
     for (let i = toIndex; i >= fromIndex; i--) {
       let entry = hist.getEntryAtIndex(i, false);
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -667,16 +667,21 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
 
         case PREFERENCES_OBSERVE:
         case PREFERENCES_GET: {
             ReadStringArray(mPrefNames, jenv, jPrefNamesField);
             mCount = jenv->GetIntField(jobj, jCountField);
             break;
         }
 
+        case PREFERENCES_REMOVE_OBSERVERS: {
+            mCount = jenv->GetIntField(jobj, jCountField);
+            break;
+        }
+
         default:
             break;
     }
 
 #ifdef DEBUG_ANDROID_EVENTS
     ALOG("AndroidGeckoEvent: %p : %d", (void*)jobj, mType);
 #endif
 }
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -733,16 +733,17 @@ public:
         CALL_OBSERVER = 33,
         REMOVE_OBSERVER = 34,
         LOW_MEMORY = 35,
         NETWORK_LINK_CHANGE = 36,
         TELEMETRY_HISTOGRAM_ADD = 37,
         ADD_OBSERVER = 38,
         PREFERENCES_OBSERVE = 39,
         PREFERENCES_GET = 40,
+        PREFERENCES_REMOVE_OBSERVERS = 41,
         dummy_java_enum_list_end
     };
 
     enum {
         // Memory pressue levels, keep in sync with those in MemoryMonitor.java
         MEMORY_PRESSURE_NONE = 0,
         MEMORY_PRESSURE_CLEANUP = 1,
         MEMORY_PRESSURE_LOW = 2,
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -538,16 +538,37 @@ nsAppShell::ProcessNextNativeEvent(bool 
     case AndroidGeckoEvent::REMOVE_OBSERVER:
         mObserversHash.Remove(curEvent->Characters());
         break;
 
     case AndroidGeckoEvent::ADD_OBSERVER:
         AddObserver(curEvent->Characters(), curEvent->Observer());
         break;
 
+    case AndroidGeckoEvent::PREFERENCES_GET:
+    case AndroidGeckoEvent::PREFERENCES_OBSERVE: {
+        const nsTArray<nsString> &prefNames = curEvent->PrefNames();
+        size_t count = prefNames.Length();
+        nsAutoArrayPtr<const PRUnichar*> prefNamePtrs(new const PRUnichar*[count]);
+        for (size_t i = 0; i < count; ++i) {
+            prefNamePtrs[i] = prefNames[i].get();
+        }
+
+        if (curEvent->Type() == AndroidGeckoEvent::PREFERENCES_GET) {
+            mBrowserApp->GetPreferences(curEvent->RequestId(), prefNamePtrs, count);
+        } else {
+            mBrowserApp->ObservePreferences(curEvent->RequestId(), prefNamePtrs, count);
+        }
+        break;
+    }
+
+    case AndroidGeckoEvent::PREFERENCES_REMOVE_OBSERVERS:
+        mBrowserApp->RemovePreferenceObservers(curEvent->RequestId());
+        break;
+
     case AndroidGeckoEvent::LOW_MEMORY:
         // TODO hook in memory-reduction stuff for different levels here
         if (curEvent->MetaState() >= AndroidGeckoEvent::MEMORY_PRESSURE_MEDIUM) {
             nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
             if (os) {
                 os->NotifyObservers(nullptr,
                                     "memory-pressure",
                                     NS_LITERAL_STRING("low-memory").get());
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -6,19 +6,26 @@
 #include "nsIDOMWindow.idl"
 
 [scriptable, uuid(0843f3c1-043e-4c64-9d8c-091370548c05)]
 interface nsIBrowserTab : nsISupports {
   readonly attribute nsIDOMWindow window;
   readonly attribute float scale;
 };
 
-[scriptable, uuid(d10377b4-1c90-493a-a532-63cb3f16ee2b)]
+[scriptable, uuid(7508b826-4129-40a0-91da-2a6bba33681f)]
 interface nsIAndroidBrowserApp : nsISupports {
   nsIBrowserTab getBrowserTab(in int32_t tabId);
+  void getPreferences(in int32_t requestId,
+                      [array, size_is(count)] in wstring prefNames,
+                      in unsigned long count);
+  void observePreferences(in int32_t requestId,
+                          [array, size_is(count)] in wstring prefNames,
+                          in unsigned long count);
+  void removePreferenceObservers(in int32_t requestId);
 };
 [scriptable, uuid(59cfcb35-69b7-47b2-8155-32b193272666)]
 interface nsIAndroidViewport : nsISupports {
   readonly attribute float x;
   readonly attribute float y;
   readonly attribute float width;
   readonly attribute float height;
   readonly attribute float pageLeft;