Bug 1369393 - Backout GV-based custom tabs, PWAs, and Tabs.java changes. r=me
authorDylan Roeh <droeh@mozilla.com>
Thu, 01 Jun 2017 09:28:31 -0500
changeset 409978 08c3c7ebfabe05ab058468e4f49833e0afc205cc
parent 409977 299efa8c78ee9674d473b029d11916962e8e189e
child 409979 5600d485769bed18de1fc9e8ba8e3f7e66bc771b
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1369393
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 1369393 - Backout GV-based custom tabs, PWAs, and Tabs.java changes. r=me
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/GlobalHistory.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java
mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
mobile/android/base/resources/layout/customtabs_activity.xml
mobile/android/chrome/content/FindHelper.js
mobile/android/chrome/content/browser.js
mobile/android/chrome/geckoview/geckoview.xul
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1413,17 +1413,17 @@ public abstract class GeckoApp extends G
             "RuntimePermissions:Check",
             "Share:Text",
             "SystemUI:Visibility",
             "ToggleChrome:Focus",
             "ToggleChrome:Hide",
             "ToggleChrome:Show",
             null);
 
-        Tabs.getInstance().attachToContext(this, mLayerView, getAppEventDispatcher());
+        Tabs.getInstance().attachToContext(this, mLayerView);
         Tabs.registerOnTabsChangedListener(this);
 
         // Use global layout state change to kick off additional initialization
         mMainLayout.getViewTreeObserver().addOnGlobalLayoutListener(this);
 
         if (Versions.preMarshmallow) {
             mTextSelection = new ActionBarTextSelection(this, getTextSelectPresenter());
         } else {
@@ -1507,17 +1507,17 @@ public abstract class GeckoApp extends G
 
                 synchronized (GeckoApp.this) {
                     mSessionRestoreParsingFinished = true;
                     GeckoApp.this.notifyAll();
                 }
 
                 // If we are doing a restore, send the parsed session data to Gecko.
                 if (!mIsRestoringActivity) {
-                    getAppEventDispatcher().dispatch("Session:Restore", restoreMessage);
+                    EventDispatcher.getInstance().dispatch("Session:Restore", restoreMessage);
                 }
 
                 // Make sure sessionstore.old is either updated or deleted as necessary.
                 getProfile().updateSessionFile(mShouldRestore);
             }
         });
 
         // Perform background initialization.
@@ -2484,17 +2484,16 @@ public abstract class GeckoApp extends G
                     rec.close(GeckoApp.this);
                 }
             });
         }
 
         super.onDestroy();
 
         Tabs.unregisterOnTabsChangedListener(this);
