Bug 1325021 - Reduce reliance on GeckoAppShell.getGeckoInterface to solve some custom tabs crashes. r=jchen
authorDylan Roeh <droeh@mozilla.com>
Mon, 08 May 2017 08:48:16 -0500
changeset 357104 d85d25fa905b78dc22436245f99b97667ec466c0
parent 357103 e9adf0e056f76898658532ead3ef84965e8d5de3
child 357105 98b0328c790987a40e6b86bdec51873994ecc4c2
push id31782
push userkwierso@gmail.com
push dateMon, 08 May 2017 23:07:35 +0000
treeherdermozilla-central@b21b974d60d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen
bugs1325021
milestone55.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 1325021 - Reduce reliance on GeckoAppShell.getGeckoInterface to solve some custom tabs crashes. r=jchen
mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
mobile/android/base/java/org/mozilla/gecko/FindInPageBar.java
mobile/android/base/java/org/mozilla/gecko/FormAssistPopup.java
mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
--- a/mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
+++ b/mobile/android/base/java/org/mozilla/gecko/ActionBarTextSelection.java
@@ -26,17 +26,17 @@ import java.util.Timer;
 import java.util.TimerTask;
 
 import android.util.Log;
 
 class ActionBarTextSelection implements TextSelection, BundleEventListener {
     private static final String LOGTAG = "GeckoTextSelection";
     private static final int SHUTDOWN_DELAY_MS = 250;
 
-    private final Context context;
+    private final GeckoApp geckoApp;
     private final ActionModePresenter presenter;
 
     private int selectionID; // Unique ID provided for each selection action.
 
     private GeckoBundle[] mCurrentItems;
 
     private TextSelectionActionModeCallback mCallback;
 
@@ -51,47 +51,47 @@ class ActionBarTextSelection implements 
                 public void run() {
                     endActionMode();
                 }
             });
         }
     };
     private ActionModeTimerTask mActionModeTimerTask;
 
