Bug 1366098 - Part 1 - Remove Fennec tab type infrastructure. r?walkingice draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Tue, 27 Jun 2017 14:13:40 -0700
changeset 606961 1cf6c9678dd382767e3fdd2295f96b49f768a055
parent 606960 e08815e2344649ac5bf079135976ccab9fee0a3a
child 636898 c017c828e846f195861b2a6c1c3f9a61cc33b6b0
push id67843
push usermozilla@buttercookie.de
push dateTue, 11 Jul 2017 17:06:09 +0000
reviewerswalkingice
bugs1366098
milestone56.0a1
Bug 1366098 - Part 1 - Remove Fennec tab type infrastructure. r?walkingice The new, preferred solution for displaying additional web content outside of our normal tab infrastructure and UI is to use a separate GeckoView instance. Therefore, the support for different having additional "tab" types that aren't displayed in the normal UI, as well as for multiple GeckoApp instances displaying different tabs is no longer needed and can be removed. MozReview-Commit-ID: FNx0gJIKybr
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/IntentHelper.java
mobile/android/base/java/org/mozilla/gecko/PrivateTab.java
mobile/android/base/java/org/mozilla/gecko/SingleTabActivity.java
mobile/android/base/java/org/mozilla/gecko/Tab.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabStrip.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabStripView.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
mobile/android/base/moz.build
mobile/android/chrome/content/browser.js
mobile/android/components/SessionStore.js
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testFilterOpenTab.java
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -176,20 +176,17 @@ import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Pattern;
 
-import static org.mozilla.gecko.Tab.TabType;
-import static org.mozilla.gecko.Tabs.INVALID_TAB_ID;
 import static org.mozilla.gecko.mma.MmaDelegate.NEW_TAB;
-
 public class BrowserApp extends GeckoApp
                         implements ActionModePresenter,
                                    AnchoredPopup.OnVisibilityChangeListener,
                                    BookmarkEditFragment.Callbacks,
                                    BrowserSearch.OnEditSuggestionListener,
                                    BrowserSearch.OnSearchListener,
                                    DynamicToolbarAnimator.MetricsListener,
                                    DynamicToolbarAnimator.ToolbarChromeProxy,
@@ -442,21 +439,16 @@ public class BrowserApp extends GeckoApp
 
         if (HardwareUtils.isTablet() && msg == TabEvents.SELECTED) {
             updateEditingModeForTab(tab);
         }
 
         super.onTabChanged(tab, msg, data);
     }
 
-    @Override
-    protected boolean saveAsLastSelectedTab(Tab tab) {
-        return tab.getType() == TabType.BROWSING;
-    }
-
     private void updateEditingModeForTab(final Tab selectedTab) {
         // (bug 1086983 comment 11) Because the tab may be selected from the gecko thread and we're
         // running this code on the UI thread, the selected tab argument may not still refer to the
         // selected tab. However, that means this code should be run again and the initial state
         // changes will be overridden. As an optimization, we can skip this update, but it may have
         // unknown side-effects so we don't.
         if (!Tabs.getInstance().isSelectedTab(selectedTab)) {
             Log.w(LOGTAG, "updateEditingModeForTab: Given tab is expected to be selected tab");
@@ -1188,48 +1180,16 @@ public class BrowserApp extends GeckoApp
         processTabQueue();
 
         for (BrowserAppDelegate delegate : delegates) {
             delegate.onResume(this);
         }
     }
 
     @Override
-    protected void restoreLastSelectedTab() {
-        if (mLastSelectedTabId < 0) {
-            // Normally, session restore will select the correct tab when starting up, however this
-            // is linked to Gecko powering up. If we're not the first activity to launch, the
-            // previously running activity might have already overwritten this by selecting a tab of
-            // its own.
-            // Therefore we check whether the session file parser has left a note for us with the
-            // correct tab to be initially selected on *BrowserApp* startup.
-            SharedPreferences prefs = getSharedPreferencesForProfile();
-            mLastSelectedTabId = prefs.getInt(STARTUP_SELECTED_TAB, INVALID_TAB_ID);
-            mLastSessionUUID = prefs.getString(STARTUP_SESSION_UUID, null);
-
-            SharedPreferences.Editor editor = prefs.edit();
-            editor.remove(STARTUP_SELECTED_TAB);
-            editor.remove(STARTUP_SESSION_UUID);
-            editor.apply();
-        }
-
-        final Tabs tabs = Tabs.getInstance();
-        final Tab tabToSelect = tabs.getTab(mLastSelectedTabId);
-
-        if (tabToSelect != null && GeckoApplication.getSessionUUID().equals(mLastSessionUUID) &&
-                tabToSelect.getType() == TabType.BROWSING) {
-            tabs.selectTab(mLastSelectedTabId);
-        } else {
-            if (!tabs.selectLastTab(TabType.BROWSING)) {
-                tabs.loadUrl(Tabs.getHomepageForStartupTab(this), Tabs.LOADURL_NEW_TAB);
-            }
-        }
-    }
-
-    @Override
     public void onPause() {
         super.onPause();
         if (mIsAbortingAppLaunch) {
             return;
         }
 
         if (mHasResumed) {
             // Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
@@ -2428,19 +2388,19 @@ public class BrowserApp extends GeckoApp
             return false;
         }
 
         final Tabs tabs = Tabs.getInstance();
         final Tab selectedTab = tabs.getSelectedTab();
         final Tab tab;
 
         if (AboutPages.isAboutReader(url)) {
-            tab = tabs.getFirstReaderTabForUrl(url, selectedTab.isPrivate(), selectedTab.getType());
+            tab = tabs.getFirstReaderTabForUrl(url, selectedTab.isPrivate());
         } else {
-            tab = tabs.getFirstTabForUrl(url, selectedTab.isPrivate(), selectedTab.getType());
+            tab = tabs.getFirstTabForUrl(url, selectedTab.isPrivate());
         }
 
         if (tab == null) {
             return false;
         }
 
         return maybeSwitchToTab(tab.getId());
     }
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -101,17 +101,16 @@ import android.widget.Toast;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -145,21 +144,16 @@ public abstract class GeckoApp extends G
     public static final String ACTION_INIT_PW              = "org.mozilla.gecko.INIT_PW";
     public static final String ACTION_SWITCH_TAB           = "org.mozilla.gecko.SWITCH_TAB";
     public static final String ACTION_SHUTDOWN             = "org.mozilla.gecko.SHUTDOWN";
 
     public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER";
 
     public static final String EXTRA_STATE_BUNDLE          = "stateBundle";
 
-    protected static final String LAST_SELECTED_TAB        = "lastSelectedTab";
-    protected static final String LAST_SESSION_UUID        = "lastSessionUUID";
-    protected static final String STARTUP_SELECTED_TAB     = "restoredSelectedTab";
-    protected static final String STARTUP_SESSION_UUID     = "restorationSessionUUID";
-
     public static final String PREFS_ALLOW_STATE_BUNDLE    = "allowStateBundle";
     public static final String PREFS_FLASH_USAGE           = "playFlashCount";
     public static final String PREFS_VERSION_CODE          = "versionCode";
     public static final String PREFS_WAS_STOPPED           = "wasStopped";
     public static final String PREFS_CRASHED_COUNT         = "crashedCount";
     public static final String PREFS_CLEANUP_TEMP_FILES    = "cleanupTempFiles";
 
     // Originally, this was only used for the telemetry core ping logic. To avoid
@@ -174,19 +168,16 @@ public abstract class GeckoApp extends G
     private static final int CLEANUP_DEFERRAL_SECONDS = 15;
 
     // Length of time in ms during which crashes are classified as startup crashes
     // for crash loop detection purposes.
     private static final int STARTUP_PHASE_DURATION_MS = 30 * 1000;
 
     private static boolean sAlreadyLoaded;
 
-    protected boolean mIgnoreLastSelectedTab;
-    protected static WeakReference<GeckoApp> mLastActiveGeckoApp;
-
     protected RelativeLayout mRootLayout;
     protected RelativeLayout mMainLayout;
 
     protected RelativeLayout mGeckoLayout;
     private OrientationEventListener mCameraOrientationEventListener;
     protected MenuPanel mMenuPanel;
     protected Menu mMenu;
     protected boolean mIsRestoringActivity;
@@ -204,29 +195,23 @@ public abstract class GeckoApp extends G
 
     private FullScreenHolder mFullScreenPluginContainer;
     private View mFullScreenPluginView;
 
     protected boolean mLastSessionCrashed;
     protected boolean mShouldRestore;
     private boolean mSessionRestoreParsingFinished = false;
 
-    protected int mLastSelectedTabId = INVALID_TAB_ID;
-    protected String mLastSessionUUID = null;
-    protected boolean mSuppressActivitySwitch = false;
-
     private boolean foregrounded = false;
 
     private static final class LastSessionParser extends SessionParser {
         private JSONArray tabs;
         private JSONObject windowObject;
         private boolean loadingExternalURL;
 
-        private int selectedTabId = INVALID_TAB_ID;
-
         private boolean selectNextTab;
         private boolean tabsWereSkipped;
         private boolean tabsWereProcessed;
 
         private SparseIntArray tabIdMap;
 
         /**
          * @param loadingExternalURL Pass true if we're going to open an additional tab to load an
@@ -243,25 +228,16 @@ public abstract class GeckoApp extends G
         public boolean allTabsSkipped() {
             return tabsWereSkipped && !tabsWereProcessed;
         }
 
         public int getNewTabId(int oldTabId) {
             return tabIdMap.get(oldTabId, INVALID_TAB_ID);
         }
 
-        /**
-         * @return The index of the tab that should be selected according to the session store data.
-         *         In conjunction with opening external tabs, this might not be the tab that
-         *         actually gets selected in the end, though.
-         */
-        public int getStoredSelectedTabId() {
-            return selectedTabId;
-        }
-
         @Override
         public void onTabRead(final SessionTab sessionTab) {
             if (sessionTab.isAboutHomeWithoutHistory()) {
                 // This is a tab pointing to about:home with no history. We won't restore
                 // this tab. If we end up restoring no tabs then the browser will decide
                 // whether it needs to open about:home or a different 'homepage'. If we'd
                 // always restore about:home only tabs then we'd never open the homepage.
                 // See bug 1261008.
@@ -287,19 +263,16 @@ public abstract class GeckoApp extends G
 
             int flags = Tabs.LOADURL_NEW_TAB;
             flags |= ((loadingExternalURL || !sessionTab.isSelected()) ? Tabs.LOADURL_DELAY_LOAD : 0);
             flags |= (tabObject.optBoolean("desktopMode") ? Tabs.LOADURL_DESKTOP : 0);
             flags |= (tabObject.optBoolean("isPrivate") ? Tabs.LOADURL_PRIVATE : 0);
 
             final Tab tab = Tabs.getInstance().loadUrl(sessionTab.getUrl(), flags);
 
-            if (sessionTab.isSelected() || selectNextTab) {
-                selectedTabId = tab.getId();
-            }
             if (selectNextTab) {
                 // We did not restore the selected tab previously. Now let's select this tab.
                 Tabs.getInstance().selectTab(tab.getId());
                 selectNextTab = false;
             }
 
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
@@ -413,40 +386,16 @@ public abstract class GeckoApp extends G
                     resetOptionsMenu();
                     resetFormAssistPopup();
                 }
                 break;
 
             case SELECTED:
                 resetOptionsMenu();
                 resetFormAssistPopup();
-
-                if (foregrounded) {
-                    tab.setWasSelectedInForeground(true);
-                }
-
-                if (mLastSelectedTabId != INVALID_TAB_ID && foregrounded &&
-                        // mSuppressActivitySwitch implies that we want to defer a pending
-                        // activity switch because we're actually about to leave the app.
-                        !mSuppressActivitySwitch && !tab.matchesActivity(this)) {
-                    startActivity(IntentHelper.getTabSwitchIntent(tab));
-                } else if (saveAsLastSelectedTab(tab)) {
-                    mLastSelectedTabId = tab.getId();
-                    mLastSessionUUID = GeckoApplication.getSessionUUID();
-                }
-                break;
-
-            case CLOSED:
-                if (saveAsLastSelectedTab(tab)) {
-                    if (mLastSelectedTabId == tab.getId() &&
-                            GeckoApplication.getSessionUUID().equals(mLastSessionUUID)) {
-                        mLastSelectedTabId = Tabs.INVALID_TAB_ID;
-                        mLastSessionUUID = null;
-                    }
-                }
                 break;
 
             case DESKTOP_MODE_CHANGE:
                 if (Tabs.getInstance().isSelectedTab(tab))
                     resetOptionsMenu();
                 break;
         }
     }
@@ -671,18 +620,16 @@ public abstract class GeckoApp extends G
     }
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
         outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground());
         outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession);
