part 2 draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Fri, 28 Apr 2017 20:05:22 +0200
changeset 570380 f9f6f401896d08aafde263f5afe9af6a8640c5d7
parent 570377 cf221c1ab979e7c17267a3cb47eb29eae52dd67a
child 570399 e2023d32866d11962b0a0c928a7a9136bc1f8a0f
push id56468
push usermozilla@buttercookie.de
push dateFri, 28 Apr 2017 19:52:36 +0000
milestone55.0a1
part 2 MozReview-Commit-ID: LdmyJ5i10jZ
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/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/customtabs/CustomTabsActivity.java
mobile/android/components/SessionStore.js
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -124,16 +124,18 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
+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 GeckoApp
     extends GeckoActivity
     implements
     BundleEventListener,
     ContextGetter,
     GeckoAppShell.GeckoInterface,
@@ -425,35 +427,38 @@ public abstract class GeckoApp
     public void removeAppStateListener(GeckoAppShell.AppStateListener listener) {
         mAppStateListeners.remove(listener);
     }
 
     @Override
     public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
         // When a tab is closed, it is always unselected first.
         // When a tab is unselected, another tab is always selected first.
-        // When we're switching activities because of differing tab types,
-        // the first statement is not true.
         switch (msg) {
             case UNSELECTED:
                 break;
 
             case LOCATION_CHANGE:
                 // We only care about location change for the selected tab.
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     resetOptionsMenu();
                     resetFormAssistPopup();
                 }
                 break;
 
             case SELECTED:
                 resetOptionsMenu();
                 resetFormAssistPopup();
 
