Bug 1356346 - Overhaul CustomTabsActivity.java to use GeckoView rather than GeckoApp. r=jchen, walkingice
authorDylan Roeh <droeh@mozilla.com>
Tue, 25 Apr 2017 11:48:05 -0500
changeset 409788 5eb1cfd59d0017e714ac7eafc78561e346d5ed5f
parent 409787 b94150e0a2991fe40a3504355fad38969b413da0
child 409789 3e9ceef9b2802d2d2353b8389e6f09c77f991577
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen, walkingice
bugs1356346
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1356346 - Overhaul CustomTabsActivity.java to use GeckoView rather than GeckoApp. r=jchen, walkingice
mobile/android/base/java/org/mozilla/gecko/GlobalHistory.java
mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java
mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
mobile/android/base/resources/layout/customtabs_activity.xml
mobile/android/chrome/geckoview/geckoview.xul
--- a/mobile/android/base/java/org/mozilla/gecko/GlobalHistory.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GlobalHistory.java
@@ -177,17 +177,17 @@ class GlobalHistory {
                 getInstance().update(context.getContentResolver(), db, uri, title);
             }
         });
     }
 
     /* protected */ void checkVisited(final String uri) {
         final String storedURI = ReaderModeUtils.stripAboutReaderUrl(uri);
 
-        final NotifierRunnable runnable = new NotifierRunnable(GeckoAppShell.getContext());
+        final NotifierRunnable runnable = new NotifierRunnable(GeckoAppShell.getApplicationContext());
         mHandler.post(new Runnable() {
             @Override
             public void run() {
                 // this runs on the same handler thread as the processing loop,
                 // so no synchronization needed
                 mPendingUris.add(storedURI);
                 if (mProcessing) {
                     // there's already a runnable queued up or working away, so
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/ActionBarPresenter.java
@@ -25,16 +25,17 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import org.mozilla.gecko.GeckoView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SiteIdentity;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.toolbar.SecurityModeUtil;
 import org.mozilla.gecko.toolbar.SiteIdentityPopup;
 import org.mozilla.gecko.util.ColorUtil;
 
 /**
@@ -103,35 +104,34 @@ public class ActionBarPresenter {
     }
 
     /**
      * To display Url in CustomView only and immediately.
      *
      * @param url Url String to display
      */
     public void displayUrlOnly(@NonNull final String url) {
-        updateCustomView(null, null, url);
+        updateCustomView(null, url, GeckoView.ProgressListener.STATE_IS_INSECURE);
     }
 
     /**
-     * Update appearance of CustomView of ActionBar.
+     * Update appearance of CustomView of ActionBar
      *
-     * @param tab a Tab instance of current web page to provide information to render ActionBar.
+     * @param title          Title for current website. Could be null if don't want to show title.
+     * @param url            URL for current website. At least Custom will show this url.
+     * @param securityStatus Security status, possible values given in GeckoView.ProgressListener
      */
-    public void update(@NonNull final Tab tab) {
-        final String title = tab.getTitle();
-        final String url = tab.getBaseDomain();
-
+    public void update(final String title, final String url, final int securityStatus) {
         // Do not update CustomView immediately. If this method be invoked rapidly several times,
         // only apply last one.
         mHandler.removeCallbacks(mUpdateAction);
         mUpdateAction = new Runnable() {
             @Override
             public void run() {
-                updateCustomView(tab.getSiteIdentity(), title, url);
+                updateCustomView(title, url, securityStatus);
             }
         };
         mHandler.postDelayed(mUpdateAction, CUSTOM_VIEW_UPDATE_DELAY);
     }
 
     /**
      * To add a always-show-as-action button to menu, and manually create a view to insert.
      *
@@ -215,35 +215,25 @@ public class ActionBarPresenter {
     /**
      * To update appearance of CustomView of ActionBar, includes its icon, title and url text.
      *
      * @param identity SiteIdentity for current website. Could be null if don't want to show icon.
      * @param title    Title for current website. Could be null if don't want to show title.
      * @param url      URL for current website. At least Custom will show this url.
      */
     @UiThread
-    private void updateCustomView(@Nullable SiteIdentity identity,
-                                  @Nullable String title,
-                                  @NonNull String url) {
-        // update site-info icon
-        if (identity == null) {
-            mIconView.setVisibility(View.INVISIBLE);
-        } else {
-            final SecurityModeUtil.Mode mode = SecurityModeUtil.resolve(identity);
+    private void updateCustomView(final String title, final String url, final int securityStatus) {
+        if (securityStatus == GeckoView.ProgressListener.STATE_IS_SECURE) {
             mIconView.setVisibility(View.VISIBLE);
-            mIconView.setImageLevel(mode.ordinal());
-            mIdentityPopup.setSiteIdentity(identity);
-
-            if (mode == SecurityModeUtil.Mode.LOCK_SECURE) {
-                // Lock-Secure is special case. Keep its original green color.
-                DrawableCompat.setTintList(mIconView.getDrawable(), null);
-            } else {
-                // Icon use same color as TextView.
-                DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
-            }
+            mIconView.setImageLevel(SecurityModeUtil.Mode.LOCK_SECURE.ordinal());
+            // Lock-Secure is special case. Keep its original green color.
+            DrawableCompat.setTintList(mIconView.getDrawable(), null);
+        } else {
+            mIconView.setVisibility(View.INVISIBLE);
+            DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
         }
 
         // If no title to use, use Url as title
         if (TextUtils.isEmpty(title)) {
             mTitleView.setText(url);
             mUrlView.setText(null);
             mUrlView.setVisibility(View.GONE);
         } else {
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -15,101 +15,104 @@ import android.graphics.drawable.Drawabl
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Browser;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.design.widget.Snackbar;
 import android.support.v4.util.SparseArrayCompat;
 import android.support.v7.app.ActionBar;
-import android.support.v7.view.ActionMode;
+import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.ProgressBar;
 
 import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.GeckoView;
+import org.mozilla.gecko.GeckoViewSettings;
 import org.mozilla.gecko.R;
-import org.mozilla.gecko.SingleTabActivity;
 import org.mozilla.gecko.SnackbarBuilder;
-import org.mozilla.gecko.Tab;
-import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
-import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.ColorUtil;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.IntentUtils;
-import org.mozilla.gecko.widget.ActionModePresenter;
 import org.mozilla.gecko.widget.GeckoPopupMenu;
 
 import java.util.List;
 
-import static org.mozilla.gecko.Tabs.TabEvents;
-
-public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabsChangedListener {
+public class CustomTabsActivity extends AppCompatActivity
+                                implements GeckoMenu.Callback,
+                                           GeckoView.ContentListener,
+                                           GeckoView.NavigationListener,
+                                           GeckoView.ProgressListener {
 
     private static final String LOGTAG = "CustomTabsActivity";
 
     private final SparseArrayCompat<PendingIntent> menuItemsIntent = new SparseArrayCompat<>();
     private GeckoPopupMenu popupMenu;
     private View doorhangerOverlay;
     private ActionBarPresenter actionBarPresenter;
-    private ProgressBar mProgressView;
     // A state to indicate whether this activity is finishing with customize animation
     private boolean usingCustomAnimation = false;
 
     private MenuItem menuItemControl;
 
+    private GeckoView mGeckoView;
+
+    private boolean mCanGoBack = false;
+    private boolean mCanGoForward = false;
+    private boolean mCanStop = false;
+    private String mCurrentUrl;
+    private String mCurrentTitle;
+    private int mSecurityStatus = GeckoView.ProgressListener.STATE_IS_INSECURE;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        setContentView(R.layout.customtabs_activity);
+
         final SafeIntent intent = new SafeIntent(getIntent());
 
         doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
 
-        mProgressView = (ProgressBar) findViewById(R.id.page_progress);
         final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
         setSupportActionBar(toolbar);
         final ActionBar actionBar = getSupportActionBar();
         bindNavigationCallback(toolbar);
 
         actionBarPresenter = new ActionBarPresenter(actionBar, getActionBarTextColor());
         actionBarPresenter.displayUrlOnly(intent.getDataString());
         actionBarPresenter.setBackgroundColor(IntentUtil.getToolbarColor(intent), getWindow());
         actionBarPresenter.setTextLongClickListener(new UrlCopyListener());
-    }
+
+        mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
 
-    @Override
-    protected void onTabOpenFromIntent(Tab tab) {
-        super.onTabOpenFromIntent(tab);
+        mGeckoView.setNavigationListener(this);
+        mGeckoView.setProgressListener(this);
+        mGeckoView.setContentListener(this);
 
-        final String host = getReferrerHost();
-        recordCustomTabUsage(host);
-        sendTelemetry();
-    }
+        final GeckoViewSettings settings = mGeckoView.getSettings();
+        settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
 
-    @Override
-    protected void onTabSelectFromIntent(Tab tab) {
-        super.onTabSelectFromIntent(tab);
-
-        // We already listen for SELECTED events, but if the activity has been destroyed and
-        // subsequently recreated without a different tab having been selected in Gecko in the
-        // meantime, our startup won't trigger a SELECTED event because the selected tab in Gecko
-        // doesn't actually change.
-        actionBarPresenter.update(tab);
+        if (intent != null && !TextUtils.isEmpty(intent.getDataString())) {
+            mGeckoView.loadUri(intent.getDataString());
+        } else {
+            Log.w(LOGTAG, "No intend found for custom tab");
+            finish();
+        }
     }
 
     private void sendTelemetry() {
         final SafeIntent startIntent = new SafeIntent(getIntent());
 
         Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab");
         if (IntentUtil.hasToolbarColor(startIntent)) {
             Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-hasToolbarColor");
@@ -150,102 +153,44 @@ public class CustomTabsActivity extends 
             // Use its package name to retrieve animation resource
             return IntentUtil.getAnimationPackageName(new SafeIntent(getIntent()));
         } else {
             return super.getPackageName();
         }
     }
 
     @Override
-    public void onDone() {
-        // We're most probably running within a foreign app's task, so we have no choice what to
-        // call here if we want to allow the user to return to that task's previous activity.
-        finish();
-    }
-
-    @Override
     public void finish() {
         super.finish();
 
         final SafeIntent intent = new SafeIntent(getIntent());
         // When 3rd party app launch this Activity, it could also specify custom exit-animation.
         if (IntentUtil.hasExitAnimation(intent)) {
             usingCustomAnimation = true;
             overridePendingTransition(IntentUtil.getEnterAnimationRes(intent),
                     IntentUtil.getExitAnimationRes(intent));
             usingCustomAnimation = false;
         }
     }
 
     @Override
-    protected int getNewTabFlags() {
-        return Tabs.LOADURL_CUSTOMTAB | super.getNewTabFlags();
-    }
-
-    @Override
-    public int getLayout() {
-        return R.layout.customtabs_activity;
-    }
-
-    @Override
-    public View getDoorhangerOverlay() {
-        return doorhangerOverlay;
-    }
-
-    @Override
-    public void onTabChanged(Tab tab, TabEvents msg, String data) {
-        super.onTabChanged(tab, msg, data);
-
-        if (!Tabs.getInstance().isSelectedTab(tab) ||
-                tab.getType() != Tab.TabType.CUSTOMTAB) {
-            return;
+    public void onBackPressed() {
+        if (mCanGoBack) {
+            mGeckoView.goBack();
+        } else {
+            finish();
         }
-
-        if (msg == TabEvents.START
-                || msg == TabEvents.STOP
-                || msg == TabEvents.ADDED
-                || msg == TabEvents.LOAD_ERROR
-                || msg == TabEvents.LOADED
-                || msg == TabEvents.LOCATION_CHANGE
-                || msg == TabEvents.SELECTED) {
-
-            updateProgress((tab.getState() == Tab.STATE_LOADING),
-                    tab.getLoadProgress());
-        }
-
-        if (msg == TabEvents.LOCATION_CHANGE
-                || msg == TabEvents.SECURITY_CHANGE
-                || msg == TabEvents.TITLE
-                || msg == TabEvents.SELECTED) {
-            actionBarPresenter.update(tab);
-        }
-
-        updateMenuItemForward();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mLayerView.getDynamicToolbarAnimator().setPinned(true, PinReason.CUSTOM_TAB);
-        actionBarPresenter.onResume();
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        mLayerView.getDynamicToolbarAnimator().setPinned(false, PinReason.CUSTOM_TAB);
-        actionBarPresenter.onPause();
     }
 
     // Usually should use onCreateOptionsMenu() to initialize menu items. But GeckoApp overwrite
     // it to support custom menu(Bug 739412). Then the parameter *menu* in this.onCreateOptionsMenu()
     // and this.onPrepareOptionsMenu() are different instances - GeckoApp.onCreatePanelMenu() changed it.
     // CustomTabsActivity only use standard menu in ActionBar, so initialize menu here.
     @Override
-    public boolean onCreatePanelMenu(final int id, final Menu menu) {
+    public boolean onCreateOptionsMenu(final Menu menu) {
 
         // if 3rd-party app asks to add an action button
         SafeIntent intent = new SafeIntent(getIntent());
         if (IntentUtil.hasActionButton(intent)) {
             final Bitmap bitmap = IntentUtil.getActionButtonIcon(intent);
             final Drawable icon = new BitmapDrawable(getResources(), bitmap);
             final boolean shouldTint = IntentUtil.isActionButtonTinted(intent);
             actionBarPresenter.addActionButton(menu, icon, shouldTint)
@@ -271,16 +216,26 @@ public class CustomTabsActivity extends 
                     }
                 });
 
         updateMenuItemForward();
         return true;
     }
 
     @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        return onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onMenuItemLongClick(MenuItem item) {
+        return false;
+    }
+
+    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case android.R.id.home:
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-home");
                 finish();
                 return true;
             case R.id.share:
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-share");
@@ -312,53 +267,29 @@ public class CustomTabsActivity extends 
     /**
      * Called when the menu that's been clicked is added by the client
      */
     private void onCustomMenuItemClicked(PendingIntent intent) {
         Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-customized-menu");
         performPendingIntent(intent);
     }
 
-    @Override
-    protected ActionModePresenter getTextSelectPresenter() {
-        return new ActionModePresenter() {
-            private ActionMode mMode;
-
-            @Override
-            public void startActionMode(ActionMode.Callback callback) {
-                mMode = startSupportActionMode(callback);
-            }
-
-            @Override
-            public void endActionMode() {
-                if (mMode != null) {
-                    mMode.finish();
-                }
-            }
-        };
-    }
-
     private void bindNavigationCallback(@NonNull final Toolbar toolbar) {
         toolbar.setNavigationOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                onDone();
-                final Tabs tabs = Tabs.getInstance();
-                final Tab tab = tabs.getSelectedTab();
-                mSuppressActivitySwitch = true;
-                tabs.closeTab(tab);
+                finish();
             }
         });
     }
 
     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();
-        final Tab tab = Tabs.getInstance().getSelectedTab();
-        additional.setData(Uri.parse(tab.getURL()));
+        additional.setData(Uri.parse(mCurrentUrl));
         try {
             pendingIntent.send(this, 0, additional);
         } catch (PendingIntent.CanceledException e) {
             Log.w(LOGTAG, "Performing a canceled pending intent", e);
         }
     }
 
     /**
@@ -430,110 +361,99 @@ public class CustomTabsActivity extends 
     private void updateMenuItemForward() {
         if ((popupMenu == null)
                 || (popupMenu.getMenu() == null)
                 || (popupMenu.getMenu().findItem(R.id.custom_tabs_menu_forward) == null)) {
             return;
         }
 
         final MenuItem forwardMenuItem = popupMenu.getMenu().findItem(R.id.custom_tabs_menu_forward);
-        final Tab tab = Tabs.getInstance().getSelectedTab();
-        final boolean enabled = (tab != null && tab.canDoForward());
-        forwardMenuItem.setEnabled(enabled);
+        forwardMenuItem.setEnabled(mCanGoForward);
     }
 
     /**
-     * Update loading progress of current page
-     *
-     * @param isLoading to indicate whether ProgressBar should be visible or not
-     * @param progress  value of loading progress in percent, should be 0 - 100.
+     * Update loading status of current page
      */
-    private void updateProgress(final boolean isLoading, final int progress) {
-        if (isLoading) {
-            mProgressView.setVisibility(View.VISIBLE);
-            mProgressView.setProgress(progress);
-        } else {
-            mProgressView.setVisibility(View.GONE);
-        }
-
+    private void updateCanStop() {
         if (menuItemControl != null) {
             Drawable icon = menuItemControl.getIcon();
-            icon.setLevel(progress);
+            if (mCanStop) {
+                icon.setLevel(0);
+            } else {
+                icon.setLevel(100);
+            }
         }
     }
 
     /**
+     * Update the state of the action bar
+     */
+    private void updateActionBar() {
+        actionBarPresenter.update(mCurrentTitle, mCurrentUrl, mSecurityStatus);
+    }
+
+    /**
      * Call this method to reload page, or stop page loading if progress not complete yet.
      */
     private void onLoadingControlClicked() {
-        final Tab tab = Tabs.getInstance().getSelectedTab();
-        if (tab != null) {
-            if (tab.getLoadProgress() == Tab.LOAD_PROGRESS_STOP) {
-                tab.doReload(true);
-            } else {
-                tab.doStop();
-            }
+        if (mCanStop) {
+            // TODO: enable this after implementing GeckoView.stop()
+            //mGeckoView.stop();
+        } else {
+            mGeckoView.reload();
         }
     }
 
     private void onForwardClicked() {
-        final Tab tab = Tabs.getInstance().getSelectedTab();
-        if ((tab != null) && tab.canDoForward()) {
-            tab.doForward();
+        if (mCanGoForward) {
+            mGeckoView.goForward();
         }
     }
 
     /**
      * Callback for Open-in menu item.
      */
     private void onOpenInClicked() {
-        final Tab tab = Tabs.getInstance().getSelectedTab();
-        if (tab != null) {
-            // To launch default browser with url of current tab.
-            final Intent intent = new Intent();
-            intent.setData(Uri.parse(tab.getURL()));
-            intent.setAction(Intent.ACTION_VIEW);
-            startActivity(intent);
-            finish();
-        }
+        final Intent intent = new Intent();
+        intent.setData(Uri.parse(mCurrentUrl));
+        intent.setAction(Intent.ACTION_VIEW);
+        startActivity(intent);
+        finish();
     }
 
     private void onActionButtonClicked() {
         Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-action-button");
         PendingIntent pendingIntent = IntentUtil.getActionButtonPendingIntent(new SafeIntent(getIntent()));
         performPendingIntent(pendingIntent);
     }
 
 
     /**
      * Callback for Share menu item.
      */
     private void onShareClicked() {
-        final String url = Tabs.getInstance().getSelectedTab().getURL();
-
-        if (!TextUtils.isEmpty(url)) {
+        if (!TextUtils.isEmpty(mCurrentUrl)) {
             Intent shareIntent = new Intent(Intent.ACTION_SEND);
             shareIntent.setType("text/plain");
-            shareIntent.putExtra(Intent.EXTRA_TEXT, url);
+            shareIntent.putExtra(Intent.EXTRA_TEXT, mCurrentUrl);
 
             Intent chooserIntent = Intent.createChooser(shareIntent, getString(R.string.share_title));
             chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             startActivity(chooserIntent);
         }
     }
 
     /**
      * Listener when user long-click ActionBar to copy URL.
      */
     private class UrlCopyListener implements View.OnLongClickListener {
         @Override
         public boolean onLongClick(View v) {
-            final String url = Tabs.getInstance().getSelectedTab().getURL();
-            if (!TextUtils.isEmpty(url)) {
-                Clipboard.setText(url);
+            if (!TextUtils.isEmpty(mCurrentUrl)) {
+                Clipboard.setText(mCurrentUrl);
                 SnackbarBuilder.builder(CustomTabsActivity.this)
                         .message(R.string.custom_tabs_hint_url_copy)
                         .duration(Snackbar.LENGTH_SHORT)
                         .buildAndShow();
             }
             return true;
         }
     }
@@ -549,9 +469,64 @@ public class CustomTabsActivity extends 
             return referrer.getHost();
         }
         String referrerName = intent.getStringExtra("android.intent.extra.REFERRER_NAME");
         if (referrerName != null) {
             return Uri.parse(referrerName).getHost();
         }
         return null;
     }
+
+    /* GeckoView.NavigationListener */
+    @Override
+    public void onLocationChange(GeckoView view, String url) {
+        mCurrentUrl = url;
+        updateActionBar();
+    }
+
+    @Override
+    public void onCanGoBack(GeckoView view, boolean canGoBack) {
+        mCanGoBack = canGoBack;
+    }
+
+    @Override
+    public void onCanGoForward(GeckoView view, boolean canGoForward) {
+        mCanGoForward = canGoForward;
+        updateMenuItemForward();
+    }
+
+    /* GeckoView.ProgressListener */
+    @Override
+    public void onPageStart(GeckoView view, String url) {
+        mCurrentUrl = url;
+        mCanStop = true;
+        updateActionBar();
+        updateCanStop();
+    }
+
+    @Override
+    public void onPageStop(GeckoView view, boolean success) {
+        mCanStop = false;
+        updateCanStop();
+    }
+
+    @Override
+    public void onSecurityChange(GeckoView view, int status) {
+        if ((status & STATE_IS_INSECURE) != 0) {
+            mSecurityStatus = STATE_IS_INSECURE;
+        } else if ((status & STATE_IS_BROKEN) != 0) {
+            mSecurityStatus = STATE_IS_BROKEN;
+        } else if ((status & STATE_IS_SECURE) != 0) {
+            mSecurityStatus = STATE_IS_SECURE;
+        }
+        updateActionBar();
+    }
+
+    /* GeckoView.ContentListener */
+    @Override
+    public void onTitleChange(GeckoView view, String title) {
+        mCurrentTitle = title;
+        updateActionBar();
+    }
+
+    @Override
+    public void onFullScreen(GeckoView view, boolean fullScreen) {}
 }
--- a/mobile/android/base/resources/layout/customtabs_activity.xml
+++ b/mobile/android/base/resources/layout/customtabs_activity.xml
@@ -7,66 +7,31 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/root_layout"
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <!--
-        This layout is quite complex because GeckoApp accesses all view groups
-        in this tree. In a perfect world this should just include a GeckoView.
-    -->
-
     <android.support.v7.widget.Toolbar
         android:id="@id/actionbar"
         android:layout_width="match_parent"
         android:layout_height="?attr/actionBarSize"
         android:elevation="4dp"
         android:background="@color/text_and_tabs_tray_grey"
         app:layout_scrollFlags="scroll|enterAlways"/>
 
-    <view class="org.mozilla.gecko.GeckoApp$MainLayout"
-        android:id="@+id/main_layout"
-        android:layout_width="match_parent"
+    <org.mozilla.gecko.GeckoView
+        android:id="@+id/gecko_view"
+        android:layout_width="fill_parent"
         android:layout_below="@id/actionbar"
         android:layout_height="match_parent"
-        android:background="@android:color/transparent">
-
-        <RelativeLayout android:id="@+id/gecko_layout"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_below="@+id/tablet_tab_strip"
-            android:layout_above="@+id/find_in_page">
-
-            <fragment class="org.mozilla.gecko.GeckoViewFragment"
-                android:id="@+id/layer_view"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:scrollbars="none"/>
-
-            <org.mozilla.gecko.FormAssistPopup android:id="@+id/form_assist_popup"
-                                               android:layout_width="match_parent"
-                                               android:layout_height="match_parent"
-                                               android:visibility="gone"/>
-
-        </RelativeLayout>
-
-    </view>
-
-    <ProgressBar
-        android:id="@id/page_progress"
-        style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="4dp"
-        android:layout_alignTop="@id/main_layout"
-        android:background="@drawable/url_bar_bg"
-        android:progressDrawable="@drawable/progressbar"
-        tools:progress="70"/>
+        android:scrollbars="none"/>
 
     <View android:id="@+id/custom_tabs_doorhanger_overlay"
-          android:layout_width="match_parent"
-          android:layout_height="match_parent"
-          android:background="@color/dark_transparent_overlay"
-          android:alpha="0"
-          android:layerType="hardware"/>
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/dark_transparent_overlay"
+        android:alpha="0"
+        android:layerType="hardware"/>
+
 </RelativeLayout>
\ No newline at end of file
--- a/mobile/android/chrome/geckoview/geckoview.xul
+++ b/mobile/android/chrome/geckoview/geckoview.xul
@@ -2,15 +2,15 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 
 <window id="main-window"
         onload="startup();"
-        windowtype="navigator:browser"
+        windowtype="navigator:geckoview"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <browser id="content" type="content" primary="true" src="about:blank" flex="1"/>
 
   <script type="application/javascript" src="chrome://geckoview/content/geckoview.js"/>
 </window>