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

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/tab/ChromeTab.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.tab;
6
7 import android.annotation.TargetApi;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.graphics.Rect;
11 import android.media.AudioManager;
12 import android.os.Build;
13 import android.os.Handler;
14 import android.os.Message;
15 import android.text.TextUtils;
16 import android.view.ActionMode;
17 import android.view.ContextMenu;
18 import android.view.KeyEvent;
19 import android.view.View;
20
21 import com.google.android.apps.chrome.R;
22
23 import org.chromium.base.Log;
24 import org.chromium.base.TraceEvent;
25 import org.chromium.base.VisibleForTesting;
26 import org.chromium.base.metrics.RecordUserAction;
27 import org.chromium.chrome.browser.ChromeActivity;
28 import org.chromium.chrome.browser.ChromeMobileApplication;
29 import org.chromium.chrome.browser.CompositorChromeActivity;
30 import org.chromium.chrome.browser.EmptyTabObserver;
31 import org.chromium.chrome.browser.FrozenNativePage;
32 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
33 import org.chromium.chrome.browser.NativePage;
34 import org.chromium.chrome.browser.Tab;
35 import org.chromium.chrome.browser.TabObserver;
36 import org.chromium.chrome.browser.TabState;
37 import org.chromium.chrome.browser.TabUma;
38 import org.chromium.chrome.browser.TabUma.TabCreationState;
39 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulator;
40 import org.chromium.chrome.browser.contextmenu.ContextMenuParams;
41 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulator;
42 import org.chromium.chrome.browser.contextualsearch.ContextualSearchTabHelper;
43 import org.chromium.chrome.browser.crash.MinidumpUploadService;
44 import org.chromium.chrome.browser.dom_distiller.ReaderModeActivityDelegate;
45 import org.chromium.chrome.browser.dom_distiller.ReaderModeManager;
46 import org.chromium.chrome.browser.download.ChromeDownloadDelegate;
47 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
48 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler.Overrid eUrlLoadingResult;
49 import org.chromium.chrome.browser.externalnav.ExternalNavigationParams;
50 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
51 import org.chromium.chrome.browser.media.MediaNotificationService;
52 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
53 import org.chromium.chrome.browser.ntp.NativePageAssassin;
54 import org.chromium.chrome.browser.ntp.NativePageFactory;
55 import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader;
56 import org.chromium.chrome.browser.policy.PolicyAuditor;
57 import org.chromium.chrome.browser.policy.PolicyAuditor.AuditEvent;
58 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
59 import org.chromium.chrome.browser.rlz.RevenueStats;
60 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
61 import org.chromium.chrome.browser.tab.BackgroundContentViewHelper.BackgroundCon tentViewDelegate;
62 import org.chromium.chrome.browser.tabmodel.TabModel;
63 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
64 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
65 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
66 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
67 import org.chromium.components.navigation_interception.InterceptNavigationDelega te;
68 import org.chromium.components.navigation_interception.NavigationParams;
69 import org.chromium.content.browser.ActivityContentVideoViewClient;
70 import org.chromium.content.browser.ContentVideoViewClient;
71 import org.chromium.content.browser.ContentViewClient;
72 import org.chromium.content.browser.ContentViewCore;
73 import org.chromium.content.browser.SelectActionMode;
74 import org.chromium.content.browser.SelectActionModeCallback.ActionHandler;
75 import org.chromium.content.browser.crypto.CipherFactory;
76 import org.chromium.content_public.browser.GestureStateListener;
77 import org.chromium.content_public.browser.InvalidateTypes;
78 import org.chromium.content_public.browser.LoadUrlParams;
79 import org.chromium.content_public.browser.NavigationController;
80 import org.chromium.content_public.browser.WebContents;
81 import org.chromium.content_public.browser.WebContentsObserver;
82 import org.chromium.content_public.common.ConsoleMessageLevel;
83 import org.chromium.content_public.common.Referrer;
84 import org.chromium.ui.WindowOpenDisposition;
85 import org.chromium.ui.base.PageTransition;
86 import org.chromium.ui.base.WindowAndroid;
87
88 import java.util.Locale;
89
90 /**
91 * A representation of a Tab for Chrome. This manages wrapping a WebContents and interacting with
92 * most of Chromium while representing a consistent version of web content to th e front end.
93 */
94 public class ChromeTab extends Tab {
95 public static final int NTP_TAB_ID = -2;
96
97 private static final String TAG = "ChromeTab";
98
99 // URL didFailLoad error code. Should match the value in net_error_list.h.
100 public static final int BLOCKED_BY_ADMINISTRATOR = -22;
101
102 public static final String PAGESPEED_PASSTHROUGH_HEADER =
103 "X-PSA-Client-Options: v=1,m=1\nCache-Control: no-cache";
104
105 private static final int MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD = 1;
106
107 /** The maximum amount of time to wait for a page to load before entering fu llscreen. -1 means
108 * wait until the page finishes loading. */
109 private static final long MAX_FULLSCREEN_LOAD_DELAY_MS = 3000;
110
111 private ReaderModeManager mReaderModeManager;
112
113 private TabRedirectHandler mTabRedirectHandler;
114
115 private ChromeDownloadDelegate mDownloadDelegate;
116
117 private boolean mIsFullscreenWaitingForLoad = false;
118 private ExternalNavigationHandler.OverrideUrlLoadingResult mLastOverrideUrlL oadingResult =
119 ExternalNavigationHandler.OverrideUrlLoadingResult.NO_OVERRIDE;
120
121 /**
122 * Whether didCommitProvisionalLoadForFrame() hasn't yet been called for the current native page
123 * (page A). To decrease latency, we show native pages in both loadUrl() and
124 * didCommitProvisionalLoadForFrame(). However, we mustn't show a new native page (page B) in
125 * loadUrl() if the current native page hasn't yet been committed. Otherwise , we'll show each
126 * page twice (A, B, A, B): the first two times in loadUrl(), the second two times in
127 * didCommitProvisionalLoadForFrame().
128 */
129 private boolean mIsNativePageCommitPending;
130
131 protected final ChromeActivity mActivity;
132
133 private WebContentsObserver mWebContentsObserver;
134
135 private Handler mHandler;
136
137 private final Runnable mCloseContentsRunnable = new Runnable() {
138 @Override
139 public void run() {
140 mActivity.getTabModelSelector().closeTab(ChromeTab.this);
141 }
142 };
143
144 /**
145 * The data reduction proxy was in use on the last page load if true.
146 */
147 protected boolean mUsedSpdyProxy;
148
149 /**
150 * The data reduction proxy was in pass through mode on the last page load i f true.
151 */
152 protected boolean mUsedSpdyProxyWithPassthrough;
153
154 /**
155 * The last page load had request headers indicating that the data reduction proxy should
156 * be put in pass through mode, if true.
157 */
158 private boolean mLastPageLoadHasSpdyProxyPassthroughHeaders;
159
160 /**
161 * Listens to gesture events fired by the ContentViewCore.
162 */
163 private GestureStateListener mGestureStateListener;
164
165 /**
166 * The background content view helper which loads the original page in backg round content view.
167 */
168 private BackgroundContentViewHelper mBackgroundContentViewHelper;
169
170 /**
171 * The load progress of swapped in content view at the time of swap.
172 */
173 private int mLoadProgressAtViewSwapInTime;
174
175 /**
176 * Whether forward history should be cleared after navigation is committed.
177 */
178 private boolean mClearAllForwardHistoryRequired;
179
180 private boolean mShouldClearRedirectHistoryForTabClobbering;
181
182 /**
183 * Basic constructor. This is hidden, so that explicitly named factory metho ds are used to
184 * create tabs. initialize() needs to be called afterwards to complete the s econd level
185 * initialization.
186 * @param creationState State in which the tab is created, needed to initial ize TabUma
187 * accounting. When null, TabUma will not be initialize d.
188 * @param frozenState TabState that was saved when the Tab was last persiste d to storage.
189 */
190 protected ChromeTab(
191 int id, ChromeActivity activity, boolean incognito, WindowAndroid na tiveWindow,
192 TabLaunchType type, int parentId, TabCreationState creationState,
193 TabState frozenState) {
194 super(id, parentId, incognito, activity, nativeWindow, type, frozenState );
195
196 if (frozenState == null) {
197 assert type != TabLaunchType.FROM_RESTORE
198 && creationState != TabCreationState.FROZEN_ON_RESTORE;
199 } else {
200 assert type == TabLaunchType.FROM_RESTORE
201 && creationState == TabCreationState.FROZEN_ON_RESTORE;
202 }
203
204 addObserver(mTabObserver);
205 mActivity = activity;
206 mHandler = new Handler() {
207 @Override
208 public void handleMessage(Message msg) {
209 if (msg == null) return;
210 if (msg.what == MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD) {
211 enableFullscreenAfterLoad();
212 }
213 }
214 };
215 setContentViewClient(createContentViewClient());
216 if (mActivity != null && creationState != null) {
217 setTabUma(new TabUma(
218 this, creationState, mActivity.getTabModelSelector().getMode l(incognito)));
219 }
220
221 if (incognito) {
222 CipherFactory.getInstance().triggerKeyGeneration();
223 }
224
225 mReaderModeManager = new ReaderModeManager(this, activity);
226 RevenueStats.getInstance().tabCreated(this);
227
228 mTabRedirectHandler = new TabRedirectHandler(activity);
229
230 ContextualSearchTabHelper.createForTab(this);
231 if (nativeWindow != null) ThumbnailTabHelper.createForTab(this);
232 }
233
234 /**
235 * Creates a minimal {@link ChromeTab} for testing. Do not use outside testi ng.
236 *
237 * @param id The id of the tab.
238 * @param incognito Whether the tab is incognito.
239 */
240 @VisibleForTesting
241 public ChromeTab(int id, boolean incognito) {
242 super(id, incognito, null, null);
243 mActivity = null;
244 mTabRedirectHandler = new TabRedirectHandler(null);
245 }
246
247 /**
248 * Creates a fresh tab. initialize() needs to be called afterwards to comple te the second level
249 * initialization.
250 * @param initiallyHidden true iff the tab being created is initially in bac kground
251 */
252 public static ChromeTab createLiveTab(int id, ChromeActivity activity, boole an incognito,
253 WindowAndroid nativeWindow, TabLaunchType type, int parentId, boolea n initiallyHidden) {
254 return new ChromeTab(id, activity, incognito, nativeWindow, type, parent Id,
255 initiallyHidden ? TabCreationState.LIVE_IN_BACKGROUND :
256 TabCreationState.LIVE_IN_FOREGROUND, null);
257 }
258
259 /**
260 * Creates a new, "frozen" tab from a saved state. This can be used for back ground tabs restored
261 * on cold start that should be loaded when switched to. initialize() needs to be called
262 * afterwards to complete the second level initialization.
263 */
264 public static ChromeTab createFrozenTabFromState(
265 int id, ChromeActivity activity, boolean incognito,
266 WindowAndroid nativeWindow, int parentId, TabState state) {
267 assert state != null;
268 return new ChromeTab(id, activity, incognito, nativeWindow,
269 TabLaunchType.FROM_RESTORE, parentId, TabCreationState.FROZEN_ON _RESTORE,
270 state);
271 }
272
273 /**
274 * Creates a new tab to be loaded lazily. This can be used for tabs opened i n the background
275 * that should be loaded when switched to. initialize() needs to be called a fterwards to
276 * complete the second level initialization.
277 */
278 public static ChromeTab createTabForLazyLoad(ChromeActivity activity, boolea n incognito,
279 WindowAndroid nativeWindow, TabLaunchType type, int parentId,
280 LoadUrlParams loadUrlParams) {
281 ChromeTab tab = new ChromeTab(
282 INVALID_TAB_ID, activity, incognito, nativeWindow, type, parentI d,
283 TabCreationState.FROZEN_FOR_LAZY_LOAD, null);
284 tab.setPendingLoadParams(loadUrlParams);
285 return tab;
286 }
287
288 public static ChromeTab fromTab(Tab tab) {
289 return (ChromeTab) tab;
290 }
291
292 /**
293 * Initializes the ChromeTab after construction with an existing ContentView Core.
294 */
295 @Override
296 protected void internalInit() {
297 super.internalInit();
298 if (mBackgroundContentViewHelper == null) {
299 BackgroundContentViewDelegate delegate = new BackgroundContentViewDe legate() {
300 @Override
301 public void onBackgroundViewReady(
302 ContentViewCore cvc, boolean didStartLoad, boolean didFi nishLoad,
303 int progress) {
304 WebContents previewWebContents = getWebContents();
305 swapContentViewCore(cvc, false, didStartLoad, didFinishLoad) ;
306 mLoadProgressAtViewSwapInTime = progress;
307 mBackgroundContentViewHelper.unloadAndDeleteWebContents(prev iewWebContents);
308
309 // Enter to fullscreen.
310 mHandler.removeMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD) ;
311 mHandler.sendEmptyMessageDelayed(
312 MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD, MAX_FULLSCREEN_ LOAD_DELAY_MS);
313 updateFullscreenEnabledState();
314 }
315
316 @Override
317 public void onLoadProgressChanged(int progress) {
318 notifyLoadProgress(getProgress());
319 }
320 };
321 mBackgroundContentViewHelper = new BackgroundContentViewHelper(
322 getWindowAndroid(), this, delegate);
323 }
324 }
325
326 /**
327 * Remember if the last load used the data reduction proxy, and if so,
328 * also remember if it used pass through mode.
329 */
330 private void maybeSetDataReductionProxyUsed() {
331 // Ignore internal URLs.
332 String url = getUrl();
333 if (url != null && url.toLowerCase(Locale.US).startsWith("chrome://")) {
334 return;
335 }
336 mUsedSpdyProxy = false;
337 mUsedSpdyProxyWithPassthrough = false;
338 if (isSpdyProxyEnabledForUrl(url)) {
339 mUsedSpdyProxy = true;
340 if (mLastPageLoadHasSpdyProxyPassthroughHeaders) {
341 mLastPageLoadHasSpdyProxyPassthroughHeaders = false;
342 mUsedSpdyProxyWithPassthrough = true;
343 }
344 }
345 }
346
347 @Override
348 protected void openNewTab(
349 LoadUrlParams params, TabLaunchType launchType, Tab parentTab, boole an incognito) {
350 mActivity.getTabModelSelector().openNewTab(params, launchType, parentTab , incognito);
351 }
352
353 @Override
354 protected TabChromeWebContentsDelegateAndroid createWebContentsDelegate() {
355 return new TabChromeWebContentsDelegateAndroidImpl();
356 }
357
358 /**
359 * An implementation for this tab's web contents delegate.
360 */
361 public class TabChromeWebContentsDelegateAndroidImpl
362 extends TabChromeWebContentsDelegateAndroid {
363 /**
364 * This method is meant to be overridden by DocumentTab because the
365 * TabModelSelector returned by the activity is not correct.
366 * TODO(dfalcantara): remove this when DocumentActivity.getTabModelSelec tor()
367 * will return the right TabModelSelector.
368 */
369 protected TabModel getTabModel() {
370 return mActivity.getTabModelSelector().getModel(isIncognito());
371 }
372
373 @Override
374 public boolean addNewContents(WebContents sourceWebContents, WebContents webContents,
375 int disposition, Rect initialPosition, boolean userGesture) {
376 if (isClosing()) return false;
377
378 // TODO(johnme): Open tabs in same order as Chrome.
379 Tab tab = mActivity.getTabCreator(isIncognito()).createTabWithWebCon tents(
380 webContents, getId(), TabLaunchType.FROM_LONGPRESS_FOREGROUN D);
381
382 if (tab == null) return false;
383
384 if (disposition == WindowOpenDisposition.NEW_POPUP) {
385 PolicyAuditor auditor =
386 ((ChromeMobileApplication) getApplicationContext()).getP olicyAuditor();
387 auditor.notifyAuditEvent(getApplicationContext(), AuditEvent.OPE N_POPUP_URL_SUCCESS,
388 tab.getUrl(), "");
389 }
390
391 return true;
392 }
393
394 @Override
395 public void activateContents() {
396 boolean activityIsDestroyed = false;
397 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
398 activityIsDestroyed = mActivity.isDestroyed();
399 }
400 if (activityIsDestroyed || !isInitialized()) {
401 Log.e(TAG, "Activity destroyed before calling activateContents() . Bailing out.");
402 return;
403 }
404
405 TabModel model = getTabModel();
406 int index = model.indexOf(ChromeTab.this);
407 if (index == TabModel.INVALID_TAB_INDEX) return;
408
409 TabModelUtils.setIndex(model, index);
410
411 // This intent is sent in order to get the activity back to the fore ground if it was
412 // not already. The previous call will activate the right tab in the context of the
413 // TabModel but will only show the tab to the user if Chrome was alr eady in the
414 // foreground.
415 // The intent is getting the tabId mostly because it does not cost m uch to do so.
416 // When receiving the intent, the tab associated with the tabId shou ld already be
417 // active.
418 // Note that calling only the intent in order to activate the tab is slightly slower
419 // because it will change the tab when the intent is handled, which happens after
420 // Chrome gets back to the foreground.
421 Intent newIntent = new Intent();
422 newIntent.setAction(Intent.ACTION_MAIN);
423 newIntent.setPackage(mActivity.getPackageName());
424 newIntent.putExtra(TabOpenType.BRING_TAB_TO_FRONT.name(),
425 ChromeTab.this.getId());
426
427 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
428
429 getApplicationContext().startActivity(newIntent);
430 }
431
432 @Override
433 public void navigationStateChanged(int flags) {
434 if ((flags & InvalidateTypes.TAB) != 0) {
435 MediaNotificationService.updateMediaNotificationForTab(
436 getApplicationContext(), getId(), isCapturingAudio(),
437 isCapturingVideo(), hasAudibleAudio(), getUrl());
438 }
439 super.navigationStateChanged(flags);
440 }
441
442 @Override
443 public void onLoadProgressChanged(int progress) {
444 if (!isLoading()) return;
445 if (progress >= mLoadProgressAtViewSwapInTime) mLoadProgressAtViewSw apInTime = 0;
446 notifyLoadProgress(getProgress());
447 }
448
449 @Override
450 public void closeContents() {
451 // Execute outside of callback, otherwise we end up deleting the nat ive
452 // objects in the middle of executing methods on them.
453 mHandler.removeCallbacks(mCloseContentsRunnable);
454 mHandler.post(mCloseContentsRunnable);
455 }
456
457 @Override
458 public boolean takeFocus(boolean reverse) {
459 if (reverse) {
460 View menuButton = mActivity.findViewById(R.id.menu_button);
461 if (menuButton == null || !menuButton.isShown()) {
462 menuButton = mActivity.findViewById(R.id.document_menu_butto n);
463 }
464 if (menuButton != null && menuButton.isShown()) {
465 return menuButton.requestFocus();
466 }
467
468 View tabSwitcherButton = mActivity.findViewById(R.id.tab_switche r_button);
469 if (tabSwitcherButton != null && tabSwitcherButton.isShown()) {
470 return tabSwitcherButton.requestFocus();
471 }
472 } else {
473 View urlBar = mActivity.findViewById(R.id.url_bar);
474 if (urlBar != null) return urlBar.requestFocus();
475 }
476 return false;
477 }
478
479 @Override
480 public void handleKeyboardEvent(KeyEvent event) {
481 if (event.getAction() == KeyEvent.ACTION_DOWN) {
482 if (mActivity.onKeyDown(event.getKeyCode(), event)) return;
483
484 // Handle the Escape key here (instead of in KeyboardShortcuts.j ava), so it doesn't
485 // interfere with other parts of the activity (e.g. the URL bar) .
486 if (event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE && event.hasNo Modifiers()) {
487 WebContents wc = getWebContents();
488 if (wc != null) wc.stop();
489 return;
490 }
491 }
492 handleMediaKey(event);
493 }
494
495 /**
496 * Redispatches unhandled media keys. This allows bluetooth headphones w ith play/pause or
497 * other buttons to function correctly.
498 */
499 @TargetApi(19)
500 private void handleMediaKey(KeyEvent e) {
501 if (Build.VERSION.SDK_INT < 19) return;
502 switch (e.getKeyCode()) {
503 case KeyEvent.KEYCODE_MUTE:
504 case KeyEvent.KEYCODE_HEADSETHOOK:
505 case KeyEvent.KEYCODE_MEDIA_PLAY:
506 case KeyEvent.KEYCODE_MEDIA_PAUSE:
507 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
508 case KeyEvent.KEYCODE_MEDIA_STOP:
509 case KeyEvent.KEYCODE_MEDIA_NEXT:
510 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
511 case KeyEvent.KEYCODE_MEDIA_REWIND:
512 case KeyEvent.KEYCODE_MEDIA_RECORD:
513 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
514 case KeyEvent.KEYCODE_MEDIA_CLOSE:
515 case KeyEvent.KEYCODE_MEDIA_EJECT:
516 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
517 AudioManager am = (AudioManager) mActivity.getSystemService(
518 Context.AUDIO_SERVICE);
519 am.dispatchMediaKeyEvent(e);
520 break;
521 default:
522 break;
523 }
524 }
525
526 /**
527 * @return Whether audio is being captured.
528 */
529 private boolean isCapturingAudio() {
530 return !isClosing() && super.nativeIsCapturingAudio(getWebContents() );
531 }
532
533 /**
534 * @return Whether video is being captured.
535 */
536 private boolean isCapturingVideo() {
537 return !isClosing() && super.nativeIsCapturingVideo(getWebContents() );
538 }
539
540 /**
541 * @return Whether audio is being played.
542 */
543 private boolean hasAudibleAudio() {
544 return !isClosing() && super.nativeHasAudibleAudio(getWebContents()) ;
545 }
546
547 }
548
549 /**
550 * Check whether the context menu download should be intercepted.
551 *
552 * @param url URL to be downloaded.
553 * @return whether the download should be intercepted.
554 */
555 protected boolean shouldInterceptContextMenuDownload(String url) {
556 return mDownloadDelegate.shouldInterceptContextMenuDownload(url);
557 }
558
559 private class ChromeTabChromeContextMenuItemDelegate extends TabChromeContex tMenuItemDelegate {
560 private boolean mIsImage;
561 private boolean mIsVideo;
562
563 public void setParamsInfo(boolean isImage, boolean isVideo) {
564 mIsImage = isImage;
565 mIsVideo = isVideo;
566 }
567
568 @Override
569 public boolean isIncognitoSupported() {
570 return PrefServiceBridge.getInstance().isIncognitoModeEnabled();
571 }
572
573 @Override
574 public boolean canLoadOriginalImage() {
575 return mUsedSpdyProxy && !mUsedSpdyProxyWithPassthrough;
576 }
577
578 @Override
579 public boolean startDownload(String url, boolean isLink) {
580 if (isLink) {
581 RecordUserAction.record("MobileContextMenuDownloadLink");
582 if (shouldInterceptContextMenuDownload(url)) {
583 return false;
584 }
585 } else if (mIsImage) {
586 RecordUserAction.record("MobileContextMenuDownloadImage");
587 } else if (mIsVideo) {
588 RecordUserAction.record("MobileContextMenuDownloadVideo");
589 }
590 return true;
591 }
592
593 @Override
594 public void onSaveToClipboard(String text, boolean isUrl) {
595 if (isUrl) {
596 RecordUserAction.record("MobileContextMenuCopyLinkAddress");
597 } else {
598 RecordUserAction.record("MobileContextMenuCopyLinkText");
599 }
600 super.onSaveToClipboard(text, isUrl);
601 }
602
603 @Override
604 public void onSaveImageToClipboard(String url) {
605 RecordUserAction.record("MobileContextMenuSaveImage");
606 super.onSaveImageToClipboard(url);
607 }
608
609 @Override
610 public void onOpenInNewTab(String url, Referrer referrer) {
611 RecordUserAction.record("MobileContextMenuOpenLinkInNewTab");
612 RecordUserAction.record("MobileNewTabOpened");
613 LoadUrlParams loadUrlParams = new LoadUrlParams(url);
614 loadUrlParams.setReferrer(referrer);
615 mActivity.getTabModelSelector().openNewTab(loadUrlParams,
616 TabLaunchType.FROM_LONGPRESS_BACKGROUND, ChromeTab.this, isI ncognito());
617 }
618
619 @Override
620 public void onOpenInNewIncognitoTab(String url) {
621 RecordUserAction.record("MobileContextMenuOpenLinkInIncognito");
622 RecordUserAction.record("MobileNewTabOpened");
623 mActivity.getTabModelSelector().openNewTab(new LoadUrlParams(url),
624 TabLaunchType.FROM_LONGPRESS_FOREGROUND, ChromeTab.this, tru e);
625 }
626
627 @Override
628 public void onOpenImageUrl(String url, Referrer referrer) {
629 RecordUserAction.record("MobileContextMenuViewImage");
630 super.onOpenImageUrl(url, referrer);
631 }
632
633 @Override
634 public void onOpenImageInNewTab(String url, Referrer referrer) {
635 boolean useOriginal = isSpdyProxyEnabledForUrl(url);
636 RecordUserAction.record("MobileContextMenuOpenImageInNewTab");
637 if (useOriginal) {
638 RecordUserAction.record("MobileContextMenuOpenOriginalImageInNew Tab");
639 }
640
641 LoadUrlParams loadUrlParams = new LoadUrlParams(url);
642 loadUrlParams.setVerbatimHeaders(useOriginal ? PAGESPEED_PASSTHROUGH _HEADER : null);
643 loadUrlParams.setReferrer(referrer);
644 mActivity.getTabModelSelector().openNewTab(loadUrlParams,
645 TabLaunchType.FROM_LONGPRESS_BACKGROUND, ChromeTab.this, isI ncognito());
646 }
647
648 @Override
649 public void onSearchByImageInNewTab() {
650 RecordUserAction.record("MobileContextMenuSearchByImage");
651 super.onSearchByImageInNewTab();
652 }
653 }
654
655 /**
656 * This class is solely to track UMA stats. When we upstream UMA stats we c an remove this.
657 */
658 private static class ChromeTabChromeContextMenuPopulator extends ChromeConte xtMenuPopulator {
659 private final ChromeTabChromeContextMenuItemDelegate mDelegate;
660
661 public ChromeTabChromeContextMenuPopulator(
662 ChromeTabChromeContextMenuItemDelegate delegate) {
663 super(delegate);
664
665 mDelegate = delegate;
666 }
667
668 @Override
669 public void buildContextMenu(ContextMenu menu, Context context,
670 ContextMenuParams params) {
671 if (params.isAnchor()) {
672 RecordUserAction.record("MobileContextMenuLink");
673 } else if (params.isImage()) {
674 RecordUserAction.record("MobileContextMenuImage");
675 } else if (params.isSelectedText()) {
676 RecordUserAction.record("MobileContextMenuText");
677 } else if (params.isVideo()) {
678 RecordUserAction.record("MobileContextMenuVideo");
679 }
680
681 mDelegate.setParamsInfo(params.isImage(), params.isVideo());
682 super.buildContextMenu(menu, context, params);
683 }
684 }
685
686 @Override
687 protected ContextMenuPopulator createContextMenuPopulator() {
688 return new ChromeTabChromeContextMenuPopulator(
689 new ChromeTabChromeContextMenuItemDelegate());
690 }
691
692 /** @return The {@link BackgroundContentViewHelper} associated with the curr ent tab. */
693 public BackgroundContentViewHelper getBackgroundContentViewHelper() {
694 return mBackgroundContentViewHelper;
695 }
696
697 @VisibleForTesting
698 public void setViewClientForTesting(ContentViewClient client) {
699 setContentViewClient(client);
700 }
701
702 @VisibleForTesting
703 public ContentViewClient getViewClientForTesting() {
704 return getContentViewClient();
705 }
706
707 private ContentViewClient createContentViewClient() {
708 return new TabContentViewClient() {
709 @Override
710 public void onBackgroundColorChanged(int color) {
711 ChromeTab.this.onBackgroundColorChanged(color);
712 }
713
714 @Override
715 public void onOffsetsForFullscreenChanged(
716 float topControlsOffsetY, float contentOffsetY, float overdr awBottomHeight) {
717 onOffsetsChanged(topControlsOffsetY, contentOffsetY, overdrawBot tomHeight,
718 isShowingSadTab());
719 }
720
721 @Override
722 public void performWebSearch(String searchQuery) {
723 if (TextUtils.isEmpty(searchQuery)) return;
724 String url = TemplateUrlService.getInstance().getUrlForSearchQue ry(searchQuery);
725 String headers = GeolocationHeader.getGeoHeader(getApplicationCo ntext(), url,
726 isIncognito());
727
728 LoadUrlParams loadUrlParams = new LoadUrlParams(url);
729 loadUrlParams.setVerbatimHeaders(headers);
730 loadUrlParams.setTransitionType(PageTransition.GENERATED);
731 mActivity.getTabModelSelector().openNewTab(loadUrlParams,
732 TabLaunchType.FROM_LONGPRESS_FOREGROUND, ChromeTab.this, isIncognito());
733 }
734
735 @Override
736 public boolean doesPerformWebSearch() {
737 return true;
738 }
739
740 @Override
741 public SelectActionMode startActionMode(
742 View view, ActionHandler actionHandler, boolean floating) {
743 if (floating) return null;
744 ChromeSelectActionModeCallback callback =
745 new ChromeSelectActionModeCallback(view.getContext(), ac tionHandler);
746 ActionMode actionMode = view.startActionMode(callback);
747 return actionMode != null ? new SelectActionMode(actionMode) : n ull;
748 }
749
750 @Override
751 public boolean supportsFloatingActionMode() {
752 return false;
753 }
754
755 @Override
756 public ContentVideoViewClient getContentVideoViewClient() {
757 return new ActivityContentVideoViewClient(mActivity) {
758 @Override
759 public void enterFullscreenVideo(View view) {
760 super.enterFullscreenVideo(view);
761 FullscreenManager fullscreenManager = getFullscreenManag er();
762 if (fullscreenManager != null) {
763 fullscreenManager.setOverlayVideoMode(true);
764 // Disable double tap for video.
765 if (getContentViewCore() != null) {
766 getContentViewCore().updateDoubleTapSupport(fals e);
767 }
768 }
769 }
770
771 @Override
772 public void exitFullscreenVideo() {
773 FullscreenManager fullscreenManager = getFullscreenManag er();
774 if (fullscreenManager != null) {
775 fullscreenManager.setOverlayVideoMode(false);
776 // Disable double tap for video.
777 if (getContentViewCore() != null) {
778 getContentViewCore().updateDoubleTapSupport(true );
779 }
780 }
781 super.exitFullscreenVideo();
782 }
783 };
784 }
785 };
786 }
787
788 private WebContentsObserver createWebContentsObserver(WebContents webContent s) {
789 return new WebContentsObserver(webContents) {
790 @Override
791 public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
792 PolicyAuditor auditor =
793 ((ChromeMobileApplication) getApplicationContext()).getP olicyAuditor();
794 auditor.notifyAuditEvent(
795 getApplicationContext(), AuditEvent.OPEN_URL_SUCCESS, va lidatedUrl, "");
796 }
797
798 @Override
799 public void didFailLoad(boolean isProvisionalLoad,
800 boolean isMainFrame, int errorCode, String description, Stri ng failingUrl) {
801 PolicyAuditor auditor =
802 ((ChromeMobileApplication) getApplicationContext()).getP olicyAuditor();
803 auditor.notifyAuditEvent(getApplicationContext(), AuditEvent.OPE N_URL_FAILURE,
804 failingUrl, description);
805 if (errorCode == BLOCKED_BY_ADMINISTRATOR) {
806 auditor.notifyAuditEvent(
807 getApplicationContext(), AuditEvent.OPEN_URL_BLOCKED , failingUrl, "");
808 }
809 }
810
811 @Override
812 public void didCommitProvisionalLoadForFrame(
813 long frameId, boolean isMainFrame, String url, int transitio nType) {
814 if (!isMainFrame) return;
815
816 mIsNativePageCommitPending = false;
817 boolean isReload = (transitionType == PageTransition.RELOAD);
818 if (!maybeShowNativePage(url, isReload)) {
819 showRenderedPage();
820 }
821
822 if (!mBackgroundContentViewHelper.hasPendingBackgroundPage()) {
823 mHandler.removeMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD) ;
824 mHandler.sendEmptyMessageDelayed(
825 MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD, MAX_FULLSCREEN_ LOAD_DELAY_MS);
826 updateFullscreenEnabledState();
827 }
828
829 // http://crbug/426679 : if navigation is canceled due to intent handling, we want
830 // to go back to the last committed entry index which was saved before the
831 // navigation, and remove the empty entries from the navigation history.
832 if (mClearAllForwardHistoryRequired && getWebContents() != null) {
833 NavigationController navigationController =
834 getWebContents().getNavigationController();
835 int lastCommittedEntryIndex = getLastCommittedEntryIndex();
836 while (navigationController.canGoForward()) {
837 boolean ret = navigationController.removeEntryAtIndex(
838 lastCommittedEntryIndex + 1);
839 assert ret;
840 }
841 } else if (mShouldClearRedirectHistoryForTabClobbering
842 && getWebContents() != null) {
843 // http://crbug/479056: Even if we clobber the current tab, we want to remove
844 // redirect history to be consistent.
845 NavigationController navigationController =
846 getWebContents().getNavigationController();
847 int indexBeforeRedirection = mTabRedirectHandler
848 .getLastCommittedEntryIndexBeforeStartingNavigation( );
849 int lastCommittedEntryIndex = getLastCommittedEntryIndex();
850 for (int i = lastCommittedEntryIndex - 1; i > indexBeforeRed irection; --i) {
851 boolean ret = navigationController.removeEntryAtIndex(i) ;
852 assert ret;
853 }
854 }
855 mClearAllForwardHistoryRequired = false;
856 mShouldClearRedirectHistoryForTabClobbering = false;
857 }
858
859 @Override
860 public void didAttachInterstitialPage() {
861 PolicyAuditor auditor =
862 ((ChromeMobileApplication) getApplicationContext()).getP olicyAuditor();
863 auditor.notifyCertificateFailure(getWebContents(), getApplicatio nContext());
864 }
865
866 @Override
867 public void didDetachInterstitialPage() {
868 if (!maybeShowNativePage(getUrl(), false)) {
869 showRenderedPage();
870 }
871 }
872
873 @Override
874 public void destroy() {
875 MediaNotificationService.updateMediaNotificationForTab(
876 getApplicationContext(), getId(), false, false, false, g etUrl());
877 super.destroy();
878 }
879 };
880 }
881
882 @Override
883 protected void didStartPageLoad(String validatedUrl, boolean showingErrorPag e) {
884 if (mBackgroundContentViewHelper.isPageSwappingInProgress()) {
885 mLoadProgressAtViewSwapInTime = 0;
886 }
887
888 mIsFullscreenWaitingForLoad = !DomDistillerUrlUtils.isDistilledPage(vali datedUrl);
889 mLoadProgressAtViewSwapInTime = 0;
890
891 super.didStartPageLoad(validatedUrl, showingErrorPage);
892 }
893
894 @Override
895 protected void didFinishPageLoad() {
896 // We should not mark finished if we have pending background page to swa p in.
897 // We'll instead call didFinishPageLoad after the background page is swa pped in.
898 if (mBackgroundContentViewHelper.hasPendingBackgroundPage()) return;
899
900 mLoadProgressAtViewSwapInTime = 0;
901
902 super.didFinishPageLoad();
903
904 // Handle the case where we were pre-renderered and the enable fullscree n message was
905 // never enqueued.
906 if (mIsFullscreenWaitingForLoad
907 && !mHandler.hasMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD)
908 && !mBackgroundContentViewHelper.hasPendingBackgroundPage()) {
909 mHandler.sendEmptyMessageDelayed(
910 MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD, MAX_FULLSCREEN_LOAD_DEL AY_MS);
911 }
912
913 maybeSetDataReductionProxyUsed();
914 }
915
916 @Override
917 protected void didFailPageLoad(int errorCode) {
918 mLoadProgressAtViewSwapInTime = 0;
919 cancelEnableFullscreenLoadDelay();
920 super.didFailPageLoad(errorCode);
921 updateFullscreenEnabledState();
922 }
923
924 private void cancelEnableFullscreenLoadDelay() {
925 mHandler.removeMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD);
926 mIsFullscreenWaitingForLoad = false;
927 }
928
929 /**
930 * Removes the enable fullscreen runnable from the UI queue and runs it imme diately.
931 */
932 @VisibleForTesting
933 public void processEnableFullscreenRunnableForTest() {
934 if (mHandler.hasMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD)) {
935 mHandler.removeMessages(MSG_ID_ENABLE_FULLSCREEN_AFTER_LOAD);
936 enableFullscreenAfterLoad();
937 }
938 }
939
940 private void enableFullscreenAfterLoad() {
941 if (!mIsFullscreenWaitingForLoad) return;
942
943 mIsFullscreenWaitingForLoad = false;
944 updateFullscreenEnabledState();
945 }
946
947 @Override
948 protected boolean isHidingTopControlsEnabled() {
949 return super.isHidingTopControlsEnabled() && !mIsFullscreenWaitingForLo ad;
950 }
951
952 @Override
953 protected void handleTabCrash() {
954 super.handleTabCrash();
955
956 // Update the most recent minidump file with the logcat. Doing this asyn chronously
957 // adds a race condition in the case of multiple simultaneously renderer crashses
958 // but because the data will be the same for all of them it is innocuous . We can
959 // attempt to do this regardless of whether it was a foreground tab in t he event
960 // that it's a real crash and not just android killing the tab.
961 Context context = getApplicationContext();
962 Intent intent = MinidumpUploadService.createFindAndUploadLastCrashIntent (context);
963 context.startService(intent);
964 RecordUserAction.record("MobileBreakpadUploadAttempt");
965 }
966
967 @Override
968 protected void setContentViewCore(ContentViewCore cvc) {
969 try {
970 TraceEvent.begin("ChromeTab.setContentViewCore");
971 super.setContentViewCore(cvc);
972 mWebContentsObserver = createWebContentsObserver(cvc.getWebContents( ));
973
974 mDownloadDelegate = new ChromeDownloadDelegate(mActivity,
975 mActivity.getTabModelSelector(), this);
976 cvc.setDownloadDelegate(mDownloadDelegate);
977 setInterceptNavigationDelegate(new InterceptNavigationDelegateImpl() );
978
979 if (mGestureStateListener == null) mGestureStateListener = createGes tureStateListener();
980 cvc.addGestureStateListener(mGestureStateListener);
981 } finally {
982 TraceEvent.end("ChromeTab.setContentViewCore");
983 }
984 }
985
986 private GestureStateListener createGestureStateListener() {
987 return new GestureStateListener() {
988 @Override
989 public void onFlingStartGesture(int vx, int vy, int scrollOffsetY, i nt scrollExtentY) {
990 onScrollingStateChanged();
991 }
992
993 @Override
994 public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) {
995 onScrollingStateChanged();
996 }
997
998 @Override
999 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) {
1000 onScrollingStateChanged();
1001 }
1002
1003 @Override
1004 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
1005 onScrollingStateChanged();
1006 }
1007
1008 private void onScrollingStateChanged() {
1009 FullscreenManager fullscreenManager = getFullscreenManager();
1010 if (fullscreenManager == null) return;
1011 fullscreenManager.onContentViewScrollingStateChanged(
1012 getContentViewCore() != null && getContentViewCore().isS crollInProgress());
1013 }
1014 };
1015 }
1016
1017 @Override
1018 protected void destroyContentViewCoreInternal(ContentViewCore cvc) {
1019 super.destroyContentViewCoreInternal(cvc);
1020
1021 if (mGestureStateListener != null) {
1022 cvc.removeGestureStateListener(mGestureStateListener);
1023 }
1024 if (mWebContentsObserver != null) {
1025 mWebContentsObserver.destroy();
1026 }
1027 mWebContentsObserver = null;
1028 }
1029
1030 @Override
1031 public void stopLoading() {
1032 super.stopLoading();
1033 mBackgroundContentViewHelper.stopLoading();
1034 }
1035
1036 @Override
1037 public int getProgress() {
1038 if (mBackgroundContentViewHelper.hasPendingBackgroundPage()
1039 || mBackgroundContentViewHelper.isPageSwappingInProgress()) {
1040 return mBackgroundContentViewHelper.getProgress();
1041 }
1042 int currentTabProgress = super.getProgress();
1043 return Math.max(mLoadProgressAtViewSwapInTime, currentTabProgress);
1044 }
1045
1046 /**
1047 * @return Whether the tab is ready to display or it should be faded in as i t loads.
1048 */
1049 public boolean shouldStall() {
1050 return (isFrozen() || needsReload())
1051 && !NativePageFactory.isNativePageUrl(getUrl(), isIncognito());
1052 }
1053
1054 @Override
1055 protected void showInternal(TabSelectionType type) {
1056 super.showInternal(type);
1057
1058 // If the NativePage was frozen while in the background (see NativePageA ssassin),
1059 // recreate the NativePage now.
1060 if (getNativePage() instanceof FrozenNativePage) {
1061 maybeShowNativePage(getUrl(), true);
1062 }
1063 NativePageAssassin.getInstance().tabShown(this);
1064 }
1065
1066 @Override
1067 protected void restoreIfNeededInternal() {
1068 super.restoreIfNeededInternal();
1069 }
1070
1071 @Override
1072 protected void hideInternal() {
1073 super.hideInternal();
1074 cancelEnableFullscreenLoadDelay();
1075
1076 // Allow this tab's NativePage to be frozen if it stays hidden for a whi le.
1077 NativePageAssassin.getInstance().tabHidden(this);
1078
1079 mTabRedirectHandler.clear();
1080 }
1081
1082 /**
1083 * Checks if spdy proxy is enabled for input url.
1084 * @param url Input url to check for spdy setting.
1085 * @return true if url is enabled for spdy proxy.
1086 */
1087 protected boolean isSpdyProxyEnabledForUrl(String url) {
1088 if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled ()
1089 && url != null && !url.toLowerCase(Locale.US).startsWith("https: //")
1090 && !isIncognito()) {
1091 return true;
1092 }
1093 return false;
1094 }
1095
1096 @Override
1097 public void goBack() {
1098 mBackgroundContentViewHelper.recordBack();
1099 super.goBack();
1100 }
1101
1102 @Override
1103 public void reload() {
1104 mBackgroundContentViewHelper.recordReload();
1105 super.reload();
1106 }
1107
1108 @Override
1109 public void reloadIgnoringCache() {
1110 mBackgroundContentViewHelper.recordReload();
1111 super.reloadIgnoringCache();
1112 }
1113
1114 @Override
1115 public int loadUrl(LoadUrlParams params) {
1116 try {
1117 TraceEvent.begin("ChromeTab.loadUrl");
1118
1119 // The data reduction proxy can only be set to pass through mode via loading an image in
1120 // a new tab. We squirrel away whether pass through mode was set, an d check it in:
1121 // @see ChromeWebContentsDelegateAndroid#onLoadStopped()
1122 mLastPageLoadHasSpdyProxyPassthroughHeaders = false;
1123 if (TextUtils.equals(params.getVerbatimHeaders(), PAGESPEED_PASSTHRO UGH_HEADER)) {
1124 mLastPageLoadHasSpdyProxyPassthroughHeaders = true;
1125 }
1126
1127 // TODO(tedchoc): When showing the android NTP, delay the call to na tiveLoadUrl until
1128 // the android view has entirely rendered.
1129 if (!mIsNativePageCommitPending) {
1130 mIsNativePageCommitPending = maybeShowNativePage(params.getUrl() , false);
1131 }
1132
1133 return super.loadUrl(params);
1134 } finally {
1135 TraceEvent.end("ChromeTab.loadUrl");
1136 }
1137 }
1138
1139 @VisibleForTesting
1140 public AuthenticatorNavigationInterceptor getAuthenticatorHelper() {
1141 return getInterceptNavigationDelegate().mAuthenticatorHelper;
1142 }
1143
1144 /**
1145 * @return the TabRedirectHandler for the tab.
1146 */
1147 public TabRedirectHandler getTabRedirectHandler() {
1148 return mTabRedirectHandler;
1149 }
1150
1151 private class InterceptNavigationDelegateImpl implements InterceptNavigation Delegate {
1152 final ExternalNavigationHandler mExternalNavHandler;
1153 final AuthenticatorNavigationInterceptor mAuthenticatorHelper;
1154
1155 InterceptNavigationDelegateImpl() {
1156 mExternalNavHandler = new ExternalNavigationHandler(mActivity);
1157
1158 mAuthenticatorHelper = ((ChromeMobileApplication) getApplicationCont ext())
1159 .createAuthenticatorNavigationInterceptor(ChromeTab.this);
1160 }
1161
1162 public boolean shouldIgnoreNewTab(String url, boolean incognito) {
1163 if (mAuthenticatorHelper != null && mAuthenticatorHelper.handleAuthe nticatorUrl(url)) {
1164 return true;
1165 }
1166
1167 ExternalNavigationParams params = new ExternalNavigationParams.Build er(url, incognito)
1168 .setTab(ChromeTab.this)
1169 .setOpenInNewTab(true)
1170 .build();
1171 return mExternalNavHandler.shouldOverrideUrlLoading(params)
1172 != ExternalNavigationHandler.OverrideUrlLoadingResult.NO_OVE RRIDE;
1173 }
1174
1175 @Override
1176 public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
1177 final String url = navigationParams.url;
1178
1179 if (mAuthenticatorHelper != null && mAuthenticatorHelper.handleAuthe nticatorUrl(url)) {
1180 return true;
1181 }
1182
1183 mTabRedirectHandler.updateNewUrlLoading(navigationParams.pageTransit ionType,
1184 navigationParams.isRedirect,
1185 navigationParams.hasUserGesture || navigationParams.hasUserG estureCarryover,
1186 mActivity.getLastUserInteractionTime(), getLastCommittedEntr yIndex());
1187 final boolean shouldCloseTab = shouldCloseContentsOnOverrideUrlLoadi ngAndLaunchIntent();
1188 boolean isInitialTabLaunchInBackground =
1189 getLaunchType() == TabLaunchType.FROM_LONGPRESS_BACKGROUND & & shouldCloseTab;
1190 // http://crbug.com/448977: If a new tab is closed by this overridin g, we should open an
1191 // Intent in a new tab when Chrome receives it again.
1192 ExternalNavigationParams params = new ExternalNavigationParams.Build er(
1193 url, isIncognito(), getReferrerUrl(), navigationParams.pageT ransitionType,
1194 navigationParams.isRedirect)
1195 .setTab(ChromeTab.this)
1196 .setApplicationMustBeInForeground(true)
1197 .setRedirectHandler(mTabRedirectHandler)
1198 .setOpenInNewTab(shouldCloseTab)
1199 .setIsBackgroundTabNavigation(isHidden() && !isInitialTabLau nchInBackground)
1200 .setIsMainFrame(navigationParams.isMainFrame)
1201 .setNeedsToCloseTabAfterIncognitoDialog(shouldCloseTab
1202 && navigationParams.isMainFrame)
1203 .build();
1204 ExternalNavigationHandler.OverrideUrlLoadingResult result =
1205 mExternalNavHandler.shouldOverrideUrlLoading(params);
1206 mLastOverrideUrlLoadingResult = result;
1207 switch (result) {
1208 case OVERRIDE_WITH_EXTERNAL_INTENT:
1209 assert mExternalNavHandler.canExternalAppHandleUrl(url);
1210 if (navigationParams.isMainFrame) {
1211 onOverrideUrlLoadingAndLaunchIntent();
1212 }
1213 return true;
1214 case OVERRIDE_WITH_CLOBBERING_TAB:
1215 mShouldClearRedirectHistoryForTabClobbering = true;
1216 return true;
1217 case OVERRIDE_WITH_INCOGNITO_MODE:
1218 if (!shouldCloseTab && navigationParams.isMainFrame) {
1219 onOverrideUrlLoadingAndLaunchIntent();
1220 }
1221 return true;
1222 case NO_OVERRIDE:
1223 default:
1224 if (navigationParams.isExternalProtocol) {
1225 logBlockedNavigationToDevToolsConsole(url);
1226 return true;
1227 }
1228 return false;
1229 }
1230 }
1231
1232 private void logBlockedNavigationToDevToolsConsole(String url) {
1233 int resId = mExternalNavHandler.canExternalAppHandleUrl(url)
1234 ? R.string.blocked_navigation_warning
1235 : R.string.unreachable_navigation_warning;
1236 getWebContents().addMessageToDevToolsConsole(
1237 ConsoleMessageLevel.WARNING, getApplicationContext().getStri ng(resId, url));
1238 }
1239
1240 private String getReferrerUrl() {
1241 // At this point, ContentView#getCurrentUrl() has already changed to
1242 // be the URL to be loaded, so we need to check the last navigation entry
1243 // instead. Note that for browser-initiated navigations, we rely on the
1244 // mLastLoadUrlAddress check bailing early.
1245 if (getWebContents() != null) {
1246 return getWebContents().getNavigationController()
1247 .getOriginalUrlForVisibleNavigationEntry();
1248 } else {
1249 return null;
1250 }
1251 }
1252 }
1253
1254 @Override
1255 protected boolean shouldIgnoreNewTab(String url, boolean incognito) {
1256 InterceptNavigationDelegateImpl delegate = getInterceptNavigationDelegat e();
1257 return delegate != null && delegate.shouldIgnoreNewTab(url, incognito);
1258 }
1259
1260 private int getLastCommittedEntryIndex() {
1261 if (getWebContents() == null) return -1;
1262 return getWebContents().getNavigationController().getLastCommittedEntryI ndex();
1263 }
1264
1265 /**
1266 * @return A potential fallback texture id to use when trying to draw this t ab.
1267 */
1268 public int getFallbackTextureId() {
1269 return INVALID_TAB_ID;
1270 }
1271
1272 private boolean shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent() {
1273 if (getWebContents() == null) return false;
1274 if (!getWebContents().getNavigationController().canGoToOffset(0)) return true;
1275
1276 // http://crbug/415948 : if the last committed entry index which was sav ed before this
1277 // navigation is invalid, it means that this navigation is the first one since this tab was
1278 // created.
1279 // In such case, we would like to close this tab.
1280 if (mTabRedirectHandler.isOnNavigation()) {
1281 return mTabRedirectHandler.getLastCommittedEntryIndexBeforeStartingN avigation()
1282 == TabRedirectHandler.INVALID_ENTRY_INDEX;
1283 }
1284 return false;
1285 }
1286
1287 /**
1288 * Called when Chrome decides to override URL loading and show an intent pic ker.
1289 */
1290 protected void onOverrideUrlLoadingAndLaunchIntent() {
1291 if (getWebContents() == null) return;
1292
1293 // Before leaving Chrome, close the empty child tab.
1294 // If a new tab is created through JavaScript open to load this
1295 // url, we would like to close it as we will load this url in a
1296 // different Activity.
1297 if (shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent()) {
1298 getChromeWebContentsDelegateAndroid().closeContents();
1299 } else if (mTabRedirectHandler.isOnNavigation()) {
1300 int lastCommittedEntryIndexBeforeNavigation =
1301 mTabRedirectHandler.getLastCommittedEntryIndexBeforeStarting Navigation();
1302 if (getLastCommittedEntryIndex() > lastCommittedEntryIndexBeforeNavi gation) {
1303 // http://crbug/426679 : we want to go back to the last committe d entry index which
1304 // was saved before this navigation, and remove the empty entrie s from the
1305 // navigation history.
1306 mClearAllForwardHistoryRequired = true;
1307 getWebContents().getNavigationController().goToNavigationIndex(
1308 lastCommittedEntryIndexBeforeNavigation);
1309 }
1310 }
1311 }
1312
1313 @Override
1314 protected InterceptNavigationDelegateImpl getInterceptNavigationDelegate() {
1315 return (InterceptNavigationDelegateImpl) super.getInterceptNavigationDel egate();
1316 }
1317
1318 @Override
1319 public void setClosing(boolean closing) {
1320 if (closing) mBackgroundContentViewHelper.recordTabClose();
1321 super.setClosing(closing);
1322 }
1323
1324 /**
1325 * @return The reader mode manager for this tab that handles UI events for r eader mode.
1326 */
1327 public ReaderModeManager getReaderModeManager() {
1328 return mReaderModeManager;
1329 }
1330
1331 public ReaderModeActivityDelegate getReaderModeActivityDelegate() {
1332 if (!(mActivity instanceof CompositorChromeActivity)) return null;
1333 return ((CompositorChromeActivity) mActivity).getReaderModeActivityDeleg ate();
1334 }
1335
1336 /**
1337 * Shows a native page for url if it's a valid chrome-native URL. Otherwise, does nothing.
1338 * @param url The url of the current navigation.
1339 * @param isReload Whether the current navigation is a reload.
1340 * @return True, if a native page was displayed for url.
1341 */
1342 private boolean maybeShowNativePage(String url, boolean isReload) {
1343 NativePage candidateForReuse = isReload ? null : getNativePage();
1344 NativePage nativePage = NativePageFactory.createNativePageForURL(url, ca ndidateForReuse,
1345 this, mActivity.getTabModelSelector(), mActivity);
1346 if (nativePage != null) {
1347 showNativePage(nativePage);
1348 notifyPageTitleChanged();
1349 notifyFaviconChanged();
1350 return true;
1351 }
1352 return false;
1353 }
1354
1355 // TODO(dtrainor): Port more methods to the observer.
1356 private final TabObserver mTabObserver = new EmptyTabObserver() {
1357 @Override
1358 public void onContentChanged(Tab tab) {
1359 mLoadProgressAtViewSwapInTime = 0;
1360 }
1361
1362 @Override
1363 public void onSSLStateUpdated(Tab tab) {
1364 PolicyAuditor auditor =
1365 ((ChromeMobileApplication) getApplicationContext()).getPolic yAuditor();
1366 auditor.notifyCertificateFailure(getWebContents(), getApplicationCon text());
1367 updateFullscreenEnabledState();
1368 }
1369
1370 @Override
1371 public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
1372 if (!didStartLoad) return;
1373
1374 String url = tab.getUrl();
1375 // Simulate the PAGE_LOAD_STARTED notification that we did not get.
1376 didStartPageLoad(url, false);
1377 if (didFinishLoad) {
1378 // Simulate the PAGE_LOAD_FINISHED notification that we did not get.
1379 didFinishPageLoad();
1380 }
1381 }
1382 };
1383
1384 @VisibleForTesting
1385 public OverrideUrlLoadingResult getLastOverrideUrlLoadingResultForTests() {
1386 return mLastOverrideUrlLoadingResult;
1387 }
1388 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698