-                if (saveAsLastSelectedTab(tab)) {
+                if (mLastSelectedTabId != INVALID_TAB_ID && isForegrounded() &&
+                        // mCheckTabSelectionOnResume implies that we want to defer a pending
+                        // activity switch because we're actually about to leave the app.
+                        !mCheckTabSelectionOnResume && !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() &&
@@ -2343,34 +2348,30 @@ public abstract class GeckoApp
     /**
      * Check whether an intent with tab switch extras refers to a tab that
      * is actually existing at the moment.
      *
      * @param intent The intent to be checked.
      * @return True if the tab specified in the intent is existing in our Tabs list.
      */
     protected boolean hasGeckoTab(SafeIntent intent) {
-        final int tabId = intent.getIntExtra(Tabs.INTENT_EXTRA_TAB_ID, INVALID_TAB_ID);
-        final String intentSessionUUID = intent.getStringExtra(Tabs.INTENT_EXTRA_SESSION_UUID);
+        final int tabId = intent.getIntExtra(INTENT_EXTRA_TAB_ID, INVALID_TAB_ID);
+        final String intentSessionUUID = intent.getStringExtra(INTENT_EXTRA_SESSION_UUID);
         final Tab tabToCheck = Tabs.getInstance().getTab(tabId);
 
         // 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(Tabs.INTENT_EXTRA_SESSION_UUID) ||
+        return tabToCheck != null && (!intent.hasExtra(INTENT_EXTRA_SESSION_UUID) ||
                 GeckoApplication.getSessionUUID().equals(intentSessionUUID));
     }
 
     protected void handleSelectTabIntent(SafeIntent intent) {
-        final int tabId = intent.getIntExtra(Tabs.INTENT_EXTRA_TAB_ID, INVALID_TAB_ID);
+        final int tabId = intent.getIntExtra(INTENT_EXTRA_TAB_ID, INVALID_TAB_ID);
         final Tab selectedTab = Tabs.getInstance().selectTab(tabId);
-        // If the tab selection has been redirected to a different activity,
-        // the selectedTab within Tabs will not have been updated yet.
-        if (selectedTab == Tabs.getInstance().getSelectedTab()) {
-            onTabSelectFromIntent(selectedTab);
-        }
+        onTabSelectFromIntent(selectedTab);
     }
 
     /**
      * 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();
@@ -2857,17 +2858,17 @@ public abstract class GeckoApp
                     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()) {
                         final GeckoBundle data = new GeckoBundle(1);
                         data.putInt("nextSelectedTabId", nextSelectedTab.getId());
                         EventDispatcher.getInstance().dispatch("Tab:KeepZombified", data);
                     }
-                    tabs.closeTabNoActivitySwitch(tab);
+                    tabs.closeTab(tab);
                     mCheckTabSelectionOnResume = true;
                     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
@@ -1,43 +1,53 @@
 /* -*- 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 org.json.JSONException;
+import org.json.JSONObject;
+import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.webapps.WebAppActivity;
 import org.mozilla.gecko.widget.ExternalIntentDuringPrivateBrowsingPromptFragment;
 
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.provider.Browser;
 import android.support.annotation.Nullable;
 import android.support.v4.app.FragmentActivity;
 import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.MimeTypeMap;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
+import static org.mozilla.gecko.Tabs.INTENT_EXTRA_SESSION_UUID;
+import static org.mozilla.gecko.Tabs.INTENT_EXTRA_TAB_ID;
+
 public final class IntentHelper implements BundleEventListener {
 
     private static final String LOGTAG = "GeckoIntentHelper";
     private static final String[] GECKO_EVENTS = {
         // Need to be on Gecko thread for synchronous callback.
         "Intent:GetHandlers",
     };
     private static final String[] UI_EVENTS = {
@@ -211,16 +221,61 @@ 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());
+        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/SingleTabActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/SingleTabActivity.java
@@ -71,17 +71,17 @@ public abstract class SingleTabActivity 
             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) &&
-                tabs.currentActivityMatchesTab(tabToSelect)) {
+                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) {
@@ -93,17 +93,17 @@ public abstract class SingleTabActivity 
      * @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 (tabs.currentActivityMatchesTab(tabToSelect)) {
+            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);
@@ -120,17 +120,17 @@ public abstract class SingleTabActivity 
             lastSessionUUID = savedInstanceState.getString(LAST_SESSION_UUID);
         } else {
             lastSelectedTabId = mLastSelectedTabId;
             lastSessionUUID = mLastSessionUUID;
         }
 
         final Tab tabToSelect = tabs.getTab(lastSelectedTabId);
         if (tabToSelect != null && GeckoApplication.getSessionUUID().equals(lastSessionUUID) &&
-                tabs.currentActivityMatchesTab(tabToSelect)) {
+                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;
--- a/mobile/android/base/java/org/mozilla/gecko/Tab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java
@@ -7,32 +7,35 @@ 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.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.text.TextUtils;
@@ -796,16 +799,42 @@ public class Tab {
 
     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
@@ -1,51 +1,41 @@
 /* -*- 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;
 
-import java.io.File;
-import java.io.IOException;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import android.app.Activity;
-import android.content.Intent;
 import android.content.SharedPreferences;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
-import org.json.JSONException;
-import org.json.JSONObject;
 import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.customtabs.CustomTabsActivity;
-import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.distribution.PartnerBrowserCustomizationsClient;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.WhatsNewReceiver;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.reader.ReaderModeUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.FileUtils;
 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 org.mozilla.gecko.webapps.WebAppIndexer;
 
 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;
@@ -308,40 +298,28 @@ public class Tabs implements BundleEvent
             Tab tab = getTab(id);
             mOrder.remove(tab);
             mTabs.remove(id);
             tabPositionCache.mTabId = INVALID_TAB_ID;
         }
     }
 
     public synchronized Tab selectTab(int id) {
-        return selectTab(id, true);
-    }
-
-    public synchronized Tab selectTab(int id, boolean switchActivities) {
         if (!mTabs.containsKey(id))
             return null;
 
         final Tab oldTab = getSelectedTab();
         final Tab tab = mTabs.get(id);
 
         // This avoids a NPE below, but callers need to be careful to
         // handle this case.
         if (tab == null || oldTab == tab) {
             return tab;
         }
 
-        if (switchActivities && oldTab != null && oldTab.getType() != tab.getType() &&
-                !currentActivityMatchesTab(tab)) {
-            // We're in the wrong activity for this kind of tab, so launch the correct one
-            // and then try again.
-            launchActivityForTab(tab);
-            return tab;
-        }
-
         mSelectedTab = tab;
         notifyListeners(tab, TabEvents.SELECTED);
 
         if (mLayerView != null) {
             mLayerView.setClearColor(getTabColor(tab));
         }
 
         if (oldTab != null) {
@@ -350,92 +328,16 @@ public class Tabs implements BundleEvent
 
         // Pass a message to Gecko to update tab state in BrowserApp.
         final GeckoBundle data = new GeckoBundle(1);
         data.putInt("id", tab.getId());
         EventDispatcher.getInstance().dispatch("Tab:Selected", data);
         return tab;
     }
 
-    /**
-     * Check whether the currently active activity matches the tab type of the passed tab.
-     */
-    public boolean currentActivityMatchesTab(Tab tab) {
-        final Activity currentActivity = GeckoActivityMonitor.getInstance().getCurrentActivity();
-
-        if (currentActivity == null) {
-            return false;
-        }
-        String currentActivityName = currentActivity.getClass().getName();
-        return currentActivityName.equals(getClassNameForTab(tab));
-    }
-
-    private void launchActivityForTab(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, getClassNameForTab(tab));
-        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());
-        mAppContext.startActivity(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 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"));
-    }
-
-    /**
-     * Get the class name of the activity that should be displaying this tab.
-     */
-    private String getClassNameForTab(Tab tab) {
-        TabType type = tab.getType();
-
-        switch (type) {
-            case CUSTOMTAB:
-                return CustomTabsActivity.class.getName();
-            case WEBAPP:
-                final int index =  WebAppIndexer.getInstance().getIndexForManifest(
-                        tab.getManifestPath(), mAppContext);
-                return WebAppIndexer.WEBAPP_CLASS + index;
-            default:
-                return AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS;
-        }
-    }
-
     public synchronized boolean selectLastTab() {
         if (mOrder.isEmpty()) {
             return false;
         }
 
         selectTab(mOrder.get(mOrder.size() - 1).getId());
         return true;
     }
@@ -535,43 +437,37 @@ public class Tabs implements BundleEvent
     }
 
     /** Close tab and then select the default next tab */
     @RobocopTarget
     public synchronized void closeTab(Tab tab) {
         closeTab(tab, getNextTab(tab));
     }
 
