Bug 1388724 - 2. Add universal DoorHanger API; r=droeh
Add a DoorHanger API that doesn't rely on browser.js/NativeWindow, so we
can use doorhangers for custom tabs and PWA. The API falls back to the
old NativeWindow implementation when dealing with the brower.js window.
MozReview-Commit-ID: 6GksKbLaNAp
--- a/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
@@ -84,17 +84,17 @@ public class DoorHangerPopup extends Anc
updatePopup();
}
@Override
public void handleMessage(final String event, final GeckoBundle geckoObject,
final EventCallback callback) {
if (event.equals("Doorhanger:Add")) {
final DoorhangerConfig config = makeConfigFromBundle(geckoObject);
- addDoorHanger(config);
+ addDoorHanger(config, callback);
} else if (event.equals("Doorhanger:Remove")) {
final int tabId = geckoObject.getInt("tabID", -1);
final String value = geckoObject.getString("value");
DoorHanger doorHanger = getDoorHanger(tabId, value);
if (doorHanger == null) {
return;
@@ -161,17 +161,17 @@ public class DoorHangerPopup extends Anc
}
}
/**
* Adds a doorhanger.
*
* This method must be called on the UI thread.
*/
- private void addDoorHanger(final DoorhangerConfig config) {
+ private void addDoorHanger(final DoorhangerConfig config, final EventCallback callback) {
final int tabId = config.getTabId();
// Don't add a doorhanger for a tab that doesn't exist
if (isGeckoApp && Tabs.getInstance().getTab(tabId) == null) {
return;
}
// Replace the doorhanger if it already exists
DoorHanger oldDoorHanger = getDoorHanger(tabId, config.getId());
@@ -179,33 +179,38 @@ public class DoorHangerPopup extends Anc
removeDoorHanger(oldDoorHanger);
}
if (!mInflated) {
init();
}
final DoorHanger newDoorHanger = DoorHanger.Get(mContext, config);
+ newDoorHanger.callback = callback;
mDoorHangers.add(newDoorHanger);
mContent.addView(newDoorHanger);
// Only update the popup if we're adding a notification to the selected tab
if (!isGeckoApp || tabId == Tabs.getInstance().getSelectedTab().getId()) {
updatePopup();
}
}
/*
* DoorHanger.OnButtonClickListener implementation
*/
@Override
public void onButtonClick(final GeckoBundle response, DoorHanger doorhanger) {
- EventDispatcher.getInstance().dispatch("Doorhanger:Reply", response);
+ if (isGeckoApp) {
+ EventDispatcher.getInstance().dispatch("Doorhanger:Reply", response);
+ } else {
+ doorhanger.callback.sendSuccess(response);
+ }
removeDoorHanger(doorhanger);
updatePopup();
}
/**
* Gets a doorhanger.
*
* This method must be called on the UI thread.
--- a/mobile/android/base/java/org/mozilla/gecko/widget/DoorHanger.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/DoorHanger.java
@@ -19,16 +19,17 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import java.util.Locale;
public abstract class DoorHanger extends LinearLayout {
public static DoorHanger Get(Context context, DoorhangerConfig config) {
final Type type = config.getType();
@@ -83,16 +84,18 @@ public abstract class DoorHanger extends
protected final Resources mResources;
protected int mDividerColor;
protected boolean mPersistWhileVisible;
protected int mPersistenceCount;
protected long mTimeout;
+ public EventCallback callback;
+
protected DoorHanger(Context context, DoorhangerConfig config, Type type) {
super(context);
mContext = context;
mResources = context.getResources();
mTabId = config.getTabId();
mIdentifier = config.getId();
mType = type;
--- a/mobile/android/modules/Prompt.jsm
+++ b/mobile/android/modules/Prompt.jsm
@@ -5,17 +5,17 @@
"use strict"
var Cc = Components.classes;
var Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Messaging.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
-this.EXPORTED_SYMBOLS = ["Prompt"];
+this.EXPORTED_SYMBOLS = ["Prompt", "DoorHanger"];
function log(msg) {
Services.console.logStringMessage(msg);
}
function getRootWindow(win) {
// Get the root xul window.
return win.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -251,8 +251,63 @@ Prompt.prototype = {
},
setMultiChoiceItems: function(aItems) {
this.msg.choiceMode = "multiple";
return this._setListItems(aItems);
},
}
+
+var DoorHanger = {
+ _getTabId: function(aWindow, aBrowserApp) {
+ let tab = aBrowserApp.getTabForWindow(aWindow.top) ||
+ aBrowserApp.selectedTab;
+ return tab ? tab.id : -1;
+ },
+
+ show: function(aWindow, aMessage, aValue, aButtons, aOptions, aCategory) {
+ let chromeWin = getRootWindow(aWindow);
+ if (chromeWin.BrowserApp) {
+ // We're dealing with browser.js.
+ return chromeWin.NativeWindow.doorhanger.show(
+ aMessage, aValue, aButtons, this._getTabId(aWindow, chromeWin.BrowserApp),
+ aOptions, aCategory);
+ }
+
+ // We're dealing with GeckoView (e.g. custom tabs).
+ aButtons = aButtons || [];
+
+ // Extract callbacks into a separate array, and replace each callback in
+ // the buttons array with an index into the callback array.
+ let callbacks = aButtons.map((aButton, aIndex) => {
+ let cb = aButton.callback;
+ aButton.callback = aIndex;
+ return cb;
+ });
+
+ EventDispatcher.for(chromeWin).sendRequestForResult({
+ type: "Doorhanger:Add",
+ message: aMessage,
+ value: aValue,
+ buttons: aButtons,
+ options: aOptions || {},
+ category: aCategory,
+ }).then(response => {
+ // Pass the value of the optional checkbox to the callback
+ callbacks[response.callback](response.checked, response.inputs);
+ });
+ },
+
+ hide: function(aWindow, aValue) {
+ let chromeWin = getRootWindow(aWindow);
+ if (chromeWin.BrowserApp) {
+ // We're dealing with browser.js.
+ return chromeWin.NativeWindow.doorhanger.hide(
+ aValue, this._getTabId(aWindow, chromeWin.BrowserApp));
+ }
+
+ EventDispatcher.for(chromeWin).sendRequest({
+ type: "Doorhanger:Remove",
+ value: aValue,
+ });
+ },
+};