-        Tabs.getInstance().detachFromContext();
 
         if (mShutdownOnDestroy) {
             GeckoApplication.shutdown(!mRestartOnShutdown ? null : new Intent(
                     Intent.ACTION_MAIN, /* uri */ null, getApplicationContext(), getClass()));
         }
     }
 
     public void showSDKVersionError() {
--- a/mobile/android/base/java/org/mozilla/gecko/GlobalHistory.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GlobalHistory.java
@@ -177,17 +177,17 @@ class GlobalHistory {
                 getInstance().update(context.getContentResolver(), db, uri, title);
             }
         });
     }
 
     /* protected */ void checkVisited(final String uri) {
         final String storedURI = ReaderModeUtils.stripAboutReaderUrl(uri);
 
-        final NotifierRunnable runnable = new NotifierRunnable(GeckoAppShell.getApplicationContext());
+        final NotifierRunnable runnable = new NotifierRunnable(GeckoAppShell.getContext());
         mHandler.post(new Runnable() {
             @Override
             public void run() {
                 // this runs on the same handler thread as the processing loop,
                 // so no synchronization needed
                 mPendingUris.add(storedURI);
                 if (mProcessing) {
                     // there's already a runnable queued up or working away, so
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -96,17 +96,16 @@ public class Tabs implements BundleEvent
     public static final int INVALID_TAB_ID = -1;
     // Used to indicate a new tab should be appended to the current tabs.
     public static final int NEW_LAST_INDEX = -1;
 
     private static final AtomicInteger sTabId = new AtomicInteger(0);
     private volatile boolean mInitialTabsAdded;
 
     private Context mAppContext;
-    private EventDispatcher mEventDispatcher;
     private LayerView mLayerView;
     private ContentObserver mBookmarksContentObserver;
     private PersistTabsRunnable mPersistTabsRunnable;
     private int mPrivateClearColor;
 
     public void closeAll() {
         for (final Tab tab : mOrder) {
             Tabs.getInstance().closeTab(tab, false);
@@ -161,21 +160,28 @@ public class Tabs implements BundleEvent
         EventDispatcher.getInstance().registerBackgroundThreadListener(this,
             // BrowserApp already wants this on the background thread.
             "Sanitize:ClearHistory",
             null);
 
         mPrivateClearColor = Color.RED;
     }
 
-    public synchronized void attachToContext(Context context, LayerView layerView, EventDispatcher eventDispatcher) {
+    public synchronized void attachToContext(Context context, LayerView layerView) {
         final Context appContext = context.getApplicationContext();
+        if (mAppContext == appContext) {
+            return;
+        }
+
+        if (mAppContext != null) {
+            // This should never happen.
+            Log.w(LOGTAG, "The application context has changed!");
+        }
 
         mAppContext = appContext;
-        mEventDispatcher = eventDispatcher;
         mLayerView = layerView;
         mPrivateClearColor = ContextCompat.getColor(context, R.color.tabs_tray_grey_pressed);
         mAccountManager = AccountManager.get(appContext);
 
         mAccountListener = new OnAccountsUpdateListener() {
             @Override
             public void onAccountsUpdated(Account[] accounts) {
                 queuePersistAllTabs();
@@ -187,20 +193,16 @@ public class Tabs implements BundleEvent
 
         if (mBookmarksContentObserver != null) {
             // It's safe to use the db here since we aren't doing any I/O.
             final GeckoProfile profile = GeckoProfile.get(context);
             BrowserDB.from(profile).registerBookmarkObserver(getContentResolver(), mBookmarksContentObserver);
         }
     }
 
-    public void detachFromContext() {
-        mLayerView = null;
-    }
-
     /**
      * Gets the tab count corresponding to the category and private state of the
      * selected tab.
      *
      * If the selected tab is a non-private tab, this will return the number of
      * non-private tabs; likewise, if this is a private tab, this will return
      * the number of private tabs.
      *
@@ -325,17 +327,16 @@ public class Tabs implements BundleEvent
 
         if (oldTab != null) {
             notifyListeners(oldTab, TabEvents.UNSELECTED);
         }
 
         // Pass a message to Gecko to update tab state in BrowserApp.
         final GeckoBundle data = new GeckoBundle(1);
         data.putInt("id", tab.getId());
-        mEventDispatcher.dispatch("Tab:Selected", data);
         EventDispatcher.getInstance().dispatch("Tab:Selected", data);
         return tab;
     }
 
     public synchronized boolean selectLastTab() {
         if (mOrder.isEmpty()) {
             return false;
         }
@@ -467,17 +468,17 @@ public class Tabs implements BundleEvent
         selectTab(nextTab.getId());
 
         tab.onDestroy();
 
         // Pass a message to Gecko to update tab state in BrowserApp
         final GeckoBundle data = new GeckoBundle(2);
         data.putInt("tabId", tabId);
         data.putBoolean("showUndoToast", showUndoToast);
-        mEventDispatcher.dispatch("Tab:Closed", data);
+        EventDispatcher.getInstance().dispatch("Tab:Closed", data);
     }
 
     /** Return the tab that will be selected by default after this one is closed */
     public Tab getNextTab(Tab tab) {
         Tab selectedTab = getSelectedTab();
         if (selectedTab != tab)
             return selectedTab;
 
@@ -1077,17 +1078,17 @@ public class Tabs implements BundleEvent
             }
             if (isFirstShownAfterActivityUnhidden) {
                 // We just opened Firefox so we want to show
                 // the toolbar but not animate it to avoid jank.
                 tabToSelect.setShouldShowToolbarWithoutAnimationOnFirstSelection(true);
             }
         }
 
-        mEventDispatcher.dispatch("Tab:Load", data);
+        EventDispatcher.getInstance().dispatch("Tab:Load", data);
 
         if (tabToSelect == null) {
             return null;
         }
 
         if (!delayLoad && !background) {
             selectTab(tabToSelect.getId());
             tracking(url);
@@ -1271,17 +1272,17 @@ public class Tabs implements BundleEvent
 
         notifyListeners(mOrder.get(toPosition), TabEvents.MOVED);
 
         final GeckoBundle data = new GeckoBundle();
         data.putInt("fromTabId", fromTabId);
         data.putInt("fromPosition", fromPosition);
         data.putInt("toTabId", toTabId);
         data.putInt("toPosition", toPosition);
-        mEventDispatcher.dispatch("Tab:Move", data);
+        EventDispatcher.getInstance().dispatch("Tab:Move", data);
     }
 
     /**
      * @return True if the homepage preference is not empty.
      */
     public static boolean hasHomepage(Context context) {
         return !TextUtils.isEmpty(getHomepage(context));
     }
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java
@@ -25,17 +25,16 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import org.mozilla.gecko.GeckoView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SiteIdentity;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.toolbar.SecurityModeUtil;
 import org.mozilla.gecko.toolbar.SiteIdentityPopup;
 import org.mozilla.gecko.util.ColorUtil;
 
 /**
@@ -104,34 +103,35 @@ public class ActionBarPresenter {
     }
 
     /**
      * To display Url in CustomView only and immediately.
      *
      * @param url Url String to display
      */
     public void displayUrlOnly(@NonNull final String url) {
-        updateCustomView(null, url, GeckoView.ProgressListener.STATE_IS_INSECURE);
+        updateCustomView(null, null, url);
     }
 
     /**
-     * Update appearance of CustomView of ActionBar
+     * Update appearance of CustomView of ActionBar.
      *
-     * @param title          Title for current website. Could be null if don't want to show title.
-     * @param url            URL for current website. At least Custom will show this url.
-     * @param securityStatus Security status, possible values given in GeckoView.ProgressListener
+     * @param tab a Tab instance of current web page to provide information to render ActionBar.
      */
-    public void update(final String title, final String url, final int securityStatus) {
+    public void update(@NonNull final Tab tab) {
+        final String title = tab.getTitle();
+        final String url = tab.getBaseDomain();
+
         // Do not update CustomView immediately. If this method be invoked rapidly several times,
         // only apply last one.
         mHandler.removeCallbacks(mUpdateAction);
         mUpdateAction = new Runnable() {
             @Override
             public void run() {
-                updateCustomView(title, url, securityStatus);
+                updateCustomView(tab.getSiteIdentity(), title, url);
             }
         };
         mHandler.postDelayed(mUpdateAction, CUSTOM_VIEW_UPDATE_DELAY);
     }
 
     /**
      * To add a always-show-as-action button to menu, and manually create a view to insert.
      *
@@ -215,25 +215,35 @@ public class ActionBarPresenter {
     /**
      * To update appearance of CustomView of ActionBar, includes its icon, title and url text.
      *
      * @param identity SiteIdentity for current website. Could be null if don't want to show icon.
      * @param title    Title for current website. Could be null if don't want to show title.
      * @param url      URL for current website. At least Custom will show this url.
      */
     @UiThread
-    private void updateCustomView(final String title, final String url, final int securityStatus) {
-        if (securityStatus == GeckoView.ProgressListener.STATE_IS_SECURE) {
+    private void updateCustomView(@Nullable SiteIdentity identity,
+                                  @Nullable String title,
+                                  @NonNull String url) {
+        // update site-info icon
+        if (identity == null) {
+            mIconView.setVisibility(View.INVISIBLE);
+        } else {
+            final SecurityModeUtil.Mode mode = SecurityModeUtil.resolve(identity);
             mIconView.setVisibility(View.VISIBLE);
-            mIconView.setImageLevel(SecurityModeUtil.Mode.LOCK_SECURE.ordinal());
-            // Lock-Secure is special case. Keep its original green color.
-            DrawableCompat.setTintList(mIconView.getDrawable(), null);
-        } else {
-            mIconView.setVisibility(View.INVISIBLE);
-            DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
+            mIconView.setImageLevel(mode.ordinal());
+            mIdentityPopup.setSiteIdentity(identity);
+
+            if (mode == SecurityModeUtil.Mode.LOCK_SECURE) {
+                // Lock-Secure is special case. Keep its original green color.
+                DrawableCompat.setTintList(mIconView.getDrawable(), null);
+            } else {
+                // Icon use same color as TextView.
+                DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
+            }
         }
 
         // If no title to use, use Url as title
         if (TextUtils.isEmpty(title)) {
             mTitleView.setText(url);
             mUrlView.setText(null);
             mUrlView.setVisibility(View.GONE);
         } else {
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -15,104 +15,101 @@ import android.graphics.drawable.Drawabl
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Browser;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.design.widget.Snackbar;
 import android.support.v4.util.SparseArrayCompat;
 import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
+import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.ProgressBar;
 
 import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.GeckoView;
-import org.mozilla.gecko.GeckoViewSettings;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.SingleTabActivity;
 import org.mozilla.gecko.SnackbarBuilder;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
+import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.ColorUtil;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.IntentUtils;
+import org.mozilla.gecko.widget.ActionModePresenter;
 import org.mozilla.gecko.widget.GeckoPopupMenu;
 
 import java.util.List;
 
-public class CustomTabsActivity extends AppCompatActivity
-                                implements GeckoMenu.Callback,
-                                           GeckoView.ContentListener,
-                                           GeckoView.NavigationListener,
-                                           GeckoView.ProgressListener {
+import static org.mozilla.gecko.Tabs.TabEvents;
+
+public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabsChangedListener {
 
     private static final String LOGTAG = "CustomTabsActivity";
 
     private final SparseArrayCompat<PendingIntent> menuItemsIntent = new SparseArrayCompat<>();
     private GeckoPopupMenu popupMenu;
     private View doorhangerOverlay;
     private ActionBarPresenter actionBarPresenter;
+    private ProgressBar mProgressView;
     // A state to indicate whether this activity is finishing with customize animation
     private boolean usingCustomAnimation = false;
 
     private MenuItem menuItemControl;
 
-    private GeckoView mGeckoView;
-
-    private boolean mCanGoBack = false;
-    private boolean mCanGoForward = false;
-    private boolean mCanStop = false;
-    private String mCurrentUrl;
-    private String mCurrentTitle;
-    private int mSecurityStatus = GeckoView.ProgressListener.STATE_IS_INSECURE;
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(R.layout.customtabs_activity);
-
         final SafeIntent intent = new SafeIntent(getIntent());
 
         doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
 
+        mProgressView = (ProgressBar) findViewById(R.id.page_progress);
         final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
         setSupportActionBar(toolbar);
         final ActionBar actionBar = getSupportActionBar();
         bindNavigationCallback(toolbar);
 
         actionBarPresenter = new ActionBarPresenter(actionBar, getActionBarTextColor());
         actionBarPresenter.displayUrlOnly(intent.getDataString());
         actionBarPresenter.setBackgroundColor(IntentUtil.getToolbarColor(intent), getWindow());
         actionBarPresenter.setTextLongClickListener(new UrlCopyListener());
-
-        mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
+    }
 
-        mGeckoView.setNavigationListener(this);
-        mGeckoView.setProgressListener(this);
-        mGeckoView.setContentListener(this);
+    @Override
+    protected void onTabOpenFromIntent(Tab tab) {
+        super.onTabOpenFromIntent(tab);
 
-        final GeckoViewSettings settings = mGeckoView.getSettings();
-        settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
+        final String host = getReferrerHost();
+        recordCustomTabUsage(host);
+        sendTelemetry();
+    }
 
-        if (intent != null && !TextUtils.isEmpty(intent.getDataString())) {
-            mGeckoView.loadUri(intent.getDataString());
-        } else {
-            Log.w(LOGTAG, "No intend found for custom tab");
-            finish();
-        }
+    @Override
+    protected void onTabSelectFromIntent(Tab tab) {
+        super.onTabSelectFromIntent(tab);
+
+        // We already listen for SELECTED events, but if the activity has been destroyed and
+        // subsequently recreated without a different tab having been selected in Gecko in the
+        // meantime, our startup won't trigger a SELECTED event because the selected tab in Gecko
+        // doesn't actually change.
+        actionBarPresenter.update(tab);
     }
 
     private void sendTelemetry() {
         final SafeIntent startIntent = new SafeIntent(getIntent());
 
         Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab");
         if (IntentUtil.hasToolbarColor(startIntent)) {
             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-hasToolbarColor");
@@ -153,44 +150,102 @@ public class CustomTabsActivity extends 
             // Use its package name to retrieve animation resource
             return IntentUtil.getAnimationPackageName(new SafeIntent(getIntent()));
         } else {
             return super.getPackageName();
         }
     }
 
     @Override
+    public void onDone() {
+        // We're most probably running within a foreign app's task, so we have no choice what to
+        // call here if we want to allow the user to return to that task's previous activity.
+        finish();
+    }
+
+    @Override
     public void finish() {
         super.finish();
 
         final SafeIntent intent = new SafeIntent(getIntent());
         // When 3rd party app launch this Activity, it could also specify custom exit-animation.
         if (IntentUtil.hasExitAnimation(intent)) {
             usingCustomAnimation = true;
             overridePendingTransition(IntentUtil.getEnterAnimationRes(intent),
                     IntentUtil.getExitAnimationRes(intent));
             usingCustomAnimation = false;
         }
     }
 
     @Override
-    public void onBackPressed() {
-        if (mCanGoBack) {
-            mGeckoView.goBack();
-        } else {
-            finish();
+    protected int getNewTabFlags() {
+        return Tabs.LOADURL_CUSTOMTAB | super.getNewTabFlags();
+    }
+
+    @Override
+    public int getLayout() {
+        return R.layout.customtabs_activity;
+    }
+
+    @Override
+    public View getDoorhangerOverlay() {
+        return doorhangerOverlay;
+    }
+
+    @Override
+    public void onTabChanged(Tab tab, TabEvents msg, String data) {
+        super.onTabChanged(tab, msg, data);
+
+        if (!Tabs.getInstance().isSelectedTab(tab) ||
+                tab.getType() != Tab.TabType.CUSTOMTAB) {
+            return;
         }
+
+        if (msg == TabEvents.START
+                || msg == TabEvents.STOP
+                || msg == TabEvents.ADDED
+                || msg == TabEvents.LOAD_ERROR
+                || msg == TabEvents.LOADED
+                || msg == TabEvents.LOCATION_CHANGE
+                || msg == TabEvents.SELECTED) {
+
+            updateProgress((tab.getState() == Tab.STATE_LOADING),
+                    tab.getLoadProgress());
+        }
+
+        if (msg == TabEvents.LOCATION_CHANGE
+                || msg == TabEvents.SECURITY_CHANGE
+                || msg == TabEvents.TITLE
+                || msg == TabEvents.SELECTED) {
+            actionBarPresenter.update(tab);
+        }
+
+        updateMenuItemForward();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mLayerView.getDynamicToolbarAnimator().setPinned(true, PinReason.CUSTOM_TAB);
+        actionBarPresenter.onResume();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mLayerView.getDynamicToolbarAnimator().setPinned(false, PinReason.CUSTOM_TAB);
+        actionBarPresenter.onPause();
     }
 
     // Usually should use onCreateOptionsMenu() to initialize menu items. But GeckoApp overwrite
     // it to support custom menu(Bug 739412). Then the parameter *menu* in this.onCreateOptionsMenu()
     // and this.onPrepareOptionsMenu() are different instances - GeckoApp.onCreatePanelMenu() changed it.
     // CustomTabsActivity only use standard menu in ActionBar, so initialize menu here.
     @Override
-    public boolean onCreateOptionsMenu(final Menu menu) {
+    public boolean onCreatePanelMenu(final int id, final Menu menu) {
 
         // if 3rd-party app asks to add an action button
         SafeIntent intent = new SafeIntent(getIntent());
         if (IntentUtil.hasActionButton(intent)) {
             final Bitmap bitmap = IntentUtil.getActionButtonIcon(intent);
             final Drawable icon = new BitmapDrawable(getResources(), bitmap);
             final boolean shouldTint = IntentUtil.isActionButtonTinted(intent);
             actionBarPresenter.addActionButton(menu, icon, shouldTint)
@@ -216,26 +271,16 @@ public class CustomTabsActivity extends 
                     }
                 });
 
         updateMenuItemForward();
         return true;
     }
 
     @Override
-    public boolean onMenuItemClick(MenuItem item) {
-        return onOptionsItemSelected(item);
-    }
-
-    @Override
-    public boolean onMenuItemLongClick(MenuItem item) {
-        return false;
-    }
-
-    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case android.R.id.home:
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-home");
                 finish();
                 return true;
             case R.id.share:
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-share");
@@ -267,29 +312,53 @@ public class CustomTabsActivity extends 
     /**
      * Called when the menu that's been clicked is added by the client
      */
     private void onCustomMenuItemClicked(PendingIntent intent) {
         Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-customized-menu");
         performPendingIntent(intent);
     }
 
+    @Override
+    protected ActionModePresenter getTextSelectPresenter() {
+        return new ActionModePresenter() {
+            private ActionMode mMode;
+
+            @Override
+            public void startActionMode(ActionMode.Callback callback) {
+                mMode = startSupportActionMode(callback);
+            }
+
+            @Override
+            public void endActionMode() {
+                if (mMode != null) {
+                    mMode.finish();
+                }
+            }
+        };
+    }
+
     private void bindNavigationCallback(@NonNull final Toolbar toolbar) {
         toolbar.setNavigationOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                finish();
+                onDone();
+                final Tabs tabs = Tabs.getInstance();
+                final Tab tab = tabs.getSelectedTab();
+                mSuppressActivitySwitch = true;
+                tabs.closeTab(tab);
             }
         });
     }
 
     private void performPendingIntent(@NonNull PendingIntent pendingIntent) {
         // bug 1337771: If intent-creator haven't set data url, call send() directly won't work.
         final Intent additional = new Intent();
-        additional.setData(Uri.parse(mCurrentUrl));
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        additional.setData(Uri.parse(tab.getURL()));
         try {
             pendingIntent.send(this, 0, additional);
         } catch (PendingIntent.CanceledException e) {
             Log.w(LOGTAG, "Performing a canceled pending intent", e);
         }
     }
 
     /**
@@ -361,99 +430,110 @@ public class CustomTabsActivity extends 
     private void updateMenuItemForward() {
         if ((popupMenu == null)
                 || (popupMenu.getMenu() == null)
                 || (popupMenu.getMenu().findItem(R.id.custom_tabs_menu_forward) == null)) {
             return;
         }
 
         final MenuItem forwardMenuItem = popupMenu.getMenu().findItem(R.id.custom_tabs_menu_forward);
-        forwardMenuItem.setEnabled(mCanGoForward);
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        final boolean enabled = (tab != null && tab.canDoForward());
+        forwardMenuItem.setEnabled(enabled);
     }
 
     /**
-     * Update loading status of current page
+     * Update loading progress of current page
+     *
+     * @param isLoading to indicate whether ProgressBar should be visible or not
+     * @param progress  value of loading progress in percent, should be 0 - 100.
      */
-    private void updateCanStop() {
+    private void updateProgress(final boolean isLoading, final int progress) {
+        if (isLoading) {
+            mProgressView.setVisibility(View.VISIBLE);
+            mProgressView.setProgress(progress);
+        } else {
+            mProgressView.setVisibility(View.GONE);
+        }
+
         if (menuItemControl != null) {
             Drawable icon = menuItemControl.getIcon();
-            if (mCanStop) {
-                icon.setLevel(0);
-            } else {
-                icon.setLevel(100);
-            }
+            icon.setLevel(progress);
         }
     }
 
     /**
-     * Update the state of the action bar
-     */
-    private void updateActionBar() {
-        actionBarPresenter.update(mCurrentTitle, mCurrentUrl, mSecurityStatus);
-    }
-
-    /**
      * Call this method to reload page, or stop page loading if progress not complete yet.
      */
     private void onLoadingControlClicked() {
-        if (mCanStop) {
-            // TODO: enable this after implementing GeckoView.stop()
-            //mGeckoView.stop();
-        } else {
-            mGeckoView.reload();
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        if (tab != null) {
+            if (tab.getLoadProgress() == Tab.LOAD_PROGRESS_STOP) {
+                tab.doReload(true);
+            } else {
+                tab.doStop();
+            }
         }
     }
 
     private void onForwardClicked() {
-        if (mCanGoForward) {
-            mGeckoView.goForward();
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        if ((tab != null) && tab.canDoForward()) {
+            tab.doForward();
         }
     }
 
     /**
      * Callback for Open-in menu item.
      */
     private void onOpenInClicked() {
-        final Intent intent = new Intent();
-        intent.setData(Uri.parse(mCurrentUrl));
-        intent.setAction(Intent.ACTION_VIEW);
-        startActivity(intent);
-        finish();
+        final Tab tab = Tabs.getInstance().getSelectedTab();
+        if (tab != null) {
+            // To launch default browser with url of current tab.
+            final Intent intent = new Intent();
+            intent.setData(Uri.parse(tab.getURL()));
+            intent.setAction(Intent.ACTION_VIEW);
+            startActivity(intent);
+            finish();
+        }
     }
 
     private void onActionButtonClicked() {
         Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-action-button");
         PendingIntent pendingIntent = IntentUtil.getActionButtonPendingIntent(new SafeIntent(getIntent()));
         performPendingIntent(pendingIntent);
     }
 
 
     /**
      * Callback for Share menu item.
      */
     private void onShareClicked() {
-        if (!TextUtils.isEmpty(mCurrentUrl)) {
+        final String url = Tabs.getInstance().getSelectedTab().getURL();
+
+        if (!TextUtils.isEmpty(url)) {
             Intent shareIntent = new Intent(Intent.ACTION_SEND);
             shareIntent.setType("text/plain");
-            shareIntent.putExtra(Intent.EXTRA_TEXT, mCurrentUrl);
+            shareIntent.putExtra(Intent.EXTRA_TEXT, url);
 
             Intent chooserIntent = Intent.createChooser(shareIntent, getString(R.string.share_title));
             chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             startActivity(chooserIntent);
         }
     }
 
     /**
      * Listener when user long-click ActionBar to copy URL.
      */
     private class UrlCopyListener implements View.OnLongClickListener {
         @Override
         public boolean onLongClick(View v) {
-            if (!TextUtils.isEmpty(mCurrentUrl)) {
-                Clipboard.setText(mCurrentUrl);
+            final String url = Tabs.getInstance().getSelectedTab().getURL();
+            if (!TextUtils.isEmpty(url)) {
+                Clipboard.setText(url);
                 SnackbarBuilder.builder(CustomTabsActivity.this)
                         .message(R.string.custom_tabs_hint_url_copy)
                         .duration(Snackbar.LENGTH_SHORT)
                         .buildAndShow();
             }
             return true;
         }
     }
@@ -469,64 +549,9 @@ public class CustomTabsActivity extends 
             return referrer.getHost();
         }
         String referrerName = intent.getStringExtra("android.intent.extra.REFERRER_NAME");
         if (referrerName != null) {
             return Uri.parse(referrerName).getHost();
         }
         return null;
     }
-
-    /* GeckoView.NavigationListener */
-    @Override
-    public void onLocationChange(GeckoView view, String url) {
-        mCurrentUrl = url;
-        updateActionBar();
-    }
-
-    @Override
-    public void onCanGoBack(GeckoView view, boolean canGoBack) {
-        mCanGoBack = canGoBack;
-    }
-
-    @Override
-    public void onCanGoForward(GeckoView view, boolean canGoForward) {
-        mCanGoForward = canGoForward;
-        updateMenuItemForward();
-    }
-
-    /* GeckoView.ProgressListener */
-    @Override
-    public void onPageStart(GeckoView view, String url) {
-        mCurrentUrl = url;
-        mCanStop = true;
-        updateActionBar();
-        updateCanStop();
-    }
-
-    @Override
-    public void onPageStop(GeckoView view, boolean success) {
-        mCanStop = false;
-        updateCanStop();
-    }
-
-    @Override
-    public void onSecurityChange(GeckoView view, int status) {
-        if ((status & STATE_IS_INSECURE) != 0) {
-            mSecurityStatus = STATE_IS_INSECURE;
-        } else if ((status & STATE_IS_BROKEN) != 0) {
-            mSecurityStatus = STATE_IS_BROKEN;
-        } else if ((status & STATE_IS_SECURE) != 0) {
-            mSecurityStatus = STATE_IS_SECURE;
-        }
-        updateActionBar();
-    }
-
-    /* GeckoView.ContentListener */
-    @Override
-    public void onTitleChange(GeckoView view, String title) {
-        mCurrentTitle = title;
-        updateActionBar();
-    }
-
-    @Override
-    public void onFullScreen(GeckoView view, boolean fullScreen) {}
 }
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -2,43 +2,39 @@
  * 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.webapps;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.List;
 
 import android.app.ActivityManager;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
+import android.support.v7.app.ActionBar;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import org.json.JSONObject;
 import org.json.JSONException;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoView;
-import org.mozilla.gecko.GeckoViewSettings;
 import org.mozilla.gecko.SingleTabActivity;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.icons.decoders.FaviconDecoder;
 import org.mozilla.gecko.icons.decoders.LoadFaviconResult;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
@@ -47,81 +43,188 @@ import org.mozilla.gecko.util.ColorUtil;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.widget.ActionModePresenter;
 import org.mozilla.gecko.widget.AnchoredPopup;
 
 import static org.mozilla.gecko.Tabs.TabEvents;
 
-public class WebAppActivity extends AppCompatActivity
-                            implements GeckoView.NavigationListener {
+public class WebAppActivity extends SingleTabActivity {
     private static final String LOGTAG = "WebAppActivity";
 
     public static final String MANIFEST_PATH = "MANIFEST_PATH";
     private static final String SAVED_INTENT = "savedIntent";
 
     private TextView mUrlView;
-    private GeckoView mGeckoView;
+    private View doorhangerOverlay;
 
-    private Uri mScope;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0 &&
-            savedInstanceState != null) {
+        savedInstanceState != null) {
             // Even though we're a single task activity, Android's task switcher has the
             // annoying habit of never updating its stored intent after our initial creation,
             // even if we've been subsequently started with a new intent.
 
             // This below is needed if we should ever decide to store a custom class as intent extra.
             savedInstanceState.setClassLoader(getClass().getClassLoader());
 
             Intent lastLaunchIntent = savedInstanceState.getParcelable(SAVED_INTENT);
             setIntent(lastLaunchIntent);
         }
 
         super.onCreate(savedInstanceState);
 
-        setContentView(R.layout.customtabs_activity);
-
         final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
         setSupportActionBar(toolbar);
 
+        final ProgressBar progressBar = (ProgressBar) findViewById(R.id.page_progress);
+        progressBar.setVisibility(View.GONE);
+
         final ActionBar actionBar = getSupportActionBar();
         actionBar.setCustomView(R.layout.webapps_action_bar_custom_view);
         actionBar.setDisplayShowCustomEnabled(true);
         actionBar.setDisplayShowTitleEnabled(false);
         actionBar.hide();
 
+        doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
+
         final View customView = actionBar.getCustomView();
         mUrlView = (TextView) customView.findViewById(R.id.webapps_action_bar_url);
 
-        mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
+        EventDispatcher.getInstance().registerUiThreadListener(this,
+                "Website:AppEntered",
+                "Website:AppLeft",
+                null);
+    }
 
-        mGeckoView.setNavigationListener(this);
+    @Override
+    public View getDoorhangerOverlay() {
+        return doorhangerOverlay;
+    }
 
-        final GeckoViewSettings settings = mGeckoView.getSettings();
-        settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
+    @Override
+    public int getLayout() {
+        return R.layout.customtabs_activity;
+    }
 
-        final Uri u = getIntent().getData();
-        if (u != null) {
-            mGeckoView.loadUri(u.toString());
+    @Override
+    public void handleMessage(final String event, final GeckoBundle message,
+                              final EventCallback callback) {
+        super.handleMessage(event, message, callback);
+
+        if (message == null ||
+                !message.containsKey("tabId") || message.getInt("tabId") != mLastSelectedTabId) {
+            return;
         }
 
-        loadManifest(getIntent().getStringExtra(MANIFEST_PATH));
+        switch (event) {
+            case "Website:AppEntered":
+                getSupportActionBar().hide();
+                break;
+
+            case "Website:AppLeft":
+                getSupportActionBar().show();
+                break;
+        }
+    }
+
+    @Override
+    public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
+        super.onTabChanged(tab, msg, data);
+
+        if (tab == null || !Tabs.getInstance().isSelectedTab(tab) ||
+                tab.getType() != Tab.TabType.WEBAPP) {
+            return;
+        }
+
+        if (msg == TabEvents.LOCATION_CHANGE ||
+                msg == TabEvents.SELECTED) {
+            mUrlView.setText(tab.getURL());
+        }
     }
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
         outState.putParcelable(SAVED_INTENT, getIntent());
     }
 
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        EventDispatcher.getInstance().unregisterUiThreadListener(this,
+                "Website:AppEntered",
+                "Website:AppLeft",
+                null);
+    }
+
+    @Override
+    protected int getNewTabFlags() {
+        return Tabs.LOADURL_WEBAPP | super.getNewTabFlags();
+    }
+
+    @Override
+    protected void onTabOpenFromIntent(Tab tab) {
+        super.onTabOpenFromIntent(tab);
+        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "webapp");
+        loadManifest(tab.getManifestPath());
+    }
+
+    /**
+     * In case this activity and its tab are reused (the user has opened
+     *  > 10 current web apps), we check that app launched is still within
+     * the same host as the intent has set.
+     * If it isn't, we reload the intent URL.
+     */
+    @Override
+    protected void onTabSelectFromIntent(Tab tab) {
+        super.onTabSelectFromIntent(tab);
+
+        SafeIntent intent = new SafeIntent(getIntent());
+
+        final String launchUrl = intent.getDataString();
+        final String currentUrl = tab.getURL();
+        final boolean isSameDomain = Uri.parse(currentUrl).getHost()
+                .equals(Uri.parse(launchUrl).getHost());
+
+        final String manifestPath;
+        if (!isSameDomain) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "webapp");
+            manifestPath = intent.getStringExtra(MANIFEST_PATH);
+            tab.setManifestUrl(manifestPath);
+            Tabs.getInstance().loadUrl(launchUrl);
+        } else {
+            manifestPath = tab.getManifestPath();
+        }
+        loadManifest(manifestPath);
+    }
+
+    @Override
+    protected ActionModePresenter getTextSelectPresenter() {
+        return new ActionModePresenter() {
+            private ActionMode mMode;
+
+            @Override
+            public void startActionMode(ActionMode.Callback callback) {
+                mMode = startSupportActionMode(callback);
+            }
+
+            @Override
+            public void endActionMode() {
+                if (mMode != null) {
+                    mMode.finish();
+                }
+            }
+        };
+    }
+
     private void loadManifest(String manifestPath) {
         if (TextUtils.isEmpty(manifestPath)) {
             Log.e(LOGTAG, "Missing manifest");
             return;
         }
         // The customisations defined in the manifest only work on Android API 21+
         if (AppConstants.Versions.preLollipop) {
             return;
@@ -129,17 +232,16 @@ public class WebAppActivity extends AppC
 
         try {
             final File manifestFile = new File(manifestPath);
             final JSONObject manifest = FileUtils.readJSONObjectFromFile(manifestFile);
             final JSONObject manifestField = manifest.getJSONObject("manifest");
             final Integer color = readColorFromManifest(manifestField);
             final String name = readNameFromManifest(manifestField);
             final Bitmap icon = readIconFromManifest(manifest);
-            mScope = readScopeFromManifest(manifest, manifestPath);
             final ActivityManager.TaskDescription taskDescription = (color == null)
                     ? new ActivityManager.TaskDescription(name, icon)
                     : new ActivityManager.TaskDescription(name, icon, color);
 
             updateStatusBarColor(color);
             setTaskDescription(taskDescription);
 
         } catch (IOException | JSONException e) {
@@ -175,81 +277,15 @@ public class WebAppActivity extends AppC
     }
 
     private Bitmap readIconFromManifest(JSONObject manifest) {
         final String iconStr = manifest.optString("cached_icon", null);
         if (iconStr == null) {
             return null;
         }
         final LoadFaviconResult loadIconResult = FaviconDecoder
-            .decodeDataURI(this, iconStr);
+            .decodeDataURI(getContext(), iconStr);
         if (loadIconResult == null) {
             return null;
         }
         return loadIconResult.getBestBitmap(GeckoAppShell.getPreferredIconSize());
     }
-
-    private Uri readScopeFromManifest(JSONObject manifest, String manifestPath) {
-        final String scopeStr = manifest.optString("scope", null);
-        if (scopeStr == null) {
-            return null;
-        }
-
-        Uri res = Uri.parse(scopeStr);
-        if (res.isRelative()) {
-            // TODO: Handle this more correctly.
-            return null;
-        }
-
-        return res;
-    }
-
-    private boolean isInScope(String url) {
-        if (mScope == null) {
-            return true;
-        }
-
-        final Uri uri = Uri.parse(url);
-
-        if (!uri.getScheme().equals(mScope.getScheme())) {
-            return false;
-        }
-
-        if (!uri.getHost().equals(mScope.getHost())) {
-            return false;
-        }
-
-        final List<String> scopeSegments = mScope.getPathSegments();
-        final List<String> urlSegments = uri.getPathSegments();
-
-        if (scopeSegments.size() > urlSegments.size()) {
-            return false;
-        }
-
-        for (int i = 0; i < scopeSegments.size(); i++) {
-            if (!scopeSegments.get(i).equals(urlSegments.get(i))) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /* GeckoView.NavigationListener */
-    @Override
-    public void onLocationChange(GeckoView view, String url) {
-        if (isInScope(url)) {
-            getSupportActionBar().hide();
-        } else {
-            getSupportActionBar().show();
-        }
-
-        mUrlView.setText(url);
-    }
-
-    @Override
-    public void onCanGoBack(GeckoView view, boolean canGoBack) {
-    }
-
-    @Override
-    public void onCanGoForward(GeckoView view, boolean canGoForward) {
-    }
 }
--- a/mobile/android/base/resources/layout/customtabs_activity.xml
+++ b/mobile/android/base/resources/layout/customtabs_activity.xml
@@ -7,31 +7,66 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/root_layout"
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
+    <!--
+        This layout is quite complex because GeckoApp accesses all view groups
+        in this tree. In a perfect world this should just include a GeckoView.
+    -->
+
     <android.support.v7.widget.Toolbar
         android:id="@id/actionbar"
         android:layout_width="match_parent"
         android:layout_height="?attr/actionBarSize"
         android:elevation="4dp"
         android:background="@color/text_and_tabs_tray_grey"
         app:layout_scrollFlags="scroll|enterAlways"/>
 
-    <org.mozilla.gecko.GeckoView
-        android:id="@+id/gecko_view"
-        android:layout_width="fill_parent"
+    <view class="org.mozilla.gecko.GeckoApp$MainLayout"
+        android:id="@+id/main_layout"
+        android:layout_width="match_parent"
         android:layout_below="@id/actionbar"
         android:layout_height="match_parent"
-        android:scrollbars="none"/>
+        android:background="@android:color/transparent">
+
+        <RelativeLayout android:id="@+id/gecko_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_below="@+id/tablet_tab_strip"
+            android:layout_above="@+id/find_in_page">
+
+            <fragment class="org.mozilla.gecko.GeckoViewFragment"
+                android:id="@+id/layer_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:scrollbars="none"/>
+
+            <org.mozilla.gecko.FormAssistPopup android:id="@+id/form_assist_popup"
+                                               android:layout_width="match_parent"
+                                               android:layout_height="match_parent"
+                                               android:visibility="gone"/>
+
+        </RelativeLayout>
+
+    </view>
+
+    <ProgressBar
+        android:id="@id/page_progress"
+        style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="4dp"
+        android:layout_alignTop="@id/main_layout"
+        android:background="@drawable/url_bar_bg"
+        android:progressDrawable="@drawable/progressbar"
+        tools:progress="70"/>
 
     <View android:id="@+id/custom_tabs_doorhanger_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@color/dark_transparent_overlay"
-        android:alpha="0"
-        android:layerType="hardware"/>
-
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@color/dark_transparent_overlay"
+          android:alpha="0"
+          android:layerType="hardware"/>
 </RelativeLayout>
\ No newline at end of file
--- a/mobile/android/chrome/content/FindHelper.js
+++ b/mobile/android/chrome/content/FindHelper.js
@@ -82,17 +82,17 @@ var FindHelper = {
       throw new Error("FindHelper: " + e + "\n" +
         "JS stack: \n" + (e.stack || Components.stack.formattedStack));
     }
 
     this._finder.addResultListener(this);
     this._initialViewport = JSON.stringify(this._targetTab.getViewport());
     this._viewportChanged = false;
 
-    WindowEventDispatcher.registerListener(this, [
+    GlobalEventDispatcher.registerListener(this, [
       "Tab:Selected",
     ]);
   },
 
   /**
    * Detach from the Finder instance (so stop listening for messages) and stop
    * tracking the active viewport.
    */
@@ -104,17 +104,17 @@ var FindHelper = {
 
     this._finder.removeSelection();
     this._finder.removeResultListener(this);
     this._finder = null;
     this._targetTab = null;
     this._initialViewport = null;
     this._viewportChanged = false;
 
-    WindowEventDispatcher.unregisterListener(this, [
+    GlobalEventDispatcher.unregisterListener(this, [
       "Tab:Selected",
     ]);
   },
 
   /**
    * When the FindInPageBar closes, it's time to stop listening for its messages.
    */
   _findClosed: function() {
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -384,25 +384,21 @@ var BrowserApp = {
     Services.obs.notifyObservers(this.browser, "BrowserChrome:Ready");
 
     this.deck = document.getElementById("browsers");
 
     BrowserEventHandler.init();
 
     Services.androidBridge.browserApp = this;
 
-    WindowEventDispatcher.registerListener(this, [
-      "Session:Restore",
+    GlobalEventDispatcher.registerListener(this, [
       "Tab:Load",
       "Tab:Selected",
       "Tab:Closed",
       "Tab:Move",
-    ]);
-
-    GlobalEventDispatcher.registerListener(this, [
       "Browser:LoadManifest",
       "Browser:Quit",
       "Fonts:Reload",
       "FormHistory:Init",
       "FullScreen:Exit",
       "Locale:OS",
       "Locale:Changed",
       "Passwords:Init",
@@ -1850,20 +1846,16 @@ var BrowserApp = {
           let sh = webNav.sessionHistory;
           if (sh)
             webNav = sh.QueryInterface(Ci.nsIWebNavigation);
         } catch (e) {}
         webNav.reload(flags);
         break;
       }
 
-      case "Session:Restore":
-        GlobalEventDispatcher.dispatch("Session:Restore", data);
-        break;
-
       case "Session:Stop":
         browser.stop();
         break;
 
       case "Tab:Load": {
         let url = data.url;
         let flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP
                   | Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
--- a/mobile/android/chrome/geckoview/geckoview.xul
+++ b/mobile/android/chrome/geckoview/geckoview.xul
@@ -2,15 +2,15 @@
 <!-- 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/. -->
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 
 <window id="main-window"
         onload="startup();"
-        windowtype="navigator:geckoview"
+        windowtype="navigator:browser"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <browser id="content" type="content" primary="true" src="about:blank" flex="1"/>
 
   <script type="application/javascript" src="chrome://geckoview/content/geckoview.js"/>
 </window>