-    /** Don't switch activities even if the default next tab is of a different tab type */
-    public synchronized void closeTabNoActivitySwitch(Tab tab) {
-        closeTab(tab, getNextTab(tab), false, false);
-    }
-
     public synchronized void closeTab(Tab tab, Tab nextTab) {
-        closeTab(tab, nextTab, false, true);
+        closeTab(tab, nextTab, false);
     }
 
     public synchronized void closeTab(Tab tab, boolean showUndoToast) {
-        closeTab(tab, getNextTab(tab), showUndoToast, true);
+        closeTab(tab, getNextTab(tab), showUndoToast);
     }
 
     /** Close tab and then select nextTab */
-    public synchronized void closeTab(final Tab tab, Tab nextTab,
-                                      boolean showUndoToast, boolean switchActivities) {
+    public synchronized void closeTab(final Tab tab, Tab nextTab, boolean showUndoToast) {
         if (tab == null)
             return;
 
         int tabId = tab.getId();
         removeTab(tabId);
 
         if (nextTab == null) {
             nextTab = loadUrl(getHomepageForNewTab(mAppContext), LOADURL_NEW_TAB);
         }
 
-        selectTab(nextTab.getId(), switchActivities);
+        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);
         EventDispatcher.getInstance().dispatch("Tab:Closed", data);
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -337,17 +337,17 @@ public class CustomTabsActivity extends 
 
     private void bindNavigationCallback(@NonNull final Toolbar toolbar) {
         toolbar.setNavigationOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 onDone();
                 final Tabs tabs = Tabs.getInstance();
                 final Tab tab = tabs.getSelectedTab();
-                tabs.closeTabNoActivitySwitch(tab);
+                tabs.closeTab(tab);
                 mCheckTabSelectionOnResume = true;
             }
         });
     }
 
     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();
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -684,20 +684,19 @@ SessionStore.prototype = {
 
   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, and
-      // - in conjunction with switching between activities, the event order as we see it can
-      //   become reversed.
+      // 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") {
       this._lastClosedTabIndex = INVALID_TAB_INDEX;
       return;
     }