-        outState.putInt(LAST_SELECTED_TAB, mLastSelectedTabId);
-        outState.putString(LAST_SESSION_UUID, mLastSessionUUID);
     }
 
     public void addTab() { }
 
     public void addPrivateTab() { }
 
     public void showNormalTabs() { }
 
@@ -1252,21 +1199,16 @@ public abstract class GeckoApp extends G
             finish();
             return;
         }
 
         // The clock starts...now. Better hurry!
         mJavaUiStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_JAVAUI");
         mGeckoReadyStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_GECKOREADY");
 
-        if (savedInstanceState != null) {
-            mLastSelectedTabId = savedInstanceState.getInt(LAST_SELECTED_TAB);
-            mLastSessionUUID = savedInstanceState.getString(LAST_SESSION_UUID);
-        }
-
         final SafeIntent intent = new SafeIntent(getIntent());
 
         earlyStartJavaSampler(intent);
 
         // GeckoLoader wants to dig some environment variables out of the
         // incoming intent, so pass it in here. GeckoLoader will do its
         // business later and dispose of the reference.
         GeckoLoader.setLastIntent(intent);
@@ -1303,19 +1245,16 @@ public abstract class GeckoApp extends G
             // This happens when the GeckoApp activity is destroyed by Android
             // without killing the entire application (see Bug 769269).
             // Now that we've got multiple GeckoApp-based activities, this can
             // also happen if we're not the first activity to run within a session.
             mIsRestoringActivity = true;
             Telemetry.addToHistogram("FENNEC_RESTORING_ACTIVITY", 1);
 
         } else {
-            // We're going to restore the last session and/or open a startup/external tab.
-            mIgnoreLastSelectedTab = true;
-
             final String action = intent.getAction();
             final String args = GeckoApplication.addDefaultGeckoArgs(
                     intent.getStringExtra("args"));
 
             sAlreadyLoaded = true;
             GeckoThread.initMainProcess(/* profile */ null, args,
                                         /* debugging */ ACTION_DEBUG.equals(action));
 
@@ -1713,27 +1652,17 @@ public abstract class GeckoApp extends G
      */
     protected void loadStartupTab(final String url, final SafeIntent intent, final int flags) {
         // Invalid url
         if (url == null) {
             loadStartupTab(flags, intent.getAction());
             return;
         }
 
-        final Tab newTab = Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags);
-        if (ThreadUtils.isOnUiThread()) {
-            onTabOpenFromIntent(newTab);
-        } else {
-            ThreadUtils.postToUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    onTabOpenFromIntent(newTab);
-                }
-            });
-        }
+        Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags);
     }
 
     protected String getIntentURI(SafeIntent intent) {
         final String passedUri;
         final String uri = getURIFromIntent(intent);
 
         if (!TextUtils.isEmpty(uri)) {
             passedUri = uri;
@@ -1863,20 +1792,16 @@ public abstract class GeckoApp extends G
             }
 
             if (GeckoThread.isRunning()) {
                 geckoConnected();
             }
         }
     }
 
-    protected void onTabOpenFromIntent(Tab tab) { }
-
-    protected void onTabSelectFromIntent(Tab tab) { }
-
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
     @Override
     public void onGlobalLayout() {
         if (Versions.preJB) {
             mMainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
         } else {
             mMainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
         }
@@ -1940,21 +1865,16 @@ public abstract class GeckoApp extends G
                 // Update all parent tab IDs ...
                 parser.updateParentId(tabs);
                 windowObject.put("tabs", tabs);
                 // ... and for recently closed tabs as well (if we've got any).
                 final JSONArray closedTabs = windowObject.optJSONArray("closedTabs");
                 parser.updateParentId(closedTabs);
                 windowObject.putOpt("closedTabs", closedTabs);
 
-                if (isExternalURL) {
-                    // Pass on the tab we would have selected if we weren't going to open an
-                    // external URL later on.
-                    windowObject.put("selectedTabId", parser.getStoredSelectedTabId());
-                }
                 sessionString = new JSONObject().put(
                         "windows", new JSONArray().put(windowObject)).toString();
             } catch (final JSONException e) {
                 throw new SessionRestoreException(e);
             }
         } else {
             if (parser.allTabsSkipped() || sessionDataValid) {
                 // If we intentionally skipped all tabs we've read from the session file, we
@@ -1962,39 +1882,21 @@ public abstract class GeckoApp extends G
                 // can infer that the exception wasn't due to a damaged session store file.
                 // The same applies if the session file was syntactically valid and
                 // simply didn't contain any tabs.
                 mShouldRestore = false;
             }
             throw new SessionRestoreException("No tabs could be read from session file");
         }
 
-        if (saveSelectedStartupTab()) {
-            // This activity is something other than our normal tabbed browsing interface and is
-            // going to overwrite our tab selection. Therefore we should stash it away for later, so
-            // e.g. BrowserApp can display the correct tab if starting up later during this session.
-            SharedPreferences.Editor prefs = getSharedPreferencesForProfile().edit();
-            prefs.putInt(STARTUP_SELECTED_TAB, parser.getStoredSelectedTabId());
-            prefs.putString(STARTUP_SESSION_UUID, GeckoApplication.getSessionUUID());
-            prefs.apply();
-        }
-
         final GeckoBundle restoreData = new GeckoBundle(1);
         restoreData.putString("sessionString", sessionString);
         return restoreData;
     }
 
-    /**
-     * Activities that don't implement a normal tabbed browsing UI and overwrite the tab selection
-     * made by session restoring should probably override this and return true.
-     */
-    protected boolean saveSelectedStartupTab() {
-        return false;
-    }
-
     @RobocopTarget
     public @NonNull EventDispatcher getAppEventDispatcher() {
         if (mLayerView == null) {
             throw new IllegalStateException("Must not call getAppEventDispatcher() until after onCreate()");
         }
 
         return mLayerView.getEventDispatcher();
     }
@@ -2127,19 +2029,16 @@ public abstract class GeckoApp extends G
                     }
                 }
             });
             return;
         }
 
         final boolean isFirstTab = !mWasFirstTabShownAfterActivityUnhidden;
         mWasFirstTabShownAfterActivityUnhidden = true; // Reset since we'll be loading a tab.
-        if (!Intent.ACTION_MAIN.equals(action)) {
-            mIgnoreLastSelectedTab = true;
-        }
 
         // if we were previously OOM killed, we can end up here when launching
         // from external shortcuts, so set this as the intent for initialization
         if (!mInitialized) {
             setIntent(externalIntent);
             return;
         }
 
