Bug 880123 - Add support for content callback interfaces to GeckoView r=blassey
authorMark Finkle <mfinkle@mozilla.com>
Wed, 06 Nov 2013 17:59:07 -0500
changeset 169458 0039e0f220bf89881c9c606b0c5aa3ff749cee68
parent 169457 43c4eda97a57fe78f538fdd6d3a32615468579be
child 169459 513900da2dceeea0948cbcea0715178439bd3234
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs880123
milestone28.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 880123 - Add support for content callback interfaces to GeckoView r=blassey
mobile/android/base/GeckoView.java
mobile/android/base/GeckoViewContent.java
mobile/android/base/moz.build
--- a/mobile/android/base/GeckoView.java
+++ b/mobile/android/base/GeckoView.java
@@ -8,16 +8,17 @@ package org.mozilla.gecko;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
+import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -34,16 +35,17 @@ import java.util.Collections;
 import java.util.List;
 
 public class GeckoView extends LayerView
     implements GeckoEventListener, ContextGetter {
 
     private static final String LOGTAG = "GeckoView";
 
     private ChromeDelegate mChromeDelegate;
+    private ContentDelegate mContentDelegate;
 
     public GeckoView(Context context, AttributeSet attrs) {
         super(context, attrs);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GeckoView);
         String url = a.getString(R.styleable.GeckoView_url);
         boolean doInit = a.getBoolean(R.styleable.GeckoView_doinit, true);
         a.recycle();
 
@@ -77,17 +79,23 @@ public class GeckoView extends LayerView
             GeckoThread.setAction(Intent.ACTION_VIEW);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(url));
         }
         GeckoAppShell.setContextGetter(this);
         if (context instanceof Activity) {
             Tabs tabs = Tabs.getInstance();
             tabs.attachToContext(context);
         }
+
         GeckoAppShell.registerEventListener("Gecko:Ready", this);
+        GeckoAppShell.registerEventListener("Content:StateChange", this);
+        GeckoAppShell.registerEventListener("Content:LoadError", this);
+        GeckoAppShell.registerEventListener("Content:PageShow", this);
+        GeckoAppShell.registerEventListener("DOMTitleChanged", this);
+        GeckoAppShell.registerEventListener("Link:Favicon", this);
 
         ThreadUtils.setUiThread(Thread.currentThread(), new Handler());
         initializeView(GeckoAppShell.getEventDispatcher());
 
         GeckoProfile profile = GeckoProfile.get(context).forceCreate();
         BrowserDB.initialize(profile.getName());
 
         if (GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) {
@@ -153,47 +161,120 @@ public class GeckoView extends LayerView
             browsers.add(new Browser(tab.getId()));
         }
         return Collections.unmodifiableList(browsers);
     }
 
     /**
     * Not part of the public API. Ignore.
     */
-    public void handleMessage(String event, JSONObject message) {
-        if (event.equals("Gecko:Ready")) {
-            handleReady();
-        }
+    public void handleMessage(final String event, final JSONObject message) {
+        ThreadUtils.postToUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    if (event.equals("Gecko:Ready")) {
+                        GeckoView.this.handleReady(message);
+                    } else if (event.equals("Content:StateChange")) {
+                        GeckoView.this.handleStateChange(message);
+                    } else if (event.equals("Content:LoadError")) {
+                        GeckoView.this.handleLoadError(message);
+                    } else if (event.equals("Content:PageShow")) {
+                        GeckoView.this.handlePageShow(message);
+                    } else if (event.equals("DOMTitleChanged")) {
+                        GeckoView.this.handleTitleChanged(message);
+                    } else if (event.equals("Link:Favicon")) {
+                        GeckoView.this.handleLinkFavicon(message);
+                    }
+                } catch (Exception e) {
+                    Log.w(LOGTAG, "handleMessage threw for " + event, e);
+                }
+            }
+        });
     }
 
-    private void handleReady() {
+    private void handleReady(final JSONObject message) {
         GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoRunning);
         Tab selectedTab = Tabs.getInstance().getSelectedTab();
         if (selectedTab != null)
             Tabs.getInstance().notifyListeners(selectedTab, Tabs.TabEvents.SELECTED);
         geckoConnected();
         GeckoAppShell.setLayerClient(getLayerClient());
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Viewport:Flush", null));
         show();
         requestRender();
 
         if (mChromeDelegate != null) {
             mChromeDelegate.onReady(this);
         }
     }
 
