Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3605)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/tab/TabChromeWebContentsDelegateAndroid.java

Issue 1358923002: Extract TabChromeWebContentsDelegateAndroid into a separate class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@chrometab10
Patch Set: Move renderer hung calls Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/tab/TabChromeWebContentsDelegateAndroid.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabChromeWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabChromeWebContentsDelegateAndroid.java
new file mode 100644
index 0000000000000000000000000000000000000000..badb1c22a75aa47a3888c91804f5ef5ff24375bd
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabChromeWebContentsDelegateAndroid.java
@@ -0,0 +1,415 @@
+// 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.tab;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.View;
+
+import org.chromium.base.Log;
+import org.chromium.base.ObserverList.RewindableIterator;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeApplication;
+import org.chromium.chrome.browser.ChromeWebContentsDelegateAndroid;
+import org.chromium.chrome.browser.RepostFormWarningDialog;
+import org.chromium.chrome.browser.document.DocumentUtils;
+import org.chromium.chrome.browser.document.DocumentWebContentsDelegate;
+import org.chromium.chrome.browser.fullscreen.FullscreenManager;
+import org.chromium.chrome.browser.media.MediaCaptureNotificationService;
+import org.chromium.chrome.browser.policy.PolicyAuditor;
+import org.chromium.chrome.browser.policy.PolicyAuditor.AuditEvent;
+import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.content_public.browser.InvalidateTypes;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.WindowOpenDisposition;
+
+/**
+ * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered
+ * {@link TabObserver}s.
+ */
+public class TabChromeWebContentsDelegateAndroid
+ extends ChromeWebContentsDelegateAndroid {
+ /** Used for logging. */
+ private static final String TAG = "WebContentsDelegate";
+
+ private final Tab mTab;
+ private final ChromeActivity mActivity;
+
+ protected Handler mHandler;
+ private final Runnable mCloseContentsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ boolean isSelected = mActivity.getTabModelSelector().getCurrentTab() == mTab;
+ mActivity.getTabModelSelector().closeTab(mTab);
+
+ // If the parent Tab belongs to another Activity, fire the Intent to bring it back.
+ if (isSelected && mTab.getParentIntent() != null
+ && mActivity.getIntent() != mTab.getParentIntent()) {
+ boolean mayLaunch = FeatureUtilities.isDocumentMode(mActivity)
+ ? isParentInAndroidOverview() : true;
+ if (mayLaunch) mActivity.startActivity(mTab.getParentIntent());
+ }
+ }
+
+ /** If the API allows it, returns whether a Task still exists for the parent Activity. */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private boolean isParentInAndroidOverview() {
+ ActivityManager activityManager =
+ (ActivityManager) mActivity.getSystemService(Context.ACTIVITY_SERVICE);
+ for (ActivityManager.AppTask task : activityManager.getAppTasks()) {
+ Intent taskIntent = DocumentUtils.getBaseIntentFromTask(task);
+ if (taskIntent != null && taskIntent.filterEquals(mTab.getParentIntent())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ public TabChromeWebContentsDelegateAndroid(Tab tab, ChromeActivity activity) {
+ mTab = tab;
+ mActivity = activity;
+
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg == null) return;
+ if (msg.what == Tab.MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD) {
+ mTab.enableFullscreenAfterLoad();
Maria 2015/09/22 23:02:57 already being handled in tab.
aurimas (slooooooooow) 2015/09/22 23:16:49 Done
+ }
+ }
+ };
+ }
+
+ @Override
+ public void onLoadProgressChanged(int progress) {
+ if (!mTab.isLoading()) return;
+ mTab.notifyLoadProgress(mTab.getProgress());
+ }
+
+ @Override
+ public void onLoadStarted() {
+ RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+ while (observers.hasNext()) {
+ observers.next().onLoadStarted(mTab);
+ }
+ }
+
+ @Override
+ public void onLoadStopped() {
+ RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+ while (observers.hasNext()) {
+ observers.next().onLoadStopped(mTab);
+ }
+ }
+
+ @Override
+ public void onUpdateUrl(String url) {
+ RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+ while (observers.hasNext()) {
+ observers.next().onUpdateUrl(mTab, url);
+ }
+ }
+
+ @Override
+ public void showRepostFormWarningDialog() {
+ mTab.resetSwipeRefreshHandler();
+ RepostFormWarningDialog warningDialog = new RepostFormWarningDialog(
+ new Runnable() {
+ @Override
+ public void run() {
+ mTab.getWebContents().getNavigationController().cancelPendingReload();
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ mTab.getWebContents().getNavigationController().continuePendingReload();
+ }
+ });
+ warningDialog.show(mActivity.getFragmentManager(), null);
+ }
+
+ @Override
+ public void toggleFullscreenModeForTab(boolean enableFullscreen) {
+ if (mTab.getFullscreenManager() != null) {
+ mTab.getFullscreenManager().setPersistentFullscreenMode(enableFullscreen);
+ }
+
+ RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+ while (observers.hasNext()) {
+ observers.next().onToggleFullscreenMode(mTab, enableFullscreen);
+ }
+ }
+
+ @Override
+ public void navigationStateChanged(int flags) {
+ if ((flags & InvalidateTypes.TAB) != 0) {
+ MediaCaptureNotificationService.updateMediaNotificationForTab(
+ mTab.getApplicationContext(), mTab.getId(), isCapturingAudio(),
+ isCapturingVideo(), mTab.getUrl());
+ }
+ if ((flags & InvalidateTypes.TITLE) != 0) {
+ // Update cached title then notify observers.
+ mTab.updateTitle();
+ }
+ if ((flags & InvalidateTypes.URL) != 0) {
+ RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+ while (observers.hasNext()) {
+ observers.next().onUrlUpdated(mTab);
+ }
+ }
+ }
+
+ @Override
+ public void visibleSSLStateChanged() {
+ RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+ while (observers.hasNext()) {
+ observers.next().onSSLStateUpdated(mTab);
+ }
+ }
+
+ @Override
+ public void webContentsCreated(WebContents sourceWebContents, long openerRenderFrameId,
+ String frameName, String targetUrl, WebContents newWebContents) {
+ RewindableIterator<TabObserver> observers = mTab.getTabObservers();
+ while (observers.hasNext()) {
+ observers.next().webContentsCreated(mTab, sourceWebContents, openerRenderFrameId,
+ frameName, targetUrl, newWebContents);
+ }
+ // The URL can't be taken from the WebContents if it's paused. Save it for later.
+ assert mWebContentsUrlMapping == null;
+ mWebContentsUrlMapping = Pair.create(newWebContents, targetUrl);
+
+ // TODO(dfalcantara): Re-remove this once crbug.com/508366 is fixed.
+ TabCreator tabCreator = mActivity.getTabCreator(mTab.isIncognito());
+
+ if (tabCreator != null && tabCreator.createsTabsAsynchronously()) {
+ DocumentWebContentsDelegate.getInstance().attachDelegate(newWebContents);
+ }
+ }
+
+ @Override
+ public void rendererUnresponsive() {
+ super.rendererUnresponsive();
+ if (mTab.getWebContents() != null) nativeOnRendererUnresponsive(mTab.getWebContents());
+ if (mTab.getFullscreenManager() == null) return;
+ mTab.mFullscreenHungRendererToken =
+ mTab.getFullscreenManager().showControlsPersistentAndClearOldToken(
+ mTab.mFullscreenHungRendererToken);
+ }
+
+ @Override
+ public void rendererResponsive() {
+ super.rendererResponsive();
+ if (mTab.getWebContents() != null) nativeOnRendererResponsive(mTab.getWebContents());
+ if (mTab.getFullscreenManager() == null) return;
+ mTab.getFullscreenManager().hideControlsPersistent(mTab.mFullscreenHungRendererToken);
+ mTab.mFullscreenHungRendererToken = FullscreenManager.INVALID_TOKEN;
+ }
+
+ @Override
+ public boolean isFullscreenForTabOrPending() {
+ return mTab.getFullscreenManager() == null
+ ? false : mTab.getFullscreenManager().getPersistentFullscreenMode();
+ }
+
+ @Override
+ public void openNewTab(String url, String extraHeaders, byte[] postData, int disposition,
+ boolean isRendererInitiated) {
+ mTab.openNewTab(url, extraHeaders, postData, disposition, true, isRendererInitiated);
+ }
+
+ private Pair<WebContents, String> mWebContentsUrlMapping;
+
+ protected TabModel getTabModel() {
+ // TODO(dfalcantara): Remove this when DocumentActivity.getTabModelSelector()
+ // can return a TabModelSelector that activateContents() can use.
+ return mActivity.getTabModelSelector().getModel(mTab.isIncognito());
+ }
+
+ @Override
+ public boolean shouldResumeRequestsForCreatedWindow() {
+ // Pause the WebContents if an Activity has to be created for it first.
+ TabCreator tabCreator = mActivity.getTabCreator(mTab.isIncognito());
+ assert tabCreator != null;
+ return !tabCreator.createsTabsAsynchronously();
+ }
+
+ @Override
+ public boolean addNewContents(WebContents sourceWebContents, WebContents webContents,
+ int disposition, Rect initialPosition, boolean userGesture) {
+ assert mWebContentsUrlMapping.first == webContents;
+
+ TabCreator tabCreator = mActivity.getTabCreator(mTab.isIncognito());
+ assert tabCreator != null;
+
+ // Grab the URL, which might not be available via the Tab.
+ String url = mWebContentsUrlMapping.second;
+ mWebContentsUrlMapping = null;
+
+ // Skip opening a new Tab if it doesn't make sense.
+ if (mTab.isClosing()) return false;
+
+ // Creating new Tabs asynchronously requires starting a new Activity to create the Tab,
+ // so the Tab returned will always be null. There's no way to know synchronously
+ // whether the Tab is created, so assume it's always successful.
+ boolean createdSuccessfully = tabCreator.createTabWithWebContents(
+ webContents, mTab.getId(), TabLaunchType.FROM_LONGPRESS_FOREGROUND, url);
+ boolean success = tabCreator.createsTabsAsynchronously() || createdSuccessfully;
+ if (success && disposition == WindowOpenDisposition.NEW_POPUP) {
+ PolicyAuditor auditor =
+ ((ChromeApplication) mTab.getApplicationContext()).getPolicyAuditor();
+ auditor.notifyAuditEvent(mTab.getApplicationContext(),
+ AuditEvent.OPEN_POPUP_URL_SUCCESS, url, "");
+ }
+
+ return success;
+ }
+
+ @Override
+ public void activateContents() {
+ boolean activityIsDestroyed = false;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ activityIsDestroyed = mActivity.isDestroyed();
+ }
+ if (activityIsDestroyed || !mTab.isInitialized()) {
+ Log.e(TAG, "Activity destroyed before calling activateContents(). Bailing out.");
+ return;
+ }
+
+ TabModel model = getTabModel();
+ int index = model.indexOf(mTab);
+ if (index == TabModel.INVALID_TAB_INDEX) return;
+ TabModelUtils.setIndex(model, index);
+ bringActivityToForeground();
+ }
+
+ /**
+ * Brings chrome's Activity to foreground, if it is not so.
+ */
+ protected void bringActivityToForeground() {
+ // This intent is sent in order to get the activity back to the foreground if it was
+ // not already. The previous call will activate the right tab in the context of the
+ // TabModel but will only show the tab to the user if Chrome was already in the
+ // foreground.
+ // The intent is getting the tabId mostly because it does not cost much to do so.
+ // When receiving the intent, the tab associated with the tabId should already be
+ // active.
+ // Note that calling only the intent in order to activate the tab is slightly slower
+ // because it will change the tab when the intent is handled, which happens after
+ // Chrome gets back to the foreground.
+ Intent newIntent = Tab.createBringTabToFrontIntent(mTab.getId());
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ mTab.getApplicationContext().startActivity(newIntent);
+ }
+
+ @Override
+ public void closeContents() {
+ // Execute outside of callback, otherwise we end up deleting the native
+ // objects in the middle of executing methods on them.
+ mHandler.removeCallbacks(mCloseContentsRunnable);
+ mHandler.post(mCloseContentsRunnable);
+ }
+
+ @Override
+ public boolean takeFocus(boolean reverse) {
+ if (reverse) {
+ View menuButton = mActivity.findViewById(R.id.menu_button);
+ if (menuButton == null || !menuButton.isShown()) {
+ menuButton = mActivity.findViewById(R.id.document_menu_button);
+ }
+ if (menuButton != null && menuButton.isShown()) {
+ return menuButton.requestFocus();
+ }
+
+ View tabSwitcherButton = mActivity.findViewById(R.id.tab_switcher_button);
+ if (tabSwitcherButton != null && tabSwitcherButton.isShown()) {
+ return tabSwitcherButton.requestFocus();
+ }
+ } else {
+ View urlBar = mActivity.findViewById(R.id.url_bar);
+ if (urlBar != null) return urlBar.requestFocus();
+ }
+ return false;
+ }
+
+ @Override
+ public void handleKeyboardEvent(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (mActivity.onKeyDown(event.getKeyCode(), event)) return;
+
+ // Handle the Escape key here (instead of in KeyboardShortcuts.java), so it doesn't
+ // interfere with other parts of the activity (e.g. the URL bar).
+ if (event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE && event.hasNoModifiers()) {
+ WebContents wc = mTab.getWebContents();
+ if (wc != null) wc.stop();
+ return;
+ }
+ }
+ handleMediaKey(event);
+ }
+
+ /**
+ * Redispatches unhandled media keys. This allows bluetooth headphones with play/pause or
+ * other buttons to function correctly.
+ */
+ @TargetApi(19)
+ private void handleMediaKey(KeyEvent e) {
+ if (Build.VERSION.SDK_INT < 19) return;
+ switch (e.getKeyCode()) {
+ case KeyEvent.KEYCODE_MUTE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ case KeyEvent.KEYCODE_MEDIA_RECORD:
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ case KeyEvent.KEYCODE_MEDIA_CLOSE:
+ case KeyEvent.KEYCODE_MEDIA_EJECT:
+ case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
+ AudioManager am = (AudioManager) mActivity.getSystemService(
+ Context.AUDIO_SERVICE);
+ am.dispatchMediaKeyEvent(e);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * @return Whether audio is being captured.
+ */
+ private boolean isCapturingAudio() {
+ return !mTab.isClosing()
+ && ChromeWebContentsDelegateAndroid.nativeIsCapturingAudio(mTab.getWebContents());
+ }
+
+ /**
+ * @return Whether video is being captured.
+ */
+ private boolean isCapturingVideo() {
+ return !mTab.isClosing()
+ && ChromeWebContentsDelegateAndroid.nativeIsCapturingVideo(mTab.getWebContents());
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698