@@ -2200,18 +2099,17 @@ public abstract class GeckoApp extends G
         // We only care about comparing session UUIDs if one was specified in the intent.
         // Otherwise, we just try matching the tab ID with one of our open tabs.
         return tabToCheck != null && (!intent.hasExtra(INTENT_EXTRA_SESSION_UUID) ||
                 GeckoApplication.getSessionUUID().equals(intentSessionUUID));
     }
 
     protected void handleSelectTabIntent(SafeIntent intent) {
         final int tabId = intent.getIntExtra(INTENT_EXTRA_TAB_ID, INVALID_TAB_ID);
-        final Tab selectedTab = Tabs.getInstance().selectTab(tabId);
-        onTabSelectFromIntent(selectedTab);
+        Tabs.getInstance().selectTab(tabId);
     }
 
     /**
      * Handles getting a URI from an intent in a way that is backwards-
      * compatible with our previous implementations.
      */
     protected String getURIFromIntent(SafeIntent intent) {
         final String action = intent.getAction();
@@ -2256,43 +2154,16 @@ public abstract class GeckoApp extends G
         if (mIsAbortingAppLaunch) {
             return;
         }
 
         foregrounded = true;
 
         GeckoAppShell.setScreenOrientationDelegate(this);
 
-        // If mIgnoreLastSelectedTab is set, we're either the first activity to run, so our startup
-        // code will (have) handle(d) tab selection, or else we've received a new intent and want to
-        // open and select a new tab as well.
-        if (!mIgnoreLastSelectedTab) {
-            Tab selectedTab = Tabs.getInstance().getSelectedTab();
-
-            // We need to check if we've selected a different tab while no GeckoApp-based activity
-            // was in foreground and catch up with any activity switches that might be needed.
-            if (selectedTab != null && !selectedTab.getWasSelectedInForeground()) {
-                selectedTab.setWasSelectedInForeground(true);
-                if (!selectedTab.matchesActivity(this)) {
-                    startActivity(IntentHelper.getTabSwitchIntent(selectedTab));
-                }
-
-                // When backing out of the app closes the current tab and therefore selects
-                // another tab, we don't switch activities even if the newly selected tab has a
-                // different type, because doing so would bring us into the foreground again.
-                // As this means that the currently selected tab doesn't match the last active
-                // GeckoApp, we need to check mSuppressActivitySwitch here as well.
-            } else if (mLastActiveGeckoApp == null || mLastActiveGeckoApp.get() != this ||
-                    mSuppressActivitySwitch) {
-                restoreLastSelectedTab();
-            }
-        }
-        mSuppressActivitySwitch = false;
-        mIgnoreLastSelectedTab = false;
-
         int newOrientation = getResources().getConfiguration().orientation;
         if (GeckoScreenOrientation.getInstance().update(newOrientation)) {
             refreshChrome();
         }
 
         // We use two times: a pseudo-unique wall-clock time to identify the
         // current session across power cycles, and the elapsed realtime to
         // track the duration of the session.
@@ -2327,22 +2198,16 @@ public abstract class GeckoApp extends G
                     Log.w(LOGTAG, "Can't record session: rec is null.");
                 }
             }
         });
 
         Restrictions.update(this);
     }
 
-    /**
-     * Called on activity resume if a different (or no) GeckoApp-based activity was previously
-     * active within our application.
-     */
-    protected void restoreLastSelectedTab() { }
-
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
 
         if (!mWindowFocusInitialized && hasFocus) {
             mWindowFocusInitialized = true;
             // XXX our editor tests require the GeckoView to have focus to pass, so we have to
             // manually shift focus to the GeckoView. requestFocus apparently doesn't work at
@@ -2361,18 +2226,16 @@ public abstract class GeckoApp extends G
 
         if (mIsAbortingAppLaunch) {
             super.onPause();
             return;
         }
 
         foregrounded = false;
 
-        mLastActiveGeckoApp = new WeakReference<GeckoApp>(this);
-
         final HealthRecorder rec = mHealthRecorder;
         final Context context = this;
 
         // In some way it's sad that Android will trigger StrictMode warnings
         // here as the whole point is to save to disk while the activity is not
         // interacting with the user.
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
@@ -2690,23 +2553,22 @@ public abstract class GeckoApp extends G
                 if (tab.doBack()) {
                     return;
                 }
 
                 if (tab.isExternal()) {
                     onDone();
                     Tab nextSelectedTab = Tabs.getInstance().getNextTab(tab);
                     // Closing the tab will select the next tab. There's no need to unzombify it
-                    // if we're really exiting - switching activities is a different matter, though.
-                    if (nextSelectedTab != null && nextSelectedTab.getType() == tab.getType()) {
+                    // if we're exiting.
+                    if (nextSelectedTab != null) {
                         final GeckoBundle data = new GeckoBundle(1);
                         data.putInt("nextSelectedTabId", nextSelectedTab.getId());
                         EventDispatcher.getInstance().dispatch("Tab:KeepZombified", data);
                     }
-                    mSuppressActivitySwitch = true;
                     tabs.closeTab(tab);
                     return;
                 }
 
                 final int parentId = tab.getParentId();
                 final Tab parent = tabs.getTab(parentId);
                 if (parent != null) {
                     // The back button should always return to the parent (not a sibling).
--- a/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java
@@ -223,60 +223,25 @@ public final class IntentHelper implemen
         if (mimeType != null && mimeType.length() > 0) {
             shareIntent.setType(mimeType);
         }
 
         return shareIntent;
     }
 
     public static Intent getTabSwitchIntent(final Tab tab) {
-        final Intent intent;
-        switch (tab.getType()) {
-            case CUSTOMTAB:
-                if (tab.getCustomTabIntent() != null) {
-                    intent = tab.getCustomTabIntent().getUnsafe();
-                } else {
-                    intent = new Intent(Intent.ACTION_VIEW);
-                    intent.setData(Uri.parse(tab.getURL()));
-                }
-                break;
-            case WEBAPP:
-                intent = new Intent(GeckoApp.ACTION_WEBAPP);
-                final String manifestPath = tab.getManifestPath();
-                try {
-                    intent.setData(getStartUriFromManifest(manifestPath));
-                } catch (IOException | JSONException e) {
-                    Log.e(LOGTAG, "Failed to get start URI from manifest", e);
-                    intent.setData(Uri.parse(tab.getURL()));
-                }
-                intent.putExtra(WebAppActivity.MANIFEST_PATH, manifestPath);
-                break;
-            default:
-                intent = new Intent(GeckoApp.ACTION_SWITCH_TAB);
-                break;
-        }
-
-        intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, tab.getTargetClassNameForTab());
+        final Intent intent = new Intent(GeckoApp.ACTION_SWITCH_TAB);
+        intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.putExtra(BrowserContract.SKIP_TAB_QUEUE_FLAG, true);
         intent.putExtra(INTENT_EXTRA_TAB_ID, tab.getId());
         intent.putExtra(INTENT_EXTRA_SESSION_UUID, GeckoApplication.getSessionUUID());
         return intent;
     }
 
-    // TODO: When things have settled down a bit, we should split this and everything similar
-    // TODO: in the WebAppActivity into a dedicated WebAppManifest class (bug 1353868).
-    private static Uri getStartUriFromManifest(String manifestPath) throws IOException, JSONException {
-        File manifestFile = new File(manifestPath);
-        final JSONObject manifest = FileUtils.readJSONObjectFromFile(manifestFile);
-        final JSONObject manifestField = manifest.getJSONObject("manifest");
-
-        return Uri.parse(manifestField.getString("start_url"));
-    }
-
     /**
      * Given a URI, a MIME type, an Android intent "action", and a title,
      * produce an intent which can be used to start an activity to open
      * the specified URI.
      *
      * @param context a <code>Context</code> instance.
      * @param targetURI the string spec of the URI to open.
      * @param mimeType an optional MIME type string.
--- a/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/PrivateTab.java
@@ -6,18 +6,18 @@
 package org.mozilla.gecko;
 
 import android.content.Context;
 
 import org.json.JSONObject;
 import org.mozilla.gecko.db.BrowserDB;
 
 public class PrivateTab extends Tab {
-    public PrivateTab(Context context, int id, String url, boolean external, int parentId, String title, TabType type) {
-        super(context, id, url, external, parentId, title, type);
+    public PrivateTab(Context context, int id, String url, boolean external, int parentId, String title) {
+        super(context, id, url, external, parentId, title);
     }
 
     @Override
     protected void saveThumbnailToDB(final BrowserDB db) {}
 
     @Override
     public void setMetadata(JSONObject metadata) {}
 
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/SingleTabActivity.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.view.View;
-
-import org.mozilla.gecko.mozglue.SafeIntent;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import static org.mozilla.gecko.Tabs.INTENT_EXTRA_SESSION_UUID;
-import static org.mozilla.gecko.Tabs.INTENT_EXTRA_TAB_ID;
-import static org.mozilla.gecko.Tabs.INVALID_TAB_ID;
-
-public abstract class SingleTabActivity extends GeckoApp {
-
-    private static final long SHOW_CONTENT_DELAY = 300;
-
-    private View mainLayout;
-    private View contentView;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        final Intent externalIntent = getIntent();
-
-        decideTabAction(new SafeIntent(externalIntent), savedInstanceState);
-
-        super.onCreate(savedInstanceState);
-        // GeckoApp's default behaviour is to reset the intent if we've got any
-        // savedInstanceState, which we don't want here.
-        setIntent(externalIntent);
-
-        mainLayout = findViewById(R.id.main_layout);
-        contentView = findViewById(R.id.gecko_layout);
-        if ((mainLayout != null) && (contentView != null)) {
-            @ColorInt final int bg = ContextCompat.getColor(this, android.R.color.white);
-            mainLayout.setBackgroundColor(bg);
-            contentView.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    @Override
-    protected void onNewIntent(Intent externalIntent) {
-        final SafeIntent intent = new SafeIntent(externalIntent);
-
-        if (decideTabAction(intent, null)) {
-            // GeckoApp will handle tab selection.
-            super.onNewIntent(intent.getUnsafe());
-        } else {
-            loadTabFromIntent(intent);
-        }
-        // Again, unlike GeckoApp's default behaviour we want to keep the intent around
-        // because we might still require its data (e.g. to get custom tab customisations).
-        setIntent(intent.getUnsafe());
-    }
-
-    @Override
-    protected boolean saveSelectedStartupTab() {
-        // We ignore the tab selection made by session restoring in order to display our own tab,
-        // so we should save that tab's ID in case the user starts up our normal browsing UI later
-        // during the session.
-        return true;
-    }
-
-    @Override
-    protected void restoreLastSelectedTab() {
-        if (!mInitialized) {
-            // During startup from onCreate(), initialize() will handle selecting the startup tab.
-            // If this here is called afterwards, it's a no-op anyway. If for some reason
-            // (e.g. debugging) initialize() takes longer than usual and hasn't finished by the time
-            // onResume() runs and calls us, we just exit early so as not to interfere.
-            return;
-        }
-
-        final Tabs tabs = Tabs.getInstance();
-        final Tab tabToSelect = tabs.getTab(mLastSelectedTabId);
-
-        // If the tab we've stored is still existing and valid select it...
-        if (tabToSelect != null && GeckoApplication.getSessionUUID().equals(mLastSessionUUID) &&
-                tabToSelect.matchesActivity(this)) {
-            tabs.selectTab(mLastSelectedTabId);
-        } else {
-            // ... otherwise fall back to the intent data and open a new tab.
-            loadTabFromIntent(new SafeIntent(getIntent()));
-        }
-    }
-
-    private void loadTabFromIntent(final SafeIntent intent) {
-        final int flags = getNewTabFlags();
-        loadStartupTab(getIntentURI(intent), intent, flags);
-    }
-
-    /**
-     * @return True if we're going to select an existing tab, false if we want to load a new tab.
-     */
-    private boolean decideTabAction(@NonNull final SafeIntent intent,
-                                    @Nullable final Bundle savedInstanceState) {
-        final Tabs tabs = Tabs.getInstance();
-
-        if (hasGeckoTab(intent)) {
-            final Tab tabToSelect = tabs.getTab(intent.getIntExtra(INTENT_EXTRA_TAB_ID, INVALID_TAB_ID));
-            if (tabToSelect.matchesActivity(this)) {
-                // Nothing further to do here, GeckoApp will select the correct
-                // tab from the intent.
-                return true;
-            }
-        }
-        // The intent doesn't refer to a valid tab, so don't pass that data on.
-        intent.getUnsafe().removeExtra(INTENT_EXTRA_TAB_ID);
-        intent.getUnsafe().removeExtra(INTENT_EXTRA_SESSION_UUID);
-        // The tab data in the intent can become stale if we've been killed, or have
-        // closed the tab/changed its type since the original intent.
-        // We therefore attempt to fall back to the last selected tab. In onNewIntent,
-        // we can directly use the stored data, otherwise we'll look for it in the
-        // savedInstanceState.
-        final int lastSelectedTabId;
-        final String lastSessionUUID;
-
-        if (savedInstanceState != null) {
-            lastSelectedTabId = savedInstanceState.getInt(LAST_SELECTED_TAB);
-            lastSessionUUID = savedInstanceState.getString(LAST_SESSION_UUID);
-        } else {
-            lastSelectedTabId = mLastSelectedTabId;
-            lastSessionUUID = mLastSessionUUID;
-        }
-
-        final Tab tabToSelect = tabs.getTab(lastSelectedTabId);
-        if (tabToSelect != null && GeckoApplication.getSessionUUID().equals(lastSessionUUID) &&
-                tabToSelect.matchesActivity(this)) {
-            intent.getUnsafe().putExtra(INTENT_EXTRA_TAB_ID, lastSelectedTabId);
-            intent.getUnsafe().putExtra(INTENT_EXTRA_SESSION_UUID, lastSessionUUID);
-            return true;
-        }
-
-        // If we end up here, this means that there's no suitable tab we can take over.
-        // Instead, we'll just open a new tab from the data specified in the intent.
-        return false;
-    }
-
-    /**
-     * For us here, mLastSelectedTabId/Hash will hold the tab that will be selected when the
-     * activity is resumed/recreated, unless
-     * - it has been explicitly overridden through an intent
-     * - the tab cannot be found, in which case the URI passed as intent data will instead be
-     *   opened in a new tab.
-     * Therefore, we only update the stored tab data from those two locations.
-     */
-
-    /**
-     * Called when an intent or onResume() has caused us to load and select a new tab.
-     *
-     * @param tab The new tab that has been opened and selected.
-     */
-    @Override
-    protected void onTabOpenFromIntent(Tab tab) {
-        mLastSelectedTabId = tab.getId();
-        mLastSessionUUID = GeckoApplication.getSessionUUID();
-        showContentView();
-    }
-
-    /**
-     * Called when an intent has caused us to select an already existing tab.
-     *
-     * @param tab The already existing tab that has been selected for this activity.
-     */
-    @Override
-    protected void onTabSelectFromIntent(Tab tab) {
-        mLastSelectedTabId = tab.getId();
-        mLastSessionUUID = GeckoApplication.getSessionUUID();
-        showContentView();
-    }
-
-    // Bug 1369681 - a workaround to prevent flash in first launch.
-    // This will be removed once we have GeckoView based implementation.
-    private void showContentView() {
-        if ((contentView != null)
-                && (mainLayout != null)
-                && (contentView.getVisibility() == View.INVISIBLE)) {
-            ThreadUtils.postDelayedToUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    @ColorInt final int bg =
-                            ContextCompat.getColor(mainLayout.getContext(), android.R.color.white);
-                    mainLayout.setBackgroundColor(bg);
-                    contentView.setVisibility(View.VISIBLE);
-                }
-            }, SHOW_CONTENT_DELAY);
-        }
-    }
-}
--- a/mobile/android/base/java/org/mozilla/gecko/Tab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java
@@ -7,68 +7,61 @@ package org.mozilla.gecko;
 
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.concurrent.Future;
 import java.util.regex.Pattern;
 
 import org.json.JSONObject;
 import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.customtabs.CustomTabsActivity;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.URLMetadata;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.icons.IconCallback;
 import org.mozilla.gecko.icons.IconDescriptor;
 import org.mozilla.gecko.icons.IconRequestBuilder;
 import org.mozilla.gecko.icons.IconResponse;
 import org.mozilla.gecko.icons.Icons;