+    private void handleStateChange(final JSONObject message) throws JSONException {
+        int state = message.getInt("state");
+        if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
+            if ((state & GeckoAppShell.WPL_STATE_START) != 0) {
+                if (mContentDelegate != null) {
+                    int id = message.getInt("tabID");
+                    mContentDelegate.onPageStart(this, new Browser(id), message.getString("uri"));
+                }
+            } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) {
+                if (mContentDelegate != null) {
+                    int id = message.getInt("tabID");
+                    mContentDelegate.onPageStop(this, new Browser(id), message.getBoolean("success"));
+                }
+            }
+        }
+    }
+
+    private void handleLoadError(final JSONObject message) throws JSONException {
+        if (mContentDelegate != null) {
+            int id = message.getInt("tabID");
+            mContentDelegate.onPageStop(GeckoView.this, new Browser(id), false);
+        }
+    }
+
+    private void handlePageShow(final JSONObject message) throws JSONException {
+        if (mContentDelegate != null) {
+            int id = message.getInt("tabID");
+            mContentDelegate.onPageShow(GeckoView.this, new Browser(id));
+        }
+    }
+
+    private void handleTitleChanged(final JSONObject message) throws JSONException {
+        if (mContentDelegate != null) {
+            int id = message.getInt("tabID");
+            mContentDelegate.onReceivedTitle(GeckoView.this, new Browser(id), message.getString("title"));
+        }
+    }
+
+    private void handleLinkFavicon(final JSONObject message) throws JSONException {
+        if (mContentDelegate != null) {
+            int id = message.getInt("tabID");
+            mContentDelegate.onReceivedFavicon(GeckoView.this, new Browser(id), message.getString("href"), message.getInt("size"));
+        }
+    }
+
     /**
     * Set the chrome callback handler.
     * This will replace the current handler.
     * @param chrome An implementation of GeckoViewChrome.
     */
     public void setChromeDelegate(ChromeDelegate chrome) {
         mChromeDelegate = chrome;
     }
 
+    /**
+    * Set the content callback handler.
+    * This will replace the current handler.
+    * @param content An implementation of ContentDelegate.
+    */
+    public void setContentDelegate(ContentDelegate content) {
+        mContentDelegate = content;
+    }
+
     public static void setGeckoInterface(final BaseGeckoInterface geckoInterface) {
         GeckoAppShell.setGeckoInterface(geckoInterface);
     }
 
     public static GeckoAppShell.GeckoInterface getGeckoInterface() {
         return GeckoAppShell.getGeckoInterface();
     }
 
@@ -305,9 +386,55 @@ public class GeckoView extends LayerView
 
     public interface ChromeDelegate {
         /**
         * Tell the host application that Gecko is ready to handle requests.
         * @param view The GeckoView that initiated the callback.
         */
         public void onReady(GeckoView view);
     }
+
+    public interface ContentDelegate {
+        /**
+        * A Browser has started loading content from the network.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that is loading the content.
+        * @param url The resource being loaded.
+        */
+        public void onPageStart(GeckoView view, GeckoView.Browser browser, String url);
+    
+        /**
+        * A Browser has finished loading content from the network.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that was loading the content.
+        * @param success Whether the page loaded successfully or an error occured.
+        */
+        public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success);
+    
+        /**
+        * A Browser is displaying content. This page could have been loaded via
+        * network or from the session history.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that is showing the content.
+        */
+        public void onPageShow(GeckoView view, GeckoView.Browser browser);
+    
+        /**
+        * A page title was discovered in the content or updated after the content
+        * loaded.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that is showing the content.
+        * @param title The title sent from the content.
+        */
+        public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title);
+    
+        /**
+        * A link element was discovered in the content or updated after the content
+        * loaded that specifies a favicon.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that is showing the content.
+        * @param url The href of the link element specifying the favicon.
+        * @param size The maximum size specified for the favicon, or -1 for any size.
+        */
+        public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size);
+    }
+
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/GeckoViewContent.java
@@ -0,0 +1,51 @@
+/* -*- 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;
+
+public class GeckoViewContent implements GeckoView.ContentDelegate {
+    /**
+    * A Browser has started loading content from the network.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that is loading the content.
+    * @param url The resource being loaded.
+    */
+    public void onPageStart(GeckoView view, GeckoView.Browser browser, String url) {}
+
+    /**
+    * A Browser has finished loading content from the network.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that was loading the content.
+    * @param success Whether the page loaded successfully or an error occured.
+    */
+    public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success) {}
+
+    /**
+    * A Browser is displaying content. This page could have been loaded via
+    * network or from the session history.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that is showing the content.
+    */
+    public void onPageShow(GeckoView view, GeckoView.Browser browser) {}
+
+    /**
+    * A page title was discovered in the content or updated after the content
+    * loaded.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that is showing the content.
+    * @param title The title sent from the content.
+    */
+    public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title) {}
+
+    /**
+    * A link element was discovered in the content or updated after the content
+    * loaded that specifies a favicon.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that is showing the content.
+    * @param url The href of the link element specifying the favicon.
+    * @param size The maximum size specified for the favicon, or -1 for any size.
+    */
+    public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size) {}
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -144,16 +144,17 @@ gbjar.sources += [
     'GeckoPreferenceFragment.java',
     'GeckoProfile.java',
     'GeckoSmsManager.java',
     'GeckoThread.java',
     'GeckoJavaSampler.java',
     'GlobalHistory.java',
     'GeckoView.java',
     'GeckoViewChrome.java',
+    'GeckoViewContent.java',
     'health/BrowserHealthRecorder.java',
     'health/BrowserHealthReporter.java',
     'InputMethods.java',
     'JavaAddonManager.java',
     'LightweightTheme.java',
     'LightweightThemeDrawable.java',
     'LinkPreference.java',
     'MemoryMonitor.java',