-    ActionBarTextSelection(@NonNull final Context context,
+    ActionBarTextSelection(@NonNull final GeckoApp geckoApp,
                            @Nullable final ActionModePresenter presenter) {
-        this.context = context;
+        this.geckoApp = geckoApp;
         this.presenter = presenter;
     }
 
     @Override
     public void create() {
         // Only register listeners if we have valid start/middle/end handles
-        if (context == null) {
+        if (geckoApp == null) {
             Log.e(LOGTAG, "Failed to initialize text selection because at least one context is null");
         } else {
-            GeckoApp.getEventDispatcher().registerUiThreadListener(this,
+            geckoApp.getAppEventDispatcher().registerUiThreadListener(this,
                     "TextSelection:ActionbarInit",
                     "TextSelection:ActionbarStatus",
                     "TextSelection:ActionbarUninit");
         }
     }
 
     @Override
     public boolean dismiss() {
         // We do not call endActionMode() here because this is already handled by the activity.
         return false;
     }
 
     @Override
     public void destroy() {
-        if (context == null) {
+        if (geckoApp == null) {
             Log.e(LOGTAG, "Do not unregister TextSelection:* listeners since context is null");
         } else {
-            GeckoApp.getEventDispatcher().unregisterUiThreadListener(this,
+            geckoApp.getAppEventDispatcher().unregisterUiThreadListener(this,
                     "TextSelection:ActionbarInit",
                     "TextSelection:ActionbarStatus",
                     "TextSelection:ActionbarUninit");
         }
     }
 
     @Override
     public void handleMessage(final String event, final GeckoBundle message,
@@ -179,17 +179,17 @@ class ActionBarTextSelection implements 
                 final GeckoBundle obj = mItems[i];
                 final MenuItem menuitem = menu.add(0, i, 0, obj.getString("label", ""));
                 final int actionEnum = obj.getBoolean("showAsAction")
                         ? MenuItem.SHOW_AS_ACTION_ALWAYS
                         : MenuItem.SHOW_AS_ACTION_NEVER;
                 menuitem.setShowAsAction(actionEnum);
 
                 final String iconString = obj.getString("icon", "");
-                ResourceDrawableUtils.getDrawable(context, iconString,
+                ResourceDrawableUtils.getDrawable(geckoApp, iconString,
                         new ResourceDrawableUtils.BitmapLoader() {
                     @Override
                     public void onBitmapFound(Drawable d) {
                         if (d != null) {
                             menuitem.setIcon(d);
                         }
                     }
                 });
@@ -203,24 +203,24 @@ class ActionBarTextSelection implements 
             return true;
         }
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
             final GeckoBundle obj = mItems[item.getItemId()];
             final GeckoBundle data = new GeckoBundle(1);
             data.putString("id", obj.getString("id", ""));
-            GeckoApp.getEventDispatcher().dispatch("TextSelection:Action", data);
+            geckoApp.getAppEventDispatcher().dispatch("TextSelection:Action", data);
             return true;
         }
 
         // Called when the user exits the action mode
         @Override
         public void onDestroyActionMode(ActionMode mode) {
             mActionMode = null;
             mCallback = null;
 
             final GeckoBundle data = new GeckoBundle(1);
             data.putInt("selectionID", selectionID);
-            GeckoApp.getEventDispatcher().dispatch("TextSelection:End", data);
+            geckoApp.getAppEventDispatcher().dispatch("TextSelection:End", data);
         }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
@@ -31,31 +31,35 @@ public class DoorHangerPopup extends Anc
 
     // Stores a set of all active DoorHanger notifications. A DoorHanger is
     // uniquely identified by its tabId and value.
     private final HashSet<DoorHanger> mDoorHangers;
 
     // Whether or not the doorhanger popup is disabled.
     private boolean mDisabled;
 
-    public DoorHangerPopup(Context context) {
-        super(context);
+    private final GeckoApp geckoApp;
+
+    public DoorHangerPopup(GeckoApp geckoApp) {
+        super(geckoApp);
+
+        this.geckoApp = geckoApp;
 
         mDoorHangers = new HashSet<DoorHanger>();
 
-        GeckoApp.getEventDispatcher().registerUiThreadListener(this,
+        geckoApp.getAppEventDispatcher().registerUiThreadListener(this,
             "Doorhanger:Add",
             "Doorhanger:Remove");
         Tabs.registerOnTabsChangedListener(this);
 
         setOnDismissListener(this);
     }
 
     void destroy() {
-        GeckoApp.getEventDispatcher().unregisterUiThreadListener(this,
+        geckoApp.getAppEventDispatcher().unregisterUiThreadListener(this,
             "Doorhanger:Add",
             "Doorhanger:Remove");
         Tabs.unregisterOnTabsChangedListener(this);
     }
 
     /**
      * Temporarily disables the doorhanger popup. If the popup is disabled,
      * it will not be shown to the user, but it will continue to process
--- a/mobile/android/base/java/org/mozilla/gecko/FindInPageBar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/FindInPageBar.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -26,21 +27,29 @@ public class FindInPageBar extends Linea
         implements TextWatcher, View.OnClickListener, BundleEventListener {
     private static final String LOGTAG = "GeckoFindInPageBar";
     private static final String REQUEST_ID = "FindInPageBar";
 
     private final Context mContext;
     /* package */ CustomEditText mFindText;
     private TextView mStatusText;
     private boolean mInflated;
+    private GeckoApp geckoApp;
 
     public FindInPageBar(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
         setFocusable(true);
+
+        while (context instanceof ContextWrapper) {
+            if (context instanceof GeckoApp) {
+                geckoApp = (GeckoApp) context;
+            }
+            context = ((ContextWrapper) context).getBaseContext();
+        }
     }
 
     public void inflateContent() {
         LayoutInflater inflater = LayoutInflater.from(mContext);
         View content = inflater.inflate(R.layout.find_in_page_content, this);
 
         content.findViewById(R.id.find_prev).setOnClickListener(this);
         content.findViewById(R.id.find_next).setOnClickListener(this);
@@ -74,17 +83,17 @@ public class FindInPageBar extends Linea
         if (!mInflated)
             inflateContent();
 
         setVisibility(VISIBLE);
         mFindText.requestFocus();
 
         // handleMessage() receives response message and determines initial state of softInput
 
-        GeckoApp.getEventDispatcher().dispatch("TextSelection:Get", null, new EventCallback() {
+        geckoApp.getAppEventDispatcher().dispatch("TextSelection:Get", null, new EventCallback() {
             @Override
             public void sendSuccess(final Object result) {
                 onTextSelectionData((String) result);
             }
 
             @Override
             public void sendError(final Object error) {
                 Log.e(LOGTAG, "TextSelection:Get failed: " + error);
--- a/mobile/android/base/java/org/mozilla/gecko/FormAssistPopup.java
+++ b/mobile/android/base/java/org/mozilla/gecko/FormAssistPopup.java
@@ -11,16 +11,17 @@ import org.mozilla.gecko.gfx.ImmutableVi
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.widget.SwipeDismissListViewTouchListener;
 import org.mozilla.gecko.widget.SwipeDismissListViewTouchListener.OnDismissCallback;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -43,16 +44,17 @@ public class FormAssistPopup extends Rel
     private final Context mContext;
     private final Animation mAnimation;
 
     private ListView mAutoCompleteList;
     private RelativeLayout mValidationMessage;
     private TextView mValidationMessageText;
     private ImageView mValidationMessageArrow;
     private ImageView mValidationMessageArrowInverted;
+    private GeckoApp geckoApp;
 
     private double mX;
     private double mY;
     private double mW;
     private double mH;
 
     private enum PopupType {
         AUTOCOMPLETE,
@@ -83,34 +85,41 @@ public class FormAssistPopup extends Rel
     public FormAssistPopup(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
 
         mAnimation = AnimationUtils.loadAnimation(context, R.anim.grow_fade_in);
         mAnimation.setDuration(75);
 
         setFocusable(false);
+
+        while (context instanceof ContextWrapper) {
+            if (context instanceof GeckoApp) {
+                geckoApp = (GeckoApp) context;
+            }
+            context = ((ContextWrapper) context).getBaseContext();
+        }
     }
 
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        GeckoApp.getEventDispatcher().registerUiThreadListener(this,
+        geckoApp.getAppEventDispatcher().registerUiThreadListener(this,
             "FormAssist:AutoCompleteResult",
             "FormAssist:ValidationMessage",
             "FormAssist:Hide");
     }
 
     void destroy() {
     }
 
     @Override
     public void onDetachedFromWindow() {
-        GeckoApp.getEventDispatcher().unregisterUiThreadListener(this,
+        geckoApp.getAppEventDispatcher().unregisterUiThreadListener(this,
             "FormAssist:AutoCompleteResult",
             "FormAssist:ValidationMessage",
             "FormAssist:Hide");
 
         super.onDetachedFromWindow();
     }
 
     @Override // BundleEventListener
@@ -148,17 +157,17 @@ public class FormAssistPopup extends Rel
             mAutoCompleteList.setOnItemClickListener(new OnItemClickListener() {
                 @Override
                 public void onItemClick(AdapterView<?> parentView, View view, int position, long id) {
                     // Use the value stored with the autocomplete view, not the label text,
                     // since they can be different.
                     final TextView textView = (TextView) view;
                     final GeckoBundle message = new GeckoBundle(1);
                     message.putString("value", (String) textView.getTag());
-                    GeckoApp.getEventDispatcher().dispatch("FormAssist:AutoComplete", message);
+                    geckoApp.getAppEventDispatcher().dispatch("FormAssist:AutoComplete", message);
                     hide();
                 }
             });
 
             // Create a ListView-specific touch listener. ListViews are given special treatment because
             // by default they handle touches for their list items... i.e. they're in charge of drawing
             // the pressed state (the list selector), handling list item clicks, etc.
             final SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(mAutoCompleteList, new OnDismissCallback() {
@@ -167,17 +176,17 @@ public class FormAssistPopup extends Rel
                     // Use the value stored with the autocomplete view, not the label text,
                     // since they can be different.
                     AutoCompleteListAdapter adapter = (AutoCompleteListAdapter) listView.getAdapter();
                     Pair<String, String> item = adapter.getItem(position);
 
                     // Remove the item from form history.
                     final GeckoBundle message = new GeckoBundle(1);
                     message.putString("value", item.second);
-                    GeckoApp.getEventDispatcher().dispatch("FormAssist:Remove", message);
+                    geckoApp.getAppEventDispatcher().dispatch("FormAssist:Remove", message);
 
                     // Update the list
                     adapter.remove(item);
                     adapter.notifyDataSetChanged();
                     positionAndShowPopup();
                 }
             });
             mAutoCompleteList.setOnTouchListener(touchListener);
@@ -357,17 +366,17 @@ public class FormAssistPopup extends Rel
             setVisibility(VISIBLE);
             startAnimation(mAnimation);
         }
     }
 
     public void hide() {
         if (isShown()) {
             setVisibility(GONE);
-            GeckoApp.getEventDispatcher().dispatch("FormAssist:Hidden", null);
+            geckoApp.getAppEventDispatcher().dispatch("FormAssist:Hidden", null);
         }
     }
 
     void onTranslationChanged() {
         ThreadUtils.assertOnUiThread();
         if (!isShown()) {
             return;
         }
--- a/mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
+++ b/mobile/android/base/java/org/mozilla/gecko/text/FloatingActionModeCallback.java
@@ -49,17 +49,17 @@ public class FloatingActionModeCallback 
     }
 
     @Override
     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
         final TextAction action = actions.get(item.getItemId());
 
         final GeckoBundle data = new GeckoBundle(1);
         data.putString("id", action.getId());
-        GeckoApp.getEventDispatcher().dispatch("TextSelection:Action", data);
+        textSelection.geckoApp.getAppEventDispatcher().dispatch("TextSelection:Action", data);
 
         return true;
     }
 
     @Override
     public void onDestroyActionMode(ActionMode mode) {}
 
     @Override
--- a/mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
+++ b/mobile/android/base/java/org/mozilla/gecko/text/FloatingToolbarTextSelection.java
@@ -31,33 +31,33 @@ import java.util.List;
 @TargetApi(Build.VERSION_CODES.M)
 public class FloatingToolbarTextSelection implements TextSelection, BundleEventListener {
     private static final String LOGTAG = "GeckoFloatTextSelection";
 
     // This is an additional offset we add to the height of the selection. This will avoid that the
     // floating toolbar overlays the bottom handle(s).
     private static final int HANDLES_OFFSET_DP = 20;
 
-    private final Activity activity;
+    /* package */ final GeckoApp geckoApp;
     private final LayerView layerView;
     private final int[] locationInWindow;
     private final float handlesOffset;
 
     private ActionMode actionMode;
     private FloatingActionModeCallback actionModeCallback;
     private int selectionID;
     /* package-private */ Rect contentRect;
 
-    public FloatingToolbarTextSelection(Activity activity, LayerView layerView) {
-        this.activity = activity;
+    public FloatingToolbarTextSelection(GeckoApp geckoApp, LayerView layerView) {
+        this.geckoApp = geckoApp;
         this.layerView = layerView;
         this.locationInWindow = new int[2];
 
         this.handlesOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                HANDLES_OFFSET_DP, activity.getResources().getDisplayMetrics());
+                HANDLES_OFFSET_DP, geckoApp.getResources().getDisplayMetrics());
     }
 
     @Override
     public boolean dismiss() {
         if (finishActionMode()) {
             endTextSelection();
             return true;
         }
@@ -67,39 +67,39 @@ public class FloatingToolbarTextSelectio
 
     private void endTextSelection() {
         if (selectionID == 0) {
             return;
         }
 
         final GeckoBundle data = new GeckoBundle(1);
         data.putInt("selectionID", selectionID);
-        GeckoApp.getEventDispatcher().dispatch("TextSelection:End", data);
+        geckoApp.getAppEventDispatcher().dispatch("TextSelection:End", data);
     }
 
     @Override
     public void create() {
         registerForEvents();
     }
 
     @Override
     public void destroy() {
         unregisterFromEvents();
     }
 
     private void registerForEvents() {
-        GeckoApp.getEventDispatcher().registerUiThreadListener(this,
+        geckoApp.getAppEventDispatcher().registerUiThreadListener(this,
                 "TextSelection:ActionbarInit",
                 "TextSelection:ActionbarStatus",
                 "TextSelection:ActionbarUninit",
                 "TextSelection:Visibility");
     }
 
     private void unregisterFromEvents() {
-        GeckoApp.getEventDispatcher().unregisterUiThreadListener(this,
+        geckoApp.getAppEventDispatcher().unregisterUiThreadListener(this,
                 "TextSelection:ActionbarInit",
                 "TextSelection:ActionbarStatus",
                 "TextSelection:ActionbarUninit",
                 "TextSelection:Visibility");
     }
 
     @Override // BundleEventListener
     public void handleMessage(final String event, final GeckoBundle message,
@@ -135,17 +135,17 @@ public class FloatingToolbarTextSelectio
     private void startActionMode(List<TextAction> actions) {
         if (actionMode != null) {
             actionModeCallback.updateActions(actions);
             actionMode.invalidate();
             return;
         }
 
         actionModeCallback = new FloatingActionModeCallback(this, actions);
-        actionMode = activity.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
+        actionMode = geckoApp.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
     }
 
     private boolean finishActionMode() {
         if (actionMode != null) {
             actionMode.finish();
             actionMode = null;
             actionModeCallback = null;
             return true;