-import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.reader.ReaderModeUtils;
 import org.mozilla.gecko.reader.ReadingListHelper;
 import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.webapps.WebAppIndexer;
 import org.mozilla.gecko.widget.SiteLogins;
 
-import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 
 public class Tab {
     private static final String LOGTAG = "GeckoTab";
 
     private static Pattern sColorPattern;
     private final int mId;
-    private TabType mType;
     private final BrowserDB mDB;
     private long mLastUsed;
     private String mUrl;
     private String mBaseDomain;
     private String mUserRequested; // The original url requested. May be typed by the user or sent by an extneral app for example.
     private String mTitle;
     private Bitmap mFavicon;
     private String mFaviconUrl;
     private String mApplicationId; // Intended to be null after explicit user action.
 
     private IconRequestBuilder mIconRequestBuilder;
     private Future<IconResponse> mRunningIconRequest;
 
     private boolean mHasFeeds;
-    private SafeIntent mCustomTabIntent;
     private String mManifestUrl;
-    private String mManifestPath;
     private boolean mHasOpenSearch;
     private final SiteIdentity mSiteIdentity;
     private SiteLogins mSiteLogins;
     private BitmapDrawable mThumbnail;
     private volatile int mParentId;
     // Indicates the url was loaded from a source external to the app. This will be cleared
     // when the user explicitly loads a new url (e.g. clicking a link is not explicit).
     private final boolean mExternal;
@@ -84,17 +77,16 @@ public class Tab {
     private final Context mAppContext;
     private ErrorType mErrorType = ErrorType.NONE;
     private volatile int mLoadProgress;
     private volatile int mRecordingCount;
     private volatile boolean mIsAudioPlaying;
     private volatile boolean mIsMediaPlaying;
     private String mMostRecentHomePanel;
     private boolean mShouldShowToolbarWithoutAnimationOnFirstSelection;
-    private boolean mWasSelectedInForeground;
 
     /*
      * Bundle containing restore data for the panel referenced in mMostRecentHomePanel. This can be
      * e.g. the most recent folder for the bookmarks panel, or any other state that should be
      * persisted. This is then used e.g. when returning to homepanels via history.
      */
     private Bundle mMostRecentHomePanelData;
 
@@ -120,21 +112,20 @@ public class Tab {
 
     public enum ErrorType {
         CERT_ERROR,  // Pages with certificate problems
         BLOCKED,     // Pages blocked for phishing or malware warnings
         NET_ERROR,   // All other types of error
         NONE         // Non error pages
     }
 
-    public Tab(Context context, int id, String url, boolean external, int parentId, String title, TabType type) {
+    public Tab(Context context, int id, String url, boolean external, int parentId, String title) {
         mAppContext = context.getApplicationContext();
         mDB = BrowserDB.from(context);
         mId = id;
-        mType = type;
         mUrl = url;
         mBaseDomain = "";
         mUserRequested = "";
         mExternal = external;
         mParentId = parentId;
         mTitle = title == null ? "" : title;
         mSiteIdentity = new SiteIdentity();
         mContentType = "";
@@ -294,32 +285,20 @@ public class Tab {
     public synchronized String getFaviconURL() {
         return mFaviconUrl;
     }
 
     public boolean hasFeeds() {
         return mHasFeeds;
     }
 
-    public SafeIntent getCustomTabIntent() {
-        return mCustomTabIntent;
-    }
-
     public String getManifestUrl() {
         return mManifestUrl;
     }
 
-    /**
-     * @return If not empty, the path to a locally installed copy of the Progressive Web App
-     *         manifest file for this tab.
-     */
-    public String getManifestPath() {
-        return mManifestPath;
-    }
-
     public boolean hasOpenSearch() {
         return mHasOpenSearch;
     }
 
     public boolean hasLoadedFromCache() {
         return mLoadedFromCache;
     }
 
@@ -479,28 +458,20 @@ public class Tab {
         mFavicon = null;
         mFaviconUrl = null;
     }
 
     public void setHasFeeds(boolean hasFeeds) {
         mHasFeeds = hasFeeds;
     }
 
-    public void setCustomTabIntent(SafeIntent intent) {
-        mCustomTabIntent = intent;
-    }
-
     public void setManifestUrl(String manifestUrl) {
         mManifestUrl = manifestUrl;
     }
 
-    public void setManifestPath(String manifestPath) {
-        mManifestPath = manifestPath;
-    }
-
     public void setHasOpenSearch(boolean hasOpenSearch) {
         mHasOpenSearch = hasOpenSearch;
     }
 
     public void setLoadedFromCache(boolean loadedFromCache) {
         mLoadedFromCache = loadedFromCache;
     }
 
@@ -784,60 +755,16 @@ public class Tab {
     public boolean getDesktopMode() {
         return mDesktopMode;
     }
 
     public boolean isPrivate() {
         return false;
     }
 
-    public void setWasSelectedInForeground(boolean state) {
-        mWasSelectedInForeground = state;
-    }
-
-    public boolean getWasSelectedInForeground() {
-        return mWasSelectedInForeground;
-    }
-
-    public TabType getType() {
-        return mType;
-    }
-
-    public enum TabType {
-        BROWSING,
-        CUSTOMTAB,
-        WEBAPP
-    }
-
-    /**
-     * @return False if the tab is not matching the activity passed as argument.
-     */
-    public boolean matchesActivity(final Activity activity) {
-        final String activityName = activity.getClass().getName();
-        return activityName.equals(getTargetClassNameForTab());
-    }
-
-    /**
-     * @return The class name of the activity that should preferably be displaying this tab.
-     */
-    public String getTargetClassNameForTab() {
-        final TabType type = getType();
-
-        switch (type) {
-            case CUSTOMTAB:
-                return CustomTabsActivity.class.getName();
-            case WEBAPP:
-                final int index =  WebAppIndexer.getInstance().getIndexForManifest(
-                        getManifestPath(), mAppContext);
-                return WebAppIndexer.WEBAPP_CLASS + index;
-            default:
-                return AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS;
-        }
-    }
-
     /**
      * Sets the tab load progress to the given percentage.
      *
      * @param progressPercentage Percentage to set progress to (0-100)
      */
     void setLoadProgress(int progressPercentage) {
         mLoadProgress = progressPercentage;
     }
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -28,17 +28,16 @@ import org.mozilla.gecko.preferences.Gec
 import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
 import org.mozilla.gecko.reader.ReaderModeUtils;
 import org.mozilla.gecko.skin.SkinConfig;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.JavaUtil;
 import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.webapps.WebAppActivity;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteException;
@@ -47,17 +46,16 @@ import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.provider.Browser;
 import android.support.annotation.UiThread;
 import android.support.v4.content.ContextCompat;
 import android.text.TextUtils;
 import android.util.Log;
 
-import static org.mozilla.gecko.Tab.TabType;
 
 public class Tabs implements BundleEventListener {
     private static final String LOGTAG = "GeckoTabs";
 
     public static final String INTENT_EXTRA_TAB_ID = "TabId";
     public static final String INTENT_EXTRA_SESSION_UUID = "SessionUUID";
     private static final String PRIVATE_TAB_INTENT_EXTRA = "private_tab";
 
@@ -84,18 +82,16 @@ public class Tabs implements BundleEvent
     public static final int LOADURL_PINNED       = 1 << 3;
     public static final int LOADURL_DELAY_LOAD   = 1 << 4;
     public static final int LOADURL_DESKTOP      = 1 << 5;
     public static final int LOADURL_BACKGROUND   = 1 << 6;
     /** Indicates the url has been specified by a source external to the app. */
     public static final int LOADURL_EXTERNAL     = 1 << 7;
     /** Indicates the tab is the first shown after Firefox is hidden and restored. */
     public static final int LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN = 1 << 8;
-    public static final int LOADURL_CUSTOMTAB    = 1 << 9;
-    public static final int LOADURL_WEBAPP = 1 << 10;
 
     private static final long PERSIST_TABS_AFTER_MILLISECONDS = 1000 * 2;
 
     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);
@@ -193,33 +189,31 @@ public class Tabs implements BundleEvent
         }
     }
 
     public void detachFromContext() {
         mLayerView = null;
     }
 
     /**
-     * Gets the tab count corresponding to the category and private state of the
-     * selected tab.
+     * Gets the tab count corresponding to the 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.
      *
      * @return the number of tabs in the current private state
      */
     public synchronized int getDisplayCount() {
         // Once mSelectedTab is non-null, it cannot be null for the remainder
         // of the object's lifetime.
         boolean getPrivate = mSelectedTab != null && mSelectedTab.isPrivate();
-        TabType type = mSelectedTab != null ? mSelectedTab.getType() : TabType.BROWSING;
         int count = 0;
         for (Tab tab : mOrder) {
-            if (tab.isPrivate() == getPrivate && tab.getType() == type) {
+            if (tab.isPrivate() == getPrivate) {
                 count++;
             }
         }
         return count;
     }
 
     public int isOpen(String url) {
         for (Tab tab : mOrder) {
@@ -243,19 +237,19 @@ public class Tabs implements BundleEvent
             };
 
             // It's safe to use the db here since we aren't doing any I/O.
             final GeckoProfile profile = GeckoProfile.get(mAppContext);
             BrowserDB.from(profile).registerBookmarkObserver(getContentResolver(), mBookmarksContentObserver);
         }
     }
 
-    private Tab addTab(int id, String url, boolean external, int parentId, String title, boolean isPrivate, int tabIndex, TabType type) {
-        final Tab tab = isPrivate ? new PrivateTab(mAppContext, id, url, external, parentId, title, type) :
-                                    new Tab(mAppContext, id, url, external, parentId, title, type);
+    private Tab addTab(int id, String url, boolean external, int parentId, String title, boolean isPrivate, int tabIndex) {
+        final Tab tab = isPrivate ? new PrivateTab(mAppContext, id, url, external, parentId, title) :
+                                    new Tab(mAppContext, id, url, external, parentId, title);
         synchronized (this) {
             lazyRegisterBookmarkObserver();
             mTabs.put(id, tab);
 
             if (tabIndex > -1) {
                 mOrder.add(tabIndex, tab);
                 if (tabPositionCache.mOrderPosition >= tabIndex) {
                     tabPositionCache.mTabId = INVALID_TAB_ID;
@@ -263,37 +257,38 @@ public class Tabs implements BundleEvent
             } else {
                 mOrder.add(tab);
             }
         }
 
         // Suppress the ADDED event to prevent animation of tabs created via session restore.
         if (mInitialTabsAdded) {
             notifyListeners(tab, TabEvents.ADDED,
-                    Integer.toString(getPrivacySpecificTabIndex(tabIndex, isPrivate, type)));
+                    Integer.toString(getPrivacySpecificTabIndex(tabIndex, isPrivate)));
         }
 
         return tab;
     }
 
     /**
-     * Return the index, among those tabs of the chosen {@code type} whose privacy setting matches
-     * {@code isPrivate}, of the tab at position {@code index} in {@code mOrder}.  Returns
-     * {@code NEW_LAST_INDEX} when {@code index} is {@code NEW_LAST_INDEX} or no matches were
+     * Return the index, among those tabs whose privacy setting matches {@code isPrivate},
+     * of the tab at position {@code index} in {@code mOrder}.
+     *
+     * @return {@code NEW_LAST_INDEX} when {@code index} is {@code NEW_LAST_INDEX} or no matches were
      * found.
      */
-    private int getPrivacySpecificTabIndex(int index, boolean isPrivate, TabType type) {
+    private int getPrivacySpecificTabIndex(int index, boolean isPrivate) {
         if (index == NEW_LAST_INDEX) {
             return NEW_LAST_INDEX;
         }
 
         int privacySpecificIndex = -1;
         for (int i = 0; i <= index; i++) {
             final Tab tab = mOrder.get(i);
-            if (tab.isPrivate() == isPrivate && tab.getType() == type) {
+            if (tab.isPrivate() == isPrivate) {
                 privacySpecificIndex++;
             }
         }
         return privacySpecificIndex > -1 ? privacySpecificIndex : NEW_LAST_INDEX;
     }
 
     public synchronized void removeTab(int id) {
         if (mTabs.containsKey(id)) {
@@ -340,54 +335,37 @@ public class Tabs implements BundleEvent
         if (mOrder.isEmpty()) {
             return false;
         }
 
         selectTab(mOrder.get(mOrder.size() - 1).getId());
         return true;
     }
 
-    public synchronized boolean selectLastTab(Tab.TabType targetType) {
-        if (mOrder.isEmpty()) {
-            return false;
-        }
-
-        Tab tabToSelect = mOrder.get(mOrder.size() - 1);
-        if (tabToSelect.getType() != targetType) {
-            tabToSelect = getPreviousTabFrom(tabToSelect, false, targetType);
-            if (tabToSelect == null) {
-                return false;
-            }
-        }
-
-        selectTab(tabToSelect.getId());
-        return true;
-    }
-
     private int getIndexOf(Tab tab) {
         return mOrder.lastIndexOf(tab);
     }
 
-    private Tab getNextTabFrom(Tab tab, boolean getPrivate, TabType type) {
+    private Tab getNextTabFrom(Tab tab, boolean getPrivate) {
         int numTabs = mOrder.size();
         int index = getIndexOf(tab);
         for (int i = index + 1; i < numTabs; i++) {
             Tab next = mOrder.get(i);
-            if (next.isPrivate() == getPrivate && next.getType() == type) {
+            if (next.isPrivate() == getPrivate) {
                 return next;
             }
         }
         return null;
     }
 
-    private Tab getPreviousTabFrom(Tab tab, boolean getPrivate, TabType type) {
+    private Tab getPreviousTabFrom(Tab tab, boolean getPrivate) {
         int index = getIndexOf(tab);
         for (int i = index - 1; i >= 0; i--) {
             Tab prev = mOrder.get(i);
-            if (prev.isPrivate() == getPrivate && prev.getType() == type) {
+            if (prev.isPrivate() == getPrivate) {
                 return prev;
             }
         }
         return null;
     }
 
     /**
      * Gets the selected tab.
@@ -478,61 +456,40 @@ public class Tabs implements BundleEvent
 
     /** 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;
 
         boolean getPrivate = tab.isPrivate();
-        TabType type = tab.getType();
-        Tab nextTab = getNextTabFrom(tab, getPrivate, type);
+        Tab nextTab = getNextTabFrom(tab, getPrivate);
         if (nextTab == null)
-            nextTab = getPreviousTabFrom(tab, getPrivate, type);
+            nextTab = getPreviousTabFrom(tab, getPrivate);
         if (nextTab == null && getPrivate) {
             // If there are no private tabs remaining, get the last normal tab.
-            nextTab = getFallbackNextTab(type);
-        }
-        if (nextTab == null && type != TabType.BROWSING) {
-            // If there are no non-private tabs of the same type remaining,
-            // fall back to TabType.BROWSING.
-            nextTab = getFallbackNextTab(TabType.BROWSING);
+            Tab lastTab = mOrder.get(mOrder.size() - 1);
+            if (!lastTab.isPrivate()) {
+                nextTab = lastTab;
+            } else {
+                nextTab = getPreviousTabFrom(lastTab, false);
+            }
         }
 
         Tab parent = getTab(tab.getParentId());
-        if (parent != null && parent.getType() == type) {
+        if (parent != null) {
             // If the next tab is a sibling, switch to it. Otherwise go back to the parent.
             if (nextTab != null && nextTab.getParentId() == tab.getParentId())
                 return nextTab;
             else
                 return parent;
         }
         return nextTab;
     }
 
-    /**
-     * Normally, {@link #getNextTab(Tab)} will attempt to find a tab of the same privacy mode and
-     * {@link TabType} as the currently selected tab. If no such tab exists, we will first fall back
-     * to non-private tabs if the current tab is a private tab. If we can't find any non-private
-     * tabs of the same type, we then start looking for any non-private {@link TabType#BROWSING} tabs.
-     *
-     * @param type The {@link TabType} of tab to be searched.
-     * @return A non-private tab of the type specified or null if none could be found.
-     */
-    private Tab getFallbackNextTab(TabType type) {
-        Tab nextTab;
-        Tab lastTab = mOrder.get(mOrder.size() - 1);
-        if (!lastTab.isPrivate() && lastTab.getType() == type) {
-            nextTab = lastTab;
-        } else {
-            nextTab = getPreviousTabFrom(lastTab, false, type);
-        }
-        return nextTab;
-    }
-
     public Iterable<Tab> getTabsInOrder() {
         return mOrder;
     }
 
     /**
      * @return the current GeckoApp instance, or throws if
      *         we aren't correctly initialized.
      */
@@ -595,18 +552,17 @@ public class Tabs implements BundleEvent
                     // Tab was already closed; abort
                     return;
                 }
             } else {
                 tab = addTab(id, url, message.getBoolean("external"),
                                       message.getInt("parentId"),
                                       message.getString("title"),
                                       message.getBoolean("isPrivate"),
-                                      message.getInt("tabIndex"),
-                                      TabType.valueOf(message.getString("tabType")));
+                                      message.getInt("tabIndex"));
                 // If we added the tab as a stub, we should have already
                 // selected it, so ignore this flag for stubbed tabs.
                 if (message.getBoolean("selected"))
                     selectTab(id);
             }
 
             if (message.getBoolean("delayLoad"))
                 tab.setState(Tab.STATE_DELAYED);
@@ -863,39 +819,38 @@ public class Tabs implements BundleEvent
     /**
      * Looks for an open tab with the given URL.
      * @param url       the URL of the tab we're looking for
      *
      * @return first Tab with the given URL, or null if there is no such tab.
      */
     @RobocopTarget
     public Tab getFirstTabForUrl(String url) {
-        return getFirstTabForUrlHelper(url, null, TabType.BROWSING);
+        return getFirstTabForUrlHelper(url, null);
     }
 
     /**
      * Looks for an open tab with the given URL and private state.
      * @param url       the URL of the tab we're looking for
      * @param isPrivate if true, only look for tabs that are private. if false,
      *                  only look for tabs that are non-private.
-     * @param type      the type of the tab we're looking for
      *
      * @return first Tab with the given URL, or null if there is no such tab.
      */
-    public Tab getFirstTabForUrl(String url, boolean isPrivate, TabType type) {
-        return getFirstTabForUrlHelper(url, isPrivate, type);
+    public Tab getFirstTabForUrl(String url, boolean isPrivate) {
+        return getFirstTabForUrlHelper(url, isPrivate);
     }
 
-    private Tab getFirstTabForUrlHelper(String url, Boolean isPrivate, TabType type) {
+    private Tab getFirstTabForUrlHelper(String url, Boolean isPrivate) {
         if (url == null) {
             return null;
         }
 
         for (Tab tab : mOrder) {
-            if (isPrivate != null && isPrivate != tab.isPrivate() || type != tab.getType()) {
+            if (isPrivate != null && isPrivate != tab.isPrivate()) {
                 continue;
             }
             if (url.equals(tab.getURL())) {
                 return tab;
             }
         }
 
         return null;
@@ -906,31 +861,29 @@ public class Tabs implements BundleEvent
      * state.
      *
      * @param url
      *            The URL of the tab we're looking for. The url parameter can be
      *            the actual article URL or the reader mode article URL.
      * @param isPrivate
      *            If true, only look for tabs that are private. If false, only
      *            look for tabs that are not private.
-     * @param type
-     *            The type of the tab we're looking for.
      *
      * @return The first Tab with the given URL, or null if there is no such
      *         tab.
      */
-    public Tab getFirstReaderTabForUrl(String url, boolean isPrivate, TabType type) {
+    public Tab getFirstReaderTabForUrl(String url, boolean isPrivate) {
         if (url == null) {
             return null;
         }
 
         url = ReaderModeUtils.stripAboutReaderUrl(url);
 
         for (Tab tab : mOrder) {
-            if (isPrivate != tab.isPrivate() || type != tab.getType()) {
+            if (isPrivate != tab.isPrivate()) {
                 continue;
             }
             String tabUrl = tab.getURL();
             if (AboutPages.isAboutReader(tabUrl)) {
                 tabUrl = ReaderModeUtils.stripAboutReaderUrl(tabUrl);
                 if (url.equals(tabUrl)) {
                     return tab;
                 }
@@ -1001,29 +954,24 @@ public class Tabs implements BundleEvent
         // delayLoad implies background tab
         boolean background = delayLoad || (flags & LOADURL_BACKGROUND) != 0;
 
         boolean isPrivate = (flags & LOADURL_PRIVATE) != 0 || (intent != null && intent.getBooleanExtra(PRIVATE_TAB_INTENT_EXTRA, false));
         boolean userEntered = (flags & LOADURL_USER_ENTERED) != 0;
         boolean desktopMode = (flags & LOADURL_DESKTOP) != 0;
         boolean external = (flags & LOADURL_EXTERNAL) != 0;
         final boolean isFirstShownAfterActivityUnhidden = (flags & LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN) != 0;
-        final boolean customTab = (flags & LOADURL_CUSTOMTAB) != 0;
-        final boolean webappTab = (flags & LOADURL_WEBAPP) != 0;
-        final TabType type = customTab ? TabType.CUSTOMTAB :
-            webappTab ? TabType.WEBAPP : TabType.BROWSING;
 
         data.putString("url", url);
         data.putString("engine", searchEngine);
         data.putInt("parentId", parentId);
         data.putBoolean("userEntered", userEntered);
         data.putBoolean("isPrivate", isPrivate);
         data.putBoolean("pinned", (flags & LOADURL_PINNED) != 0);
         data.putBoolean("desktopMode", desktopMode);
-        data.putString("tabType", type.name());
 
         final boolean needsNewTab;
         final String applicationId = (intent == null) ? null :
                 intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
         if (applicationId == null) {
             needsNewTab = (flags & LOADURL_NEW_TAB) != 0;
         } else {
             // If you modify this code, be careful that intent != null.
@@ -1062,29 +1010,20 @@ public class Tabs implements BundleEvent
             // The URL is updated for the tab once Gecko responds with the
             // Tab:Added data. We can preliminarily set the tab's URL as
             // long as it's a valid URI.
             String tabUrl = (url != null && Uri.parse(url).getScheme() != null) ? url : null;
 
             // Add the new tab to the end of the tab order.
             final int tabIndex = NEW_LAST_INDEX;
 
-            tabToSelect = addTab(tabId, tabUrl, external, parentId, url, isPrivate, tabIndex, type);
+            tabToSelect = addTab(tabId, tabUrl, external, parentId, url, isPrivate, tabIndex);
             tabToSelect.setDesktopMode(desktopMode);
             tabToSelect.setApplicationId(applicationId);
-            if (intent != null) {
-                if (customTab) {
-                    // The intent can contain all sorts of customisations, so we save it in case
-                    // we need to launch a new custom tab activity for this tab.
-                    tabToSelect.setCustomTabIntent(intent);
-                }
-                if (intent.hasExtra(WebAppActivity.MANIFEST_PATH)) {
-                    tabToSelect.setManifestPath(intent.getStringExtra(WebAppActivity.MANIFEST_PATH));
-                }
-            }
+
             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);
--- a/mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/TwoLinePageRow.java
@@ -1,32 +1,30 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
-import java.lang.ref.WeakReference;
 import java.util.concurrent.Future;
 
 import android.content.Context;
 import android.database.Cursor;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.widget.TextViewCompat;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.URLColumns;
 import org.mozilla.gecko.distribution.PartnerBookmarksProviderProxy;
 import org.mozilla.gecko.icons.IconDescriptor;
@@ -200,23 +198,22 @@ public class TwoLinePageRow extends Line
     /**
      * Replaces the page URL with "Switch to tab" if there is already a tab open with that URL.
      * Only looks for tabs that are either private or non-private, depending on the current
      * selected tab.
      */
     protected void updateDisplayedUrl() {
         final Tab selectedTab = Tabs.getInstance().getSelectedTab();
         final boolean isPrivate = (selectedTab != null) && (selectedTab.isPrivate());
-        final Tab.TabType type = selectedTab != null ? selectedTab.getType() : Tab.TabType.BROWSING;
 
         // We always want to display the underlying page url, however for readermode pages
         // we navigate to the about:reader equivalent, hence we need to use that url when finding
         // existing tabs
         final String navigationUrl = mHasReaderCacheItem ? ReaderModeUtils.getAboutReaderForUrl(mPageUrl) : mPageUrl;
-        Tab tab = Tabs.getInstance().getFirstTabForUrl(navigationUrl, isPrivate, type);
+        Tab tab = Tabs.getInstance().getFirstTabForUrl(navigationUrl, isPrivate);
 
 
         if (!mShowIcons || tab == null) {
             setUrl(mPageUrl);
             setSwitchToTabIcon(NO_ICON);
         } else {
             setUrl(R.string.switch_to_tab);
             setSwitchToTabIcon(R.drawable.ic_url_bar_tab);
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabStrip.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabStrip.java
@@ -19,43 +19,38 @@ import android.view.ViewTreeObserver;
 
 import org.mozilla.gecko.BrowserApp.TabStripInterface;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.widget.themed.ThemedImageButton;
 import org.mozilla.gecko.widget.themed.ThemedLinearLayout;
 
-import static org.mozilla.gecko.Tab.TabType;
-
 public class TabStrip extends ThemedLinearLayout
                       implements TabStripInterface {
     private static final String LOGTAG = "GeckoTabStrip";
 
     private final TabStripView tabStripView;
     private final ThemedImageButton addTabButton;
 
     private final TabsListener tabsListener;
     private OnTabAddedOrRemovedListener tabChangedListener;
 
     // True when the tab strip isn't visible to the user due to something being drawn over it.
     private boolean tabStripIsCovered;
     private boolean tabsNeedUpdating;
-    private final TabType type;
 
     public TabStrip(Context context) {
         this(context, null);
     }
 
     public TabStrip(Context context, AttributeSet attrs) {
         super(context, attrs);
         setOrientation(HORIZONTAL);
 
-        type = TabType.BROWSING;
-
         LayoutInflater.from(context).inflate(R.layout.tab_strip_inner, this);
         tabStripView = (TabStripView) findViewById(R.id.tab_strip);
 
         addTabButton = (ThemedImageButton) findViewById(R.id.tablet_add_tab);
         addTabButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 final Tabs tabs = Tabs.getInstance();
@@ -113,20 +108,16 @@ public class TabStrip extends ThemedLine
 
     public void setOnTabChangedListener(OnTabAddedOrRemovedListener listener) {
         tabChangedListener = listener;
     }
 
     private class TabsListener implements Tabs.OnTabsChangedListener {
         @Override
         public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
-            if (msg != Tabs.TabEvents.RESTORED && tab.getType() != type) {
-                return;
-            }
-
             switch (msg) {
                 case RESTORED:
                     tabStripView.restoreTabs();
                     break;
 
                 case ADDED:
                     final int tabIndex = Integer.parseInt(data);
                     tabStripView.addTab(tab, tabIndex);
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripView.java
@@ -26,37 +26,32 @@ import android.support.v7.widget.helper.
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.animation.DecelerateInterpolator;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static org.mozilla.gecko.Tab.TabType;
-
 public class TabStripView extends RecyclerView
                           implements TabsTouchHelperCallback.DragListener {
     private static final int ANIM_TIME_MS = 200;
     private static final DecelerateInterpolator ANIM_INTERPOLATOR = new DecelerateInterpolator();
 
     private final TabStripAdapter adapter;
-    private final TabType type;
     private boolean isPrivate;
 
     private final TabAnimatorListener animatorListener;
 
     private final Paint fadingEdgePaint;
     private final int fadingEdgeSize;
 
     public TabStripView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        type = TabType.BROWSING;
-
         fadingEdgePaint = new Paint();
         final Resources resources = getResources();
         fadingEdgeSize =
                 resources.getDimensionPixelOffset(R.dimen.tablet_tab_strip_fading_edge_size);
 
         animatorListener = new TabAnimatorListener();
 
         setChildrenDrawingOrderEnabled(true);
@@ -80,17 +75,17 @@ public class TabStripView extends Recycl
     }
 
     /* package */ void refreshTabs() {
         // Store a different copy of the tabs, so that we don't have
         // to worry about accidentally updating it on the wrong thread.
         final List<Tab> tabs = new ArrayList<>();
 
         for (final Tab tab : Tabs.getInstance().getTabsInOrder()) {
-            if (tab.isPrivate() == isPrivate && tab.getType() == type) {
+            if (tab.isPrivate() == isPrivate) {
                 tabs.add(tab);
             }
         }
 
         adapter.refresh(tabs);
         updateSelectedPosition();
     }
 
@@ -99,17 +94,17 @@ public class TabStripView extends Recycl
     }
 
     /* package */ void restoreTabs() {
         refreshTabs();
         animateRestoredTabs();
     }
 
     /* package */ void addTab(Tab tab, int position) {
-        if (tab.isPrivate() != isPrivate || tab.getType() != type) {
+        if (tab.isPrivate() != isPrivate) {
             return;
         }
 
         adapter.addTab(tab, position);
     }
 
     /* package */ void removeTab(Tab tab) {
         adapter.removeTab(tab);
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
@@ -15,38 +15,34 @@ import android.content.res.TypedArray;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.Button;
 
 import java.util.ArrayList;
 
-import static org.mozilla.gecko.Tab.TabType;
-
 public abstract class TabsLayout extends RecyclerView
         implements TabsPanel.TabsLayout,
         Tabs.OnTabsChangedListener,
         RecyclerViewClickSupport.OnItemClickListener,
         TabsTouchHelperCallback.DismissListener,
         TabsTouchHelperCallback.DragListener {
 
     private static final String LOGTAG = "Gecko" + TabsLayout.class.getSimpleName();
 
     private final boolean isPrivate;
-    private final TabType type;
     private TabsPanel tabsPanel;
     private final TabsLayoutAdapter tabsAdapter;
 
     public TabsLayout(Context context, AttributeSet attrs, int itemViewLayoutResId) {
         super(context, attrs);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
         isPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
-        type = TabType.BROWSING;
         a.recycle();
 
         tabsAdapter = new TabsLayoutAdapter(context, itemViewLayoutResId, isPrivate,
                 /* close on click listener */
                 new Button.OnClickListener() {
                     @Override
                     public void onClick(View v) {
                         // The view here is the close button, which has a reference
@@ -95,20 +91,16 @@ public abstract class TabsLayout extends
     }
 
     protected void autoHidePanel() {
         tabsPanel.autoHidePanel();
     }
 
     @Override
     public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
-        if (msg != Tabs.TabEvents.RESTORED && tab.getType() != type) {
-            return;
-        }
-
         switch (msg) {
             case ADDED:
                 int tabIndex = Integer.parseInt(data);
                 tabIndex = tabIndex == Tabs.NEW_LAST_INDEX ? tabsAdapter.getItemCount() : tabIndex;
                 tabsAdapter.notifyTabInserted(tab, tabIndex);
                 if (addAtIndexRequiresScroll(tabIndex)) {
                     // (The SELECTED tab is updated *after* this call to ADDED, so don't just call
                     // scrollSelectedTabToTopOfTray().)
@@ -184,17 +176,17 @@ public abstract class TabsLayout extends
 
     private void refreshTabsData() {
         // Store a different copy of the tabs, so that we don't have to worry about
         // accidentally updating it on the wrong thread.
         final ArrayList<Tab> tabData = new ArrayList<>();
         final Iterable<Tab> allTabs = Tabs.getInstance().getTabsInOrder();
 
         for (final Tab tab : allTabs) {
-            if (tab.isPrivate() == isPrivate && tab.getType() == type) {
+            if (tab.isPrivate() == isPrivate) {
                 tabData.add(tab);
             }
         }
 
         tabsAdapter.setTabs(tabData);
         scrollSelectedTabToTopOfTray();
     }
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -852,17 +852,16 @@ gbjar.sources += ['java/org/mozilla/geck
     'restrictions/RestrictionConfiguration.java',
     'restrictions/RestrictionProvider.java',
     'restrictions/Restrictions.java',
     'ScreenshotObserver.java',
     'search/SearchEngine.java',
     'search/SearchEngineManager.java',
     'SessionParser.java',
     'SharedPreferencesHelper.java',
-    'SingleTabActivity.java',
     'SiteIdentity.java',
     'SnackbarBuilder.java',
     'SuggestClient.java',
     'switchboard/AsyncConfigLoader.java',
     'switchboard/DeviceUuidFactory.java',
     'switchboard/Preferences.java',
     'switchboard/Switch.java',
     'switchboard/SwitchBoard.java',
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1877,18 +1877,17 @@ var BrowserApp = {
         let params = {
           selected: ("selected" in data) ? data.selected : !delayLoad,
           parentId: ("parentId" in data) ? data.parentId : -1,
           flags: flags,
           tabID: data.tabID,
           isPrivate: (data.isPrivate === true),
           pinned: (data.pinned === true),
           delayLoad: (delayLoad === true),
-          desktopMode: (data.desktopMode === true),
-          tabType: ("tabType" in data) ? data.tabType : "BROWSING"
+          desktopMode: (data.desktopMode === true)
         };
 
         params.userRequested = url;
 
         if (data.engine) {
           let engine = Services.search.getEngineByName(data.engine);
           if (engine) {
             let submission = engine.getSubmission(url);
@@ -3396,23 +3395,16 @@ nsBrowserAccess.prototype = {
 
     // If OPEN_SWITCHTAB was not handled above, we need to open a new tab,
     // along with other OPEN_ values that create a new tab.
     let newTab = (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW ||
                   aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB ||
                   aWhere == Ci.nsIBrowserDOMWindow.OPEN_SWITCHTAB);
     let isPrivate = false;
 
-    if (aOpener != null) {
-      let parent = BrowserApp.getTabForWindow(aOpener.top);
-      if (parent != null) {
-        newTab = newTab && parent.tabType != "CUSTOMTAB";
-      }
-    }
-
     if (newTab) {
       let parentId = -1;
       if (!isExternal && aOpener) {
         let parent = BrowserApp.getTabForWindow(aOpener.top);
         if (parent) {
           parentId = parent.id;
           isPrivate = PrivateBrowsingUtils.isBrowserPrivate(parent.browser);
         }
@@ -3582,19 +3574,16 @@ Tab.prototype = {
 
     // When the tab is stubbed from Java, there's a window between the stub
     // creation and the tab creation in Gecko where the stub could be removed
     // or the selected tab can change (which is easiest to hit during startup).
     // To prevent these races, we need to differentiate between tab stubs from
     // Java and new tabs from Gecko.
     let stub = false;
 
-    // The authoritative list of possible tab types is the TabType enum in Tab.java.
-    this.type = "tabType" in aParams ? aParams.tabType : "BROWSING";
-
     if (!aParams.zombifying) {
       if ("tabID" in aParams) {
         this.id = aParams.tabID;
         stub = true;
       } else {
         let jenv = JNI.GetForThread();
         let jTabs = JNI.LoadClass(jenv, "org.mozilla.gecko.Tabs", {
           static_methods: [
@@ -3607,17 +3596,16 @@ Tab.prototype = {
 
       this.desktopMode = ("desktopMode" in aParams) ? aParams.desktopMode : false;
       this._parentId = ("parentId" in aParams && typeof aParams.parentId == "number")
                       ? aParams.parentId : -1;
 
       let message = {
         type: "Tab:Added",
         tabID: this.id,
-        tabType: this.type,
         uri: truncate(uri, MAX_URI_LENGTH),
         parentId: this.parentId,
         tabIndex: ("tabIndex" in aParams) ? aParams.tabIndex : -1,
         external: ("external" in aParams) ? aParams.external : false,
         selected: ("selected" in aParams || aParams.cancelEditMode === true)
                   ? aParams.selected !== false || aParams.cancelEditMode === true : true,
         cancelEditMode: aParams.cancelEditMode === true,
         title: truncate(title, MAX_TITLE_LENGTH),
@@ -3670,18 +3658,17 @@ Tab.prototype = {
       entries: [{
         url: aURL,
         title: truncate(title, MAX_TITLE_LENGTH)
       }],
       index: 1,
       desktopMode: this.desktopMode,
       isPrivate: isPrivate,
       tabId: this.id,
-      parentId: this.parentId,
-      type: this.type
+      parentId: this.parentId
     };
 
     if (aParams.delayLoad) {
       // If this is a zombie tab, mark the browser for delay loading, which will
       // restore the tab when selected using the session data added above
       this.browser.__SS_restore = true;
       this.browser.setAttribute("pending", "true");
     } else {
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -679,26 +679,17 @@ SessionStore.prototype = {
       this.saveStateDelayed();
     }
   },
 
   onTabClose: function ss_onTabClose(aWindow, aBrowser, aTabIndex) {
     let data = aBrowser.__SS_data;
     let tab = aWindow.BrowserApp.getTabForId(data.tabId);
 
-    let windowData = this._windows[aWindow.__SSID];
-    if (windowData.selectedTabId == tab.id) {
-      // Normally, we will first select another tab anyway before closing the previous tab, which
-      // would make this logic moot. However we only update the selected tab when selecting a normal
-      // BROWSING-type tab, so we include this just to be on the safe side - although normally there
-      // should always be at least one BROWSING-type tab open.
-      windowData.selectedTabId = INVALID_TAB_ID;
-    }
-
-    if (this._maxTabsUndo == 0 || this._sessionDataIsEmpty(data) || tab.type != "BROWSING") {
+    if (this._maxTabsUndo == 0 || this._sessionDataIsEmpty(data)) {
       this._lastClosedTabIndex = INVALID_TAB_INDEX;
       return;
     }
 
     if (aWindow.BrowserApp.tabs.length > 0) {
       // Bundle this browser's data and extra data and save in the closedTabs
       // window property
       data.extData = aBrowser.__SS_extdata || {};
@@ -806,19 +797,17 @@ SessionStore.prototype = {
   onTabSelect: function ss_onTabSelect(aWindow, aBrowser) {
     if (this._loadState != STATE_RUNNING) {
       return;
     }
 
     let tab = aWindow.BrowserApp.getTabForBrowser(aBrowser);
     let tabId = tab.id;
 
-    if (tab.type == "BROWSING") {
-      this._windows[aWindow.__SSID].selectedTabId = tabId;
-    }
+    this._windows[aWindow.__SSID].selectedTabId = tabId;
 
     // Restore the resurrected browser
     if (tabId != this._keepAsZombieTabId) {
       this.restoreZombieTab(tab);
     } else {
       log("keeping as zombie tab " + tabId);
     }
     // The tab id passed through Tab:KeepZombified is valid for one TabSelect only.
@@ -1073,20 +1062,16 @@ SessionStore.prototype = {
       normalData.windows.push(normalWin);
       privateData.windows.push({ tabs: [] });
 
       // Split the session data into private and non-private data objects.
       // Non-private session data will be saved to disk, and private session
       // data will be sent to Java for Android to hold it in memory.
       for (let i = 0; i < win.tabs.length; ++i) {
         let tab = win.tabs[i];
-        if (tab.type != "BROWSING") {
-          continue;
-        }
-
         let savedWin = tab.isPrivate ? privateData.windows[winIndex] : normalData.windows[winIndex];
         savedWin.tabs.push(tab);
         if (win.selectedTabId === tab.tabId) {
           savedWin.selected = savedWin.tabs.length; // 1-based index
         }
       }
     }
 
@@ -1135,33 +1120,32 @@ SessionStore.prototype = {
     let tab = aWindow.BrowserApp.getTabForBrowser(aBrowser);
     tabData.entries = aHistory.entries;
     tabData.index = aHistory.index;
     tabData.attributes = { image: aBrowser.mIconURL };
     tabData.desktopMode = tab.desktopMode;
     tabData.isPrivate = aBrowser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing;
     tabData.tabId = tab.id;
     tabData.parentId = tab.parentId;
-    tabData.type = tab.type;
 
     aBrowser.__SS_data = tabData;
   },
 
   _collectWindowData: function ss__collectWindowData(aWindow) {
     // Ignore windows not tracked by SessionStore
     if (!aWindow.__SSID || !this._windows[aWindow.__SSID]) {
       return;
     }
 
     let winData = this._windows[aWindow.__SSID];
     winData.tabs = [];
 
     let selectedTab = aWindow.BrowserApp.selectedTab;
 
-    if (selectedTab != null && selectedTab.type == "BROWSING") {
+    if (selectedTab != null) {
       winData.selectedTabId = selectedTab.id;
     }
 
     let tabs = aWindow.BrowserApp.tabs;
     for (let i = 0; i < tabs.length; i++) {
       let browser = tabs[i].browser;
       if (browser.__SS_data) {
         let tabData = browser.__SS_data;
@@ -1457,25 +1441,16 @@ SessionStore.prototype = {
         this._windows[window.__SSID].selectedTabId = tab.id;
       } else {
         // Mark the browser for delay loading
         tab.browser.__SS_restore = true;
         tab.browser.setAttribute("pending", "true");
       }
     }
 
-    if (state.windows[0].hasOwnProperty("selectedTabId") &&
-      this._windows[window.__SSID].selectedTabId == INVALID_TAB_ID) {
-      // If none of the restored tabs was the selected tab, we might be opening an URL from an
-      // external intent. If this new tab is a normal BROWSING tab, we'll catch its selection
-      // anyway, however if we've opened a custom tab/web app or anything like that we want to
-      // ignore it. So instead, we store the tab we would have selected from the session file.
-      this._windows[window.__SSID].selectedTabId = state.windows[0].selectedTabId;
-    }
-
     // Restore the closed tabs array on the current window.
     if (state.windows[0].closedTabs && this._maxTabsUndo > 0) {
       this._windows[window.__SSID].closedTabs = state.windows[0].closedTabs;
       log("_restoreWindow() loaded " + state.windows[0].closedTabs.length + " closed tabs");
     }
   },
 
   getClosedTabCount: function ss_getClosedTabCount(aWindow) {
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testFilterOpenTab.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testFilterOpenTab.java
@@ -12,18 +12,16 @@ import org.mozilla.gecko.PrivateTab;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.TabsProvider;
 
 import android.content.ContentProvider;
 import android.content.Context;
 import android.database.Cursor;
 
-import static org.mozilla.gecko.Tab.TabType;
-
 /**
  * Tests that local tabs are filtered prior to upload.
  * - create a set of tabs and persists them through TabsAccessor.
  * - verifies that tabs are filtered by querying.
  */
 public class testFilterOpenTab extends ContentProviderTest {
     private static final String[] TABS_PROJECTION_COLUMNS = new String[] {
                                                                 BrowserContract.Tabs.TITLE,
@@ -51,21 +49,21 @@ public class testFilterOpenTab extends C
         return mProvider.query(BrowserContract.Tabs.CONTENT_URI,
                                TABS_PROJECTION_COLUMNS,
                                LOCAL_TABS_SELECTION,
                                null,
                                null);
     }
 
     private Tab createTab(int id, String url, boolean external, int parentId, String title) {
-        return new Tab((Context) getActivity(), id, url, external, parentId, title, TabType.BROWSING);
+        return new Tab((Context) getActivity(), id, url, external, parentId, title);
     }
 
     private Tab createPrivateTab(int id, String url, boolean external, int parentId, String title) {
-        return new PrivateTab((Context) getActivity(), id, url, external, parentId, title, TabType.BROWSING);
+        return new PrivateTab((Context) getActivity(), id, url, external, parentId, title);
     }
 
     @Override
     public void setUp() throws Exception {
         super.setUp(sTabProviderCallable, BrowserContract.TABS_AUTHORITY, "tabs.db");
         mTests.add(new TestInsertLocalTabs());
     }