Bug 928756 - [geckoview] Convert prompt-based actions into separate GeckoView endpoints r=bnicholson
authorMark Finkle <mfinkle@mozilla.com>
Wed, 06 Nov 2013 17:59:11 -0500
changeset 169459 513900da2dceeea0948cbcea0715178439bd3234
parent 169458 0039e0f220bf89881c9c606b0c5aa3ff749cee68
child 169460 2c3cd0a21ca08466cb4f14cb7758702e79d2eb09
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)
reviewersbnicholson
bugs928756
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 928756 - [geckoview] Convert prompt-based actions into separate GeckoView endpoints r=bnicholson
mobile/android/base/GeckoView.java
mobile/android/base/GeckoViewChrome.java
mobile/android/chrome/content/browser.js
mobile/android/components/PromptService.js
mobile/android/modules/Prompt.jsm
--- a/mobile/android/base/GeckoView.java
+++ b/mobile/android/base/GeckoView.java
@@ -86,16 +86,18 @@ public class GeckoView extends LayerView
         }
 
         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);
+        GeckoAppShell.registerEventListener("Prompt:Show", this);
+        GeckoAppShell.registerEventListener("Prompt:ShowTop", 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)) {
@@ -178,16 +180,18 @@ public class GeckoView extends LayerView
                     } 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);
+                    } else if (event.equals("Prompt:Show") || event.equals("Prompt:ShowTop")) {
+                        GeckoView.this.handlePrompt(message);
                     }
                 } catch (Exception e) {
                     Log.w(LOGTAG, "handleMessage threw for " + event, e);
                 }
             }
         });
     }
 
@@ -247,16 +251,35 @@ public class GeckoView extends LayerView
 
     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"));
         }
     }
 
+    private void handlePrompt(final JSONObject message) throws JSONException {
+        if (mChromeDelegate != null) {
+            String hint = message.optString("hint");
+            if ("alert".equals(hint)) {
+                String text = message.optString("text");
+                mChromeDelegate.onAlert(GeckoView.this, null, text, new PromptResult(message.optString("guid")));
+            } else if ("confirm".equals(hint)) {
+                String text = message.optString("text");
+                mChromeDelegate.onConfirm(GeckoView.this, null, text, new PromptResult(message.optString("guid")));
+            } else if ("prompt".equals(hint)) {
+                String text = message.optString("text");
+                String defaultValue = message.optString("textbox0");
+                mChromeDelegate.onPrompt(GeckoView.this, null, text, defaultValue, new PromptResult(message.optString("guid")));
+            } else if ("remotedebug".equals(hint)) {
+                mChromeDelegate.onDebugRequest(GeckoView.this, new PromptResult(message.optString("guid")));
+            }
+        }
+    }
+
     /**
     * Set the chrome callback handler.
     * This will replace the current handler.
     * @param chrome An implementation of GeckoViewChrome.
     */
     public void setChromeDelegate(ChromeDelegate chrome) {
         mChromeDelegate = chrome;
     }
@@ -379,22 +402,113 @@ public class GeckoView extends LayerView
         public void goForward() {
             Tab tab = Tabs.getInstance().getTab(mId);
             if (tab != null) {
                 tab.doForward();
             }
         }
     }
 
+    /* Provides a means for the client to indicate whether a JavaScript
+     * dialog request should proceed. An instance of this class is passed to
+     * various GeckoViewChrome callback actions.
+     */
+    public class PromptResult {
+        private final int RESULT_OK = 0;
+        private final int RESULT_CANCEL = 1;
+
+        private final String mGUID;
+
+        public PromptResult(String guid) {
+            mGUID = guid;
+        }
+
+        private JSONObject makeResult(int resultCode) {
+            JSONObject result = new JSONObject();
+            try {
+                result.put("guid", mGUID);
+                result.put("button", resultCode);
+            } catch(JSONException ex) { }
+            return result;
+        }
+
+        /**
+        * Handle a confirmation response from the user.
+        */
+        public void confirm() {
+            JSONObject result = makeResult(RESULT_OK);
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString()));
+        }
+
+        /**
+        * Handle a confirmation response from the user.
+        * @param value String value to return to the browser context.
+        */
+        public void confirmWithValue(String value) {
+            JSONObject result = makeResult(RESULT_OK);
+            try {
+                result.put("textbox0", value);
+            } catch(JSONException ex) { }
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString()));
+        }
+
+        /**
+        * Handle a cancellation response from the user.
+        */
+        public void cancel() {
+            JSONObject result = makeResult(RESULT_CANCEL);
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString()));
+        }
+    }
+
     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);
+
+        /**
+        * Tell the host application to display an alert dialog.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that is loading the content.
+        * @param message The string to display in the dialog.
+        * @param result A PromptResult used to send back the result without blocking.
+        * Defaults to cancel requests.
+        */
+        public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result);
+    
+        /**
+        * Tell the host application to display a confirmation dialog.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that is loading the content.
+        * @param message The string to display in the dialog.
+        * @param result A PromptResult used to send back the result without blocking.
+        * Defaults to cancel requests.
+        */
+        public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result);
+    
+        /**
+        * Tell the host application to display an input prompt dialog.
+        * @param view The GeckoView that initiated the callback.
+        * @param browser The Browser that is loading the content.
+        * @param message The string to display in the dialog.
+        * @param defaultValue The string to use as default input.
+        * @param result A PromptResult used to send back the result without blocking.
+        * Defaults to cancel requests.
+        */
+        public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result);
+    
+        /**
+        * Tell the host application to display a remote debugging request dialog.
+        * @param view The GeckoView that initiated the callback.
+        * @param result A PromptResult used to send back the result without blocking.
+        * Defaults to cancel requests.
+        */
+        public void onDebugRequest(GeckoView view, GeckoView.PromptResult result);
     }
 
     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.
--- a/mobile/android/base/GeckoViewChrome.java
+++ b/mobile/android/base/GeckoViewChrome.java
@@ -6,9 +6,56 @@
 package org.mozilla.gecko;
 
 public class GeckoViewChrome implements GeckoView.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) {}
+
+    /**
+    * Tell the host application to display an alert dialog.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that is loading the content.
+    * @param message The string to display in the dialog.
+    * @param result A PromptResult used to send back the result without blocking.
+    * Defaults to cancel requests.
+    */
+    public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result) {
+        result.cancel();
+    }
+
+    /**
+    * Tell the host application to display a confirmation dialog.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that is loading the content.
+    * @param message The string to display in the dialog.
+    * @param result A PromptResult used to send back the result without blocking.
+    * Defaults to cancel requests.
+    */
+    public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result) {
+        result.cancel();
+    }
+
+    /**
+    * Tell the host application to display an input prompt dialog.
+    * @param view The GeckoView that initiated the callback.
+    * @param browser The Browser that is loading the content.
+    * @param message The string to display in the dialog.
+    * @param defaultValue The string to use as default input.
+    * @param result A PromptResult used to send back the result without blocking.
+    * Defaults to cancel requests.
+    */
+    public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result) {
+        result.cancel();
+    }
+
+    /**
+    * Tell the host application to display a remote debugging request dialog.
+    * @param view The GeckoView that initiated the callback.
+    * @param result A PromptResult used to send back the result without blocking.
+    * Defaults to cancel requests.
+    */
+    public void onDebugRequest(GeckoView view, GeckoView.PromptResult result) {
+        result.cancel();
+    }
 }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -7120,16 +7120,17 @@ var RemoteDebugger = {
     let msg = Strings.browser.GetStringFromName("remoteIncomingPromptMessage");
     let disable = Strings.browser.GetStringFromName("remoteIncomingPromptDisable");
     let cancel = Strings.browser.GetStringFromName("remoteIncomingPromptCancel");
     let agree = Strings.browser.GetStringFromName("remoteIncomingPromptAccept");
 
     // Make prompt. Note: button order is in reverse.
     let prompt = new Prompt({
       window: null,
+      hint: "remotedebug",
       title: title,
       message: msg,
       buttons: [ agree, cancel, disable ],
       priority: 1
     });
 
     // The debugger server expects a synchronous response, so spin on result since Prompt is async.
     let result = null;
--- a/mobile/android/components/PromptService.js
+++ b/mobile/android/components/PromptService.js
@@ -213,28 +213,30 @@ InternalPrompt.prototype = {
     else
       return this.nsIAuthPrompt_promptPassword.apply(this, arguments);
   },
 
   /* ----------  nsIPrompt  ---------- */
 
   alert: function alert(aTitle, aText) {
     let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ]);
+    p.setHint("alert");
     this.showPrompt(p);
   },
 
   alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) {
     let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ], aCheckMsg, aCheckState);
     let data = this.showPrompt(p);
     if (aCheckState && data.button > -1)
       aCheckState.value = data.checkbox0 == "true";
   },
 
   confirm: function confirm(aTitle, aText) {
     let p = this._getPrompt(aTitle, aText);
+    p.setHint("confirm");
     let data = this.showPrompt(p);
     return (data.button == 0);
   },
 
   confirmCheck: function confirmCheck(aTitle, aText, aCheckMsg, aCheckState) {
     let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState);
     let data = this.showPrompt(p);
     let ok = data.button == 0;
@@ -286,16 +288,17 @@ InternalPrompt.prototype = {
     let data = this.showPrompt(p);
     if (aCheckState && data.button > -1)
       aCheckState.value = data.checkbox0 == "true";
     return data.button;
   },
 
   nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) {
     let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState);
+    p.setHint("prompt");
     p.addTextbox({
       value: aValue.value,
       autofocus: true
     });
     let data = this.showPrompt(p);
 
     let ok = data.button == 0;
     if (aCheckState && data.button > -1)
--- a/mobile/android/modules/Prompt.jsm
+++ b/mobile/android/modules/Prompt.jsm
@@ -27,22 +27,33 @@ function Prompt(aOptions) {
     this.msg.title = aOptions.title;
 
   if ("message" in aOptions && aOptions.message != null)
     this.msg.text = aOptions.message;
 
   if ("buttons" in aOptions && aOptions.buttons != null)
     this.msg.buttons = aOptions.buttons;
 
+  if ("hint" in aOptions && aOptions.hint != null)
+    this.msg.hint = aOptions.hint;
+
   let idService = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); 
   this.guid = idService.generateUUID().toString();
   this.msg.guid = this.guid;
 }
 
 Prompt.prototype = {
+  setHint: function(aHint) {
+    if (!aHint)
+      delete this.msg.hint;
+    else
+      this.msg.hint = aHint;
+    return this;
+  },
+
   addButton: function(aOptions) {
     if (!this.msg.buttons)
       this.msg.buttons = [];
     this.msg.buttons.push(aOptions.label);
     return this;
   },
 
   _addInput: function(aOptions) {