Index: chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a37ad16f1e37ba8afc6ba05f35ebea01c6906fc7 |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/document/DocumentActivity.java |
@@ -0,0 +1,991 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.document; |
+ |
+import android.annotation.TargetApi; |
+import android.app.Activity; |
+import android.content.Intent; |
+import android.content.res.Configuration; |
+import android.graphics.Bitmap; |
+import android.graphics.Color; |
+import android.net.Uri; |
+import android.os.Build; |
+import android.text.TextUtils; |
+import android.util.Log; |
+import android.view.KeyEvent; |
+import android.view.Menu; |
+import android.view.View; |
+import android.view.ViewGroup; |
+import android.view.Window; |
+ |
+import com.google.android.apps.chrome.R; |
+ |
+import org.chromium.base.ActivityState; |
+import org.chromium.base.ApplicationStatus; |
+import org.chromium.base.SysUtils; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.library_loader.LibraryLoader; |
+import org.chromium.base.metrics.RecordUserAction; |
+import org.chromium.chrome.browser.ChromeMobileApplication; |
+import org.chromium.chrome.browser.CompositorChromeActivity; |
+import org.chromium.chrome.browser.IntentHandler; |
+import org.chromium.chrome.browser.KeyboardShortcuts; |
+import org.chromium.chrome.browser.Tab; |
+import org.chromium.chrome.browser.TabState; |
+import org.chromium.chrome.browser.UrlConstants; |
+import org.chromium.chrome.browser.UrlUtilities; |
+import org.chromium.chrome.browser.appmenu.AppMenuHandler; |
+import org.chromium.chrome.browser.appmenu.AppMenuObserver; |
+import org.chromium.chrome.browser.appmenu.ChromeAppMenuPropertiesDelegate; |
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerDocument; |
+import org.chromium.chrome.browser.document.DocumentTab.DocumentTabObserver; |
+import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils; |
+import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor; |
+import org.chromium.chrome.browser.firstrun.FirstRunStatus; |
+import org.chromium.chrome.browser.metrics.UmaUtils; |
+import org.chromium.chrome.browser.ntp.DocumentNewTabPage; |
+import org.chromium.chrome.browser.ntp.NewTabPage; |
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager; |
+import org.chromium.chrome.browser.preferences.PrefServiceBridge; |
+import org.chromium.chrome.browser.preferences.bandwidth.BandwidthReductionPreferences; |
+import org.chromium.chrome.browser.preferences.bandwidth.DataReductionPromoScreen; |
+import org.chromium.chrome.browser.signin.SigninPromoScreen; |
+import org.chromium.chrome.browser.ssl.ConnectionSecurityHelperSecurityLevel; |
+import org.chromium.chrome.browser.tabmodel.SingleTabModelSelector; |
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; |
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; |
+import org.chromium.chrome.browser.tabmodel.document.ActivityDelegate; |
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel; |
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModel.InitializationObserver; |
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelImpl; |
+import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector; |
+import org.chromium.chrome.browser.toolbar.ToolbarControlContainer; |
+import org.chromium.chrome.browser.toolbar.ToolbarHelper; |
+import org.chromium.chrome.browser.util.FeatureUtilities; |
+import org.chromium.chrome.browser.util.IntentUtils; |
+import org.chromium.chrome.browser.widget.ControlContainer; |
+import org.chromium.chrome.browser.widget.RoundedIconGenerator; |
+import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager; |
+import org.chromium.components.service_tab_launcher.ServiceTabLauncher; |
+import org.chromium.content.browser.ContentVideoView; |
+import org.chromium.content.browser.ContentViewCore; |
+import org.chromium.content_public.browser.LoadUrlParams; |
+import org.chromium.content_public.browser.NavigationEntry; |
+import org.chromium.content_public.browser.navigation_controller.LoadURLType; |
+import org.chromium.content_public.common.Referrer; |
+import org.chromium.ui.base.PageTransition; |
+ |
+/** |
+ * This is the main activity for Chrome while running in document mode. Each activity |
+ * instance represents a single web page at a time while providing Chrome functionality. |
+ * |
+ * <p> |
+ * Tab switching is handled via the system wide Android task management system. |
+ */ |
+@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
+public class DocumentActivity extends CompositorChromeActivity { |
+ protected static final String KEY_INITIAL_URL = "DocumentActivity.KEY_INITIAL_URL"; |
+ |
+ private static final String TAG = "DocumentActivity"; |
+ |
+ private static final int APP_ICON_MIN_SIZE_DP = 32; |
+ private static final int APP_ICON_SIZE_DP = 64; |
+ private static final int APP_ICON_CORNER_RADIUS_DP = 3; |
+ private static final int APP_ICON_TEXT_SIZE_DP = 30; |
+ private static final int APP_ICON_DEFAULT_BACKGROUND_COLOR = Color.rgb(50, 50, 50); |
+ |
+ // Animation exit duration defined in chrome/android/java/res/anim/menu_exit.xml as 150ms, |
+ // plus add another 20ms for a re-layout. |
+ private static final int MENU_EXIT_ANIMATION_WAIT_MS = 170; |
+ |
+ private DocumentTabModel mTabModel; |
+ private InitializationObserver mTabInitializationObserver; |
+ |
+ private Bitmap mIcon; |
+ // Indicates whether mIcon was generated by RoundedIconGenerator. |
+ private boolean mIsUsingGeneratedIcon; |
+ |
+ private Integer mThemeColor; |
+ private int mDefaultThemeColor; |
+ |
+ private DocumentTab mDocumentTab; |
+ private ToolbarHelper mToolbarHelper; |
+ |
+ private ChromeAppMenuPropertiesDelegate mChromeAppMenuPropertiesDelegate; |
+ private AppMenuHandler mAppMenuHandler; |
+ private RoundedIconGenerator mDocumentAppIconGenerator; |
+ private FindToolbarManager mFindToolbarManager; |
+ private boolean mRecordedStartupUma; |
+ |
+ // Whether the page has started loading in this (browser) process once already. Must only be |
+ // used from the UI thread. |
+ private static boolean sIsFirstPageLoadStart = true; |
+ |
+ /** Whether the DocumentTab has already been added to the TabModel. */ |
+ private boolean mNeedsToBeAddedToTabModel; |
+ |
+ @Override |
+ protected boolean isStartedUpCorrectly(Intent intent) { |
+ int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); |
+ boolean isDocumentMode = FeatureUtilities.isDocumentMode(this); |
+ boolean isStartedUpCorrectly = tabId != Tab.INVALID_TAB_ID && isDocumentMode; |
+ if (!isStartedUpCorrectly) { |
+ Log.e(TAG, "Discarding Intent: Tab = " + tabId + ", Document mode = " + isDocumentMode); |
+ } |
+ return isStartedUpCorrectly; |
+ } |
+ |
+ @Override |
+ public void preInflationStartup() { |
+ // Decide whether to record startup UMA histograms. This is done early in the main |
+ // Activity.onCreate() to avoid recording navigation delays when they require user input to |
+ // proceed. See more details in ChromeTabbedActivity.preInflationStartup(). |
+ if (!LibraryLoader.isInitialized()) { |
+ UmaUtils.setRunningApplicationStart(true); |
+ } |
+ |
+ DocumentTabModelSelector.setPrioritizedTabId( |
+ ActivityDelegate.getTabIdFromIntent(getIntent())); |
+ |
+ mTabModel = ChromeMobileApplication.getDocumentTabModelSelector().getModel(isIncognito()); |
+ mTabModel.startTabStateLoad(); |
+ updateLastTabId(); |
+ |
+ SingleTabModelSelector selector = new SingleTabModelSelector(this, isIncognito(), false) { |
+ @Override |
+ public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType type, Tab parent, |
+ boolean incognito) { |
+ PendingDocumentData params = null; |
+ if (loadUrlParams.getPostData() != null |
+ || loadUrlParams.getVerbatimHeaders() != null |
+ || loadUrlParams.getReferrer() != null) { |
+ params = new PendingDocumentData(); |
+ params.postData = loadUrlParams.getPostData(); |
+ params.extraHeaders = loadUrlParams.getVerbatimHeaders(); |
+ params.referrer = loadUrlParams.getReferrer(); |
+ } |
+ |
+ int launchMode = type == TabLaunchType.FROM_LONGPRESS_BACKGROUND |
+ ? ChromeLauncherActivity.LAUNCH_MODE_AFFILIATED |
+ : ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND; |
+ Activity parentActivity = |
+ parent == null ? null : parent.getWindowAndroid().getActivity().get(); |
+ ChromeLauncherActivity.launchDocumentInstance(parentActivity, incognito, |
+ launchMode, loadUrlParams.getUrl(), |
+ DocumentMetricIds.STARTED_BY_WINDOW_OPEN, |
+ loadUrlParams.getTransitionType(), false, params); |
+ return null; |
+ } |
+ }; |
+ setTabModelSelector(selector); |
+ |
+ super.preInflationStartup(); |
+ |
+ supportRequestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY); |
+ } |
+ |
+ @Override |
+ protected int getControlContainerLayoutId() { |
+ return R.layout.control_container; |
+ } |
+ |
+ @Override |
+ public void postInflationStartup() { |
+ super.postInflationStartup(); |
+ |
+ ToolbarControlContainer controlContainer = |
+ ((ToolbarControlContainer) findViewById(R.id.control_container)); |
+ mChromeAppMenuPropertiesDelegate = new ChromeAppMenuPropertiesDelegate(this); |
+ mAppMenuHandler = new AppMenuHandler(this, mChromeAppMenuPropertiesDelegate, |
+ R.menu.main_menu); |
+ mToolbarHelper = new ToolbarHelper(this, controlContainer, mAppMenuHandler, |
+ mChromeAppMenuPropertiesDelegate, getCompositorViewHolder().getInvalidator()); |
+ |
+ final int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); |
+ mTabInitializationObserver = new InitializationObserver(mTabModel) { |
+ @Override |
+ public boolean isSatisfied(int currentState) { |
+ return currentState >= DocumentTabModelImpl.STATE_LOAD_TAB_STATE_BG_END |
+ || mTabModel.isTabStateReady(tabId); |
+ } |
+ |
+ @Override |
+ public boolean isCanceled() { |
+ return isDestroyed() || isFinishing(); |
+ } |
+ |
+ @Override |
+ public void runImmediately() { |
+ initializeUI(); |
+ } |
+ }; |
+ } |
+ |
+ @Override |
+ @VisibleForTesting |
+ public AppMenuHandler getAppMenuHandler() { |
+ return mAppMenuHandler; |
+ } |
+ |
+ @Override |
+ public void prepareMenu(Menu menu) { |
+ if (isNewTabPage() && !isIncognito()) { |
+ menu.findItem(R.id.new_tab_menu_id).setVisible(false); |
+ } |
+ } |
+ |
+ @Override |
+ public void finishNativeInitialization() { |
+ ChromePreferenceManager preferenceManager = |
+ ChromePreferenceManager.getInstance(this); |
+ if (isNewTabPage() && FirstRunStatus.getFirstRunFlowComplete(this)) { |
+ // Only show promos on second NTP. |
+ if (preferenceManager.getPromosSkippedOnFirstStart()) { |
+ // Data reduction promo should be temporarily suppressed if the sign in promo is |
+ // shown to avoid nagging users too much. |
+ if (!SigninPromoScreen.launchSigninPromoIfNeeded(this)) { |
+ DataReductionPromoScreen.launchDataReductionPromo(this); |
+ } |
+ } else { |
+ preferenceManager.setPromosSkippedOnFirstStart(true); |
+ } |
+ } |
+ |
+ FirstRunSignInProcessor.start(this); |
+ |
+ if (!preferenceManager.hasAttemptedMigrationOnUpgrade()) { |
+ InitializationObserver observer = new InitializationObserver( |
+ ChromeMobileApplication.getDocumentTabModelSelector().getModel(false)) { |
+ @Override |
+ protected void runImmediately() { |
+ DocumentMigrationHelper.migrateTabsToDocumentForUpgrade(DocumentActivity.this, |
+ DocumentMigrationHelper.FINALIZE_MODE_NO_ACTION); |
+ } |
+ |
+ @Override |
+ public boolean isSatisfied(int currentState) { |
+ return currentState == DocumentTabModelImpl.STATE_FULLY_LOADED; |
+ } |
+ |
+ @Override |
+ public boolean isCanceled() { |
+ return false; |
+ } |
+ }; |
+ |
+ observer.runWhenReady(); |
+ } |
+ |
+ getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, |
+ Window.PROGRESS_VISIBILITY_OFF); |
+ |
+ // Initialize the native-side TabModels so that the Profile is available when necessary. |
+ ChromeMobileApplication.getDocumentTabModelSelector().onNativeLibraryReady(); |
+ mTabInitializationObserver.runWhenReady(); |
+ |
+ if (mNeedsToBeAddedToTabModel) { |
+ mNeedsToBeAddedToTabModel = false; |
+ mTabModel.addTab(getIntent(), mDocumentTab); |
+ getTabModelSelector().setTab(mDocumentTab); |
+ } |
+ |
+ super.finishNativeInitialization(); |
+ } |
+ |
+ @Override |
+ protected void onDeferredStartup() { |
+ super.onDeferredStartup(); |
+ mToolbarHelper.onDeferredStartup(); |
+ } |
+ |
+ @Override |
+ public boolean hasDoneFirstDraw() { |
+ return mToolbarHelper.hasDoneFirstDraw(); |
+ } |
+ |
+ /** |
+ * @return The ID of the Tab. |
+ */ |
+ protected final int determineTabId() { |
+ int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); |
+ if (tabId == Tab.INVALID_TAB_ID && mDocumentTab != null) tabId = mDocumentTab.getId(); |
+ return tabId; |
+ } |
+ |
+ /** |
+ * Determine the last known URL that this Document was displaying when it was stopped. |
+ * @return URL that the user was last known to have seen, or null if one couldn't be found. |
+ */ |
+ private String determineLastKnownUrl() { |
+ int tabId = determineTabId(); |
+ String url = mTabModel.getCurrentUrlForDocument(tabId); |
+ if (TextUtils.isEmpty(url)) url = determineInitialUrl(tabId); |
+ return url; |
+ } |
+ |
+ /** |
+ * Determine the URL that this Document was initially opened for. It can be stashed in multiple |
+ * locations because IncognitoDocumentActivities are disallowed from passing URLs in the Intent. |
+ * @param tabId ID of the Tab owned by this Activity. |
+ * @return Initial URL, or null if it couldn't be determined. |
+ */ |
+ protected String determineInitialUrl(int tabId) { |
+ String initialUrl = null; |
+ |
+ // Check if the TabModel has it. |
+ if (mTabModel != null) { |
+ initialUrl = mTabModel.getInitialUrlForDocument(tabId); |
+ } |
+ |
+ // Check if the URL was passed through the Intent. |
+ if (TextUtils.isEmpty(initialUrl) && getIntent() != null) { |
+ initialUrl = IntentHandler.getUrlFromIntent(getIntent()); |
+ } |
+ |
+ // Check the Tab's history. |
+ if (TextUtils.isEmpty(initialUrl) && mDocumentTab != null |
+ && mDocumentTab.getWebContents() != null) { |
+ NavigationEntry entry = |
+ mDocumentTab.getWebContents().getNavigationController().getEntryAtIndex(0); |
+ if (entry != null) initialUrl = entry.getOriginalUrl(); |
+ } |
+ |
+ return initialUrl; |
+ } |
+ |
+ @Override |
+ public CharSequence onCreateDescription() { |
+ return mDocumentTab != null ? mDocumentTab.getTitle() : ""; |
+ } |
+ |
+ @Override |
+ public final DocumentTab getActivityTab() { |
+ return mDocumentTab; |
+ } |
+ |
+ @Override |
+ public void onStartWithNative() { |
+ super.onStartWithNative(); |
+ handleDocumentUma(); |
+ ChromeLauncherActivity.sendExceptionCount(); |
+ } |
+ |
+ private void handleDocumentUma() { |
+ if (mRecordedStartupUma) { |
+ DocumentUma.recordStartedBy( |
+ DocumentMetricIds.STARTED_BY_ACTIVITY_BROUGHT_TO_FOREGROUND); |
+ } else { |
+ mRecordedStartupUma = true; |
+ |
+ if (getSavedInstanceState() == null) { |
+ DocumentUma.recordStartedBy(getPackageName(), getIntent()); |
+ } else { |
+ DocumentUma.recordStartedBy(DocumentMetricIds.STARTED_BY_ACTIVITY_RESTARTED); |
+ } |
+ } |
+ DocumentUma.recordInDocumentMode(true); |
+ } |
+ |
+ @Override |
+ public void onPause() { |
+ // If finishing, release all the active media players as we don't know when onStop() |
+ // will get called. |
+ super.onPause(); |
+ if (isFinishing() && mDocumentTab != null && mDocumentTab.getWebContents() != null) { |
+ mDocumentTab.getWebContents().releaseMediaPlayers(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onWindowFocusChanged(boolean hasWindowFocus) { |
+ if (hasWindowFocus) updateLastTabId(); |
+ } |
+ |
+ @Override |
+ public void onResume() { |
+ super.onResume(); |
+ |
+ if (getIntent() != null) { |
+ DocumentUtils.finishOtherTasksWithTabID( |
+ ActivityDelegate.getTabIdFromIntent(getIntent()), getTaskId()); |
+ } |
+ } |
+ |
+ @Override |
+ public void onResumeWithNative() { |
+ super.onResumeWithNative(); |
+ |
+ if (mDocumentTab != null) { |
+ PendingDocumentData pendingData = ChromeMobileApplication.getDocumentTabModelSelector() |
+ .removePendingDocumentData(ActivityDelegate.getTabIdFromIntent(getIntent())); |
+ if (pendingData != null && pendingData.url != null) { |
+ loadLastKnownUrl(pendingData); |
+ } |
+ mDocumentTab.show(TabSelectionType.FROM_USER); |
+ } |
+ } |
+ |
+ @Override |
+ protected void onDestroyInternal() { |
+ if (mToolbarHelper != null) mToolbarHelper.destroy(); |
+ |
+ super.onDestroyInternal(); |
+ } |
+ |
+ @Override |
+ public void onStopWithNative() { |
+ if (mDocumentTab != null) { |
+ mDocumentTab.setShouldPreserve(!IntentUtils.safeGetBooleanExtra(getIntent(), |
+ IntentHandler.EXTRA_APPEND_TASK, false)); |
+ mTabModel.updateEntry(getIntent(), mDocumentTab); |
+ } |
+ if (mAppMenuHandler != null) mAppMenuHandler.hideAppMenu(); |
+ super.onStopWithNative(); |
+ } |
+ |
+ @Override |
+ public SingleTabModelSelector getTabModelSelector() { |
+ return (SingleTabModelSelector) super.getTabModelSelector(); |
+ } |
+ |
+ @Override |
+ public void onConfigurationChanged(Configuration newConfig) { |
+ if (mAppMenuHandler != null) mAppMenuHandler.hideAppMenu(); |
+ super.onConfigurationChanged(newConfig); |
+ } |
+ |
+ @Override |
+ public void onOrientationChange(int orientation) { |
+ super.onOrientationChange(orientation); |
+ mToolbarHelper.onOrientationChange(); |
+ } |
+ |
+ @Override |
+ protected void onAccessibilityModeChanged(boolean enabled) { |
+ super.onAccessibilityModeChanged(enabled); |
+ mToolbarHelper.onAccessibilityStatusChanged(enabled); |
+ } |
+ |
+ private void loadLastKnownUrl(PendingDocumentData pendingData) { |
+ Intent intent = getIntent(); |
+ if (pendingData != null && pendingData.originalIntent != null) { |
+ intent = pendingData.originalIntent; |
+ } |
+ boolean isIntentChromeOrFirstParty = IntentHandler.isIntentChromeOrFirstParty(intent, |
+ getApplicationContext()); |
+ int transitionType = intent == null ? PageTransition.LINK |
+ : IntentUtils.safeGetIntExtra(intent, IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, |
+ PageTransition.LINK); |
+ if ((transitionType != PageTransition.TYPED) |
+ && (transitionType != PageTransition.LINK) |
+ && !isIntentChromeOrFirstParty) { |
+ // Only 1st party authenticated applications can set the transition type to be |
+ // anything other than TYPED for security reasons. |
+ transitionType = PageTransition.LINK; |
+ } |
+ |
+ if (transitionType == PageTransition.LINK) { |
+ transitionType |= PageTransition.FROM_API; |
+ } |
+ |
+ String url = (pendingData != null && pendingData.url != null) ? pendingData.url : |
+ determineLastKnownUrl(); |
+ |
+ LoadUrlParams loadUrlParams = new LoadUrlParams(url, transitionType); |
+ if (getIntent() != null) { |
+ loadUrlParams.setIntentReceivedTimestamp(getOnCreateTimestampUptimeMs()); |
+ } |
+ |
+ Uri referrerExtra = null; |
+ if (isIntentChromeOrFirstParty |
+ && IntentUtils.safeGetParcelableExtra(intent, Intent.EXTRA_REFERRER) != null) { |
+ referrerExtra = (Uri) IntentUtils.safeGetParcelableExtra(intent, Intent.EXTRA_REFERRER); |
+ loadUrlParams.setReferrer(new Referrer( |
+ referrerExtra.toString(), 1 /* WebReferrerPolicyDefault */)); |
+ } |
+ |
+ if (pendingData != null) { |
+ if (pendingData.postData != null) { |
+ loadUrlParams.setPostData(pendingData.postData); |
+ loadUrlParams.setLoadType(LoadURLType.BROWSER_INITIATED_HTTP_POST); |
+ } |
+ loadUrlParams.setVerbatimHeaders(pendingData.extraHeaders); |
+ loadUrlParams.setReferrer(pendingData.referrer); |
+ mDocumentTab.getTabRedirectHandler().updateIntent(pendingData.originalIntent); |
+ } else { |
+ if (getIntent() != null) { |
+ try { |
+ intent = IntentUtils.safeGetParcelableExtra(getIntent(), |
+ IntentHandler.EXTRA_ORIGINAL_INTENT); |
+ } catch (Throwable t) { |
+ // Ignore exception. |
+ } |
+ } |
+ mDocumentTab.getTabRedirectHandler().updateIntent(intent); |
+ } |
+ |
+ mDocumentTab.loadUrl(loadUrlParams); |
+ if (pendingData != null && pendingData.requestId > 0) { |
+ ServiceTabLauncher.onWebContentsForRequestAvailable( |
+ pendingData.requestId, mDocumentTab.getWebContents()); |
+ } |
+ |
+ if (getIntent() != null && IntentUtils.safeGetBooleanExtra(getIntent(), |
+ IntentHandler.EXTRA_USE_DESKTOP_USER_AGENT, false)) { |
+ // The desktop user agent can't be carried over without the Tab already having a URL, |
+ // so we're forced to set the user agent and then reload the page. |
+ mDocumentTab.setUseDesktopUserAgent(true, true); |
+ } |
+ } |
+ |
+ @Override |
+ public void terminateIncognitoSession() { |
+ ChromeMobileApplication.getDocumentTabModelSelector().getModel(true).closeAllTabs(); |
+ } |
+ |
+ private void initializeUI() { |
+ mDefaultThemeColor = isIncognito() |
+ ? getResources().getColor(R.color.incognito_primary_color) |
+ : getResources().getColor(R.color.default_primary_color); |
+ PendingDocumentData pendingData = ChromeMobileApplication.getDocumentTabModelSelector() |
+ .removePendingDocumentData(ActivityDelegate.getTabIdFromIntent(getIntent())); |
+ int tabId = determineTabId(); |
+ TabState tabState = mTabModel.getTabStateForDocument(tabId); |
+ mDocumentTab = DocumentTab.create(DocumentActivity.this, isIncognito(), |
+ getWindowAndroid(), determineLastKnownUrl(), |
+ pendingData != null ? pendingData.webContents : null, |
+ pendingData != null ? pendingData.webContentsPaused : false, tabState); |
+ if (mTabModel.isNativeInitialized()) { |
+ mTabModel.addTab(getIntent(), mDocumentTab); |
+ } else { |
+ mNeedsToBeAddedToTabModel = true; |
+ } |
+ |
+ mAppMenuHandler.addObserver(new AppMenuObserver() { |
+ @Override |
+ public void onMenuVisibilityChanged(boolean isVisible) { |
+ if (!isVisible) { |
+ mChromeAppMenuPropertiesDelegate.onMenuDismissed(); |
+ } |
+ } |
+ }); |
+ |
+ getTabModelSelector().setTab(mDocumentTab); |
+ |
+ if (!mDocumentTab.didRestoreState() || (pendingData != null && pendingData.url != null)) { |
+ if (!mDocumentTab.isCreatedWithWebContents()) { |
+ // Don't load tabs in the background on low end devices. We will call |
+ // loadLastKnownUrl() in onResumeWithNative() next time activity is resumed. |
+ int launchMode = IntentUtils.safeGetIntExtra(getIntent(), |
+ ChromeLauncherActivity.EXTRA_LAUNCH_MODE, |
+ ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND); |
+ if (SysUtils.isLowEndDevice() |
+ && launchMode == ChromeLauncherActivity.LAUNCH_MODE_AFFILIATED |
+ && pendingData != null) { |
+ // onResumeWithNative() wants pendingData.url to be non-null |
+ if (pendingData.url == null) { |
+ pendingData.url = determineLastKnownUrl(); |
+ } |
+ ChromeMobileApplication.getDocumentTabModelSelector().addPendingDocumentData( |
+ ActivityDelegate.getTabIdFromIntent(getIntent()), pendingData); |
+ // Use the URL as the document title until tab is loaded |
+ updateTaskDescription(pendingData.url, null); |
+ } else { |
+ loadLastKnownUrl(pendingData); |
+ } |
+ } |
+ mDocumentTab.setShouldPreserve(IntentUtils.safeGetBooleanExtra(getIntent(), |
+ IntentHandler.EXTRA_PRESERVE_TASK, false)); |
+ } |
+ |
+ ToolbarControlContainer controlContainer = |
+ (ToolbarControlContainer) findViewById(R.id.control_container); |
+ LayoutManagerDocument layoutDriver = new LayoutManagerDocument(getCompositorViewHolder()); |
+ initializeCompositorContent(layoutDriver, findViewById(R.id.url_bar), |
+ (ViewGroup) findViewById(android.R.id.content), controlContainer); |
+ |
+ mFindToolbarManager = new FindToolbarManager(this, getTabModelSelector(), |
+ mToolbarHelper.getContextualMenuBar() |
+ .getCustomSelectionActionModeCallback()); |
+ controlContainer.setFindToolbarManager(mFindToolbarManager); |
+ if (getContextualSearchManager() != null) { |
+ getContextualSearchManager().setFindToolbarManager(mFindToolbarManager); |
+ } |
+ |
+ mToolbarHelper.initializeControls( |
+ mFindToolbarManager, null, layoutDriver, null, null, null, null); |
+ |
+ mDocumentTab.setFullscreenManager(getFullscreenManager()); |
+ |
+ mDocumentTab.addObserver(new DocumentTabObserver() { |
+ @Override |
+ public void onPageLoadStarted(Tab tab) { |
+ // Discard startup navigation measurements when the user interfered and started the |
+ // 2nd navigation (in activity lifetime) in parallel. |
+ if (!sIsFirstPageLoadStart) { |
+ UmaUtils.setRunningApplicationStart(false); |
+ } else { |
+ sIsFirstPageLoadStart = false; |
+ } |
+ } |
+ |
+ @Override |
+ public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) { |
+ if (!didStartLoad) return; |
+ mThemeColor = tab.getWebContents().getThemeColor(mDefaultThemeColor); |
+ mIcon = null; |
+ updateTaskDescription(); |
+ } |
+ |
+ @Override |
+ protected void onFaviconReceived(Bitmap image) { |
+ super.onFaviconReceived(image); |
+ int newHeight = image.getHeight(); |
+ int newWidth = image.getWidth(); |
+ int minSize = |
+ (int) getResources().getDisplayMetrics().density * APP_ICON_MIN_SIZE_DP; |
+ if (newHeight < minSize || newWidth < minSize) return; |
+ if (mIcon != null && !mIsUsingGeneratedIcon && mIcon.getWidth() >= newWidth |
+ && mIcon.getHeight() >= newHeight) { |
+ return; |
+ } |
+ mIcon = image; |
+ mIsUsingGeneratedIcon = false; |
+ updateTaskDescription(); |
+ } |
+ |
+ @Override |
+ public void onUrlUpdated(Tab tab) { |
+ assert mDocumentTab == tab; |
+ |
+ updateTaskDescription(); |
+ mTabModel.updateEntry(getIntent(), mDocumentTab); |
+ } |
+ |
+ @Override |
+ public void onTitleUpdated(Tab tab) { |
+ super.onTitleUpdated(tab); |
+ updateTaskDescription(); |
+ } |
+ |
+ @Override |
+ public void onSSLStateUpdated(Tab tab) { |
+ int securityLevel = tab.getSecurityLevel(); |
+ if (securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_ERROR |
+ || securityLevel == ConnectionSecurityHelperSecurityLevel.SECURITY_WARNING |
+ || securityLevel |
+ == ConnectionSecurityHelperSecurityLevel.SECURITY_POLICY_WARNING) { |
+ resetThemeColorAndIcon(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onDidNavigateMainFrame(Tab tab, String url, String baseUrl, |
+ boolean isNavigationToDifferentPage, boolean isFragmentNavigation, |
+ int statusCode) { |
+ if (!isNavigationToDifferentPage) return; |
+ mIcon = null; |
+ } |
+ |
+ @Override |
+ public void onLoadStopped(Tab tab) { |
+ assert mDocumentTab == tab; |
+ |
+ updateTaskDescription(); |
+ mTabModel.updateEntry(getIntent(), mDocumentTab); |
+ } |
+ |
+ @Override |
+ public void onDidChangeThemeColor(int color) { |
+ if (color == Color.TRANSPARENT) color = mDefaultThemeColor; |
+ |
+ // Ignore any transparency value. |
+ color |= 0xFF000000; |
+ |
+ mThemeColor = Integer.valueOf(color); |
+ updateTaskDescription(); |
+ } |
+ |
+ @Override |
+ public void onDidAttachInterstitialPage(Tab tab) { |
+ resetThemeColorAndIcon(); |
+ } |
+ |
+ @Override |
+ public void onDidDetachInterstitialPage(Tab tab) { |
+ mThemeColor = tab.getWebContents().getThemeColor(mDefaultThemeColor); |
+ mIcon = null; |
+ updateTaskDescription(); |
+ } |
+ |
+ @Override |
+ public void onCrash(Tab tab, boolean sadTabShown) { |
+ int currentState = ApplicationStatus.getStateForActivity(DocumentActivity.this); |
+ if (currentState != ActivityState.STOPPED) return; |
+ |
+ if (!isTaskRoot() || IntentUtils.safeGetBooleanExtra(getIntent(), |
+ IntentHandler.EXTRA_APPEND_TASK, false)) { |
+ return; |
+ } |
+ |
+ // Finishing backgrounded Activities whose renderers have crashed allows us to |
+ // destroy them and return resources sooner than if we wait for Android to destroy |
+ // the Activities themselves. Problematically, this also removes |
+ // IncognitoDocumentActivity instances from Android's Recents menu and auto-closes |
+ // the tab. Instead, take a hit and keep the Activities alive -- Android will |
+ // eventually destroy the Activities, anyway (crbug.com/450292). |
+ if (!isIncognito()) finish(); |
+ } |
+ |
+ @Override |
+ public void onSetCoveredByChildActivity() { |
+ mTabModel.updateEntry(getIntent(), mDocumentTab); |
+ } |
+ }); |
+ |
+ removeWindowBackground(); |
+ |
+ if (mDocumentTab != null) { |
+ BandwidthReductionPreferences.launchDataReductionSSLInfoBar( |
+ DocumentActivity.this, mDocumentTab.getWebContents()); |
+ } |
+ } |
+ |
+ private void resetThemeColorAndIcon() { |
+ mThemeColor = null; |
+ mIcon = null; |
+ updateTaskDescription(); |
+ } |
+ |
+ private void updateLastTabId() { |
+ ChromeMobileApplication.getDocumentTabModelSelector().selectModel(isIncognito()); |
+ int tabId = mDocumentTab == null |
+ ? ActivityDelegate.getTabIdFromIntent(getIntent()) : mDocumentTab.getId(); |
+ mTabModel.setLastShownId(tabId); |
+ } |
+ |
+ @Override |
+ public boolean handleBackPressed() { |
+ if (mDocumentTab == null) return false; |
+ |
+ View view = ContentVideoView.getContentVideoView(); |
+ if (view != null && view.getContext() == this) { |
+ ContentVideoView.getContentVideoView().exitFullscreen(false); |
+ return true; |
+ } |
+ |
+ if (getFullscreenManager().getPersistentFullscreenMode()) { |
+ getFullscreenManager().setPersistentFullscreenMode(false); |
+ return true; |
+ } |
+ |
+ if (mDocumentTab.canGoBack()) { |
+ mDocumentTab.goBack(); |
+ } else if (!mDocumentTab.shouldPreserve()) { |
+ finishAndRemoveTask(); |
+ } else { |
+ moveTaskToBack(true); |
+ } |
+ return true; |
+ } |
+ |
+ @Override |
+ public boolean createContextualSearchTab(ContentViewCore searchContentViewCore) { |
+ NavigationEntry entry = |
+ searchContentViewCore.getWebContents().getNavigationController().getPendingEntry(); |
+ String url = entry != null |
+ ? entry.getUrl() : searchContentViewCore.getWebContents().getUrl(); |
+ PendingDocumentData documentData = new PendingDocumentData(); |
+ ChromeLauncherActivity.launchDocumentInstance(this, false, |
+ ChromeLauncherActivity.LAUNCH_MODE_FOREGROUND, url, |
+ DocumentMetricIds.STARTED_BY_CONTEXTUAL_SEARCH, |
+ PageTransition.LINK, false, documentData); |
+ return false; |
+ } |
+ |
+ @Override |
+ public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) { |
+ if (id == R.id.new_tab_menu_id) { |
+ mHandler.postDelayed(new Runnable() { |
+ @Override |
+ public void run() { |
+ launchNtp(false); |
+ } |
+ }, MENU_EXIT_ANIMATION_WAIT_MS); |
+ RecordUserAction.record("MobileMenuNewTab"); |
+ RecordUserAction.record("MobileNewTabOpened"); |
+ } else if (id == R.id.new_incognito_tab_menu_id) { |
+ // This action must be recorded before opening the incognito tab since UMA actions |
+ // are dropped when an incognito tab is open. |
+ RecordUserAction.record("MobileMenuNewIncognitoTab"); |
+ RecordUserAction.record("MobileNewTabOpened"); |
+ mHandler.postDelayed(new Runnable() { |
+ @Override |
+ public void run() { |
+ launchNtp(true); |
+ } |
+ }, MENU_EXIT_ANIMATION_WAIT_MS); |
+ } else if (id == R.id.all_bookmarks_menu_id) { |
+ if (!EnhancedBookmarkUtils.showEnhancedBookmarkIfEnabled(this)) { |
+ DocumentNewTabPage.launchBookmarksDialog(this, mDocumentTab, getTabModelSelector()); |
+ } |
+ RecordUserAction.record("MobileMenuAllBookmarks"); |
+ } else if (id == R.id.recent_tabs_menu_id) { |
+ DocumentNewTabPage.launchRecentTabsDialog(this, mDocumentTab, false); |
+ RecordUserAction.record("MobileMenuOpenTabs"); |
+ } else if (id == R.id.find_in_page_id) { |
+ mFindToolbarManager.showToolbar(); |
+ if (fromMenu) { |
+ RecordUserAction.record("MobileMenuFindInPage"); |
+ } else { |
+ RecordUserAction.record("MobileShortcutFindInPage"); |
+ } |
+ } else if (id == R.id.show_menu) { |
+ if (mToolbarHelper.isInitialized()) { |
+ mAppMenuHandler.showAppMenu(mToolbarHelper.getMenuAnchor(), true, |
+ false); |
+ } |
+ } else if (id == R.id.focus_url_bar) { |
+ if (mToolbarHelper.isInitialized()) mToolbarHelper.setUrlBarFocus(true); |
+ } else { |
+ return super.onMenuOrKeyboardAction(id, fromMenu); |
+ } |
+ return true; |
+ } |
+ |
+ @Override |
+ public boolean dispatchKeyEvent(KeyEvent event) { |
+ Boolean result = KeyboardShortcuts.dispatchKeyEvent(event, this, |
+ mToolbarHelper.isInitialized()); |
+ return result != null ? result : super.dispatchKeyEvent(event); |
+ } |
+ |
+ @Override |
+ public boolean onKeyDown(int keyCode, KeyEvent event) { |
+ if (!mToolbarHelper.isInitialized()) return false; |
+ return KeyboardShortcuts.onKeyDown(event, this, true, false) |
+ || super.onKeyDown(keyCode, event); |
+ } |
+ |
+ @Override |
+ public boolean shouldShowAppMenu() { |
+ if (mDocumentTab == null || !mToolbarHelper.isInitialized()) { |
+ return false; |
+ } |
+ |
+ return super.shouldShowAppMenu(); |
+ } |
+ |
+ private void updateTaskDescription() { |
+ if (mDocumentTab == null) { |
+ updateTaskDescription(null, null); |
+ return; |
+ } |
+ |
+ if (isNewTabPage() && !isIncognito()) { |
+ // NTP needs a new color in recents, but uses the default application title and icon; |
+ updateTaskDescription(null, null); |
+ return; |
+ } |
+ |
+ String label = mDocumentTab.getTitle(); |
+ String domain = UrlUtilities.getDomainAndRegistry(mDocumentTab.getUrl(), false); |
+ if (TextUtils.isEmpty(label)) { |
+ label = domain; |
+ } |
+ if (mIcon == null && TextUtils.isEmpty(label)) { |
+ updateTaskDescription(null, null); |
+ return; |
+ } |
+ |
+ if (isIncognito()) { |
+ mIcon = null; |
+ } else if (mIcon == null) { |
+ if (mDocumentAppIconGenerator == null) { |
+ mDocumentAppIconGenerator = new RoundedIconGenerator(this, |
+ APP_ICON_SIZE_DP, APP_ICON_SIZE_DP, |
+ APP_ICON_CORNER_RADIUS_DP, APP_ICON_DEFAULT_BACKGROUND_COLOR, |
+ APP_ICON_TEXT_SIZE_DP); |
+ } |
+ mIcon = mDocumentAppIconGenerator.generateIconForUrl(mDocumentTab.getUrl()); |
+ mIsUsingGeneratedIcon = true; |
+ } |
+ |
+ updateTaskDescription(label, mIcon); |
+ } |
+ |
+ protected int getThemeColor() { |
+ if (isIncognito()) { |
+ return mDefaultThemeColor; |
+ } else { |
+ return mThemeColor != null ? mThemeColor.intValue() : mDefaultThemeColor; |
+ } |
+ } |
+ |
+ private boolean shouldUseDefaultStatusBarColor() { |
+ return isIncognito() || mThemeColor == null || mThemeColor == mDefaultThemeColor; |
+ } |
+ |
+ protected void updateTaskDescription(String label, Bitmap icon) { |
+ int color = getThemeColor(); |
+ DocumentUtils.updateTaskDescription(this, label, icon, color, |
+ shouldUseDefaultStatusBarColor()); |
+ mToolbarHelper.setThemeColor(color); |
+ |
+ ControlContainer controlContainer = |
+ (ControlContainer) findViewById(R.id.control_container); |
+ controlContainer.getToolbarResourceAdapter().invalidate(null); |
+ } |
+ |
+ /** |
+ * @return Whether the {@link DocumentTab} this activity uses is incognito. |
+ */ |
+ protected boolean isIncognito() { |
+ return false; |
+ } |
+ |
+ /** |
+ * @return Whether this activity contains a tab that is showing the new tab page. |
+ */ |
+ boolean isNewTabPage() { |
+ String url; |
+ if (mDocumentTab == null) { |
+ // If the Tab hasn't been created yet, then we're really early in initialization. |
+ // Use a combination of the original URL from the Intent and whether or not the Tab is |
+ // retargetable to know whether or not the user navigated away from the NTP. |
+ // If the Entry doesn't exist, then the DocumentActivity never got a chance to add |
+ // itself to the TabList and is likely to be retargetable. |
+ int tabId = ActivityDelegate.getTabIdFromIntent(getIntent()); |
+ url = IntentHandler.getUrlFromIntent(getIntent()); |
+ if (mTabModel.hasEntryForTabId(tabId) && !mTabModel.isRetargetable(tabId)) return false; |
+ } else { |
+ url = mDocumentTab.getUrl(); |
+ } |
+ return NewTabPage.isNTPUrl(url); |
+ } |
+ |
+ /** |
+ * Determines whether the given class can be classified as a DocumentActivity (this includes |
+ * both regular document activity and incognito document activity). |
+ * @param className The class name to inspect. |
+ * @return Whether the className is that of a document activity. |
+ */ |
+ public static boolean isDocumentActivity(String className) { |
+ return TextUtils.equals(className, IncognitoDocumentActivity.class.getName()) |
+ || TextUtils.equals(className, DocumentActivity.class.getName()); |
+ } |
+ |
+ /** |
+ * Launch a new DocumentActivity showing the new tab page. |
+ * @param incognito Whether the new NTP should be in incognito mode. |
+ */ |
+ private void launchNtp(boolean incognito) { |
+ if (incognito && !PrefServiceBridge.getInstance().isIncognitoModeEnabled()) return; |
+ ChromeLauncherActivity.launchDocumentInstance(this, incognito, |
+ ChromeLauncherActivity.LAUNCH_MODE_RETARGET, UrlConstants.NTP_URL, |
+ DocumentMetricIds.STARTED_BY_OPTIONS_MENU, PageTransition.AUTO_TOPLEVEL, false, |
+ null); |
+ } |
+} |