1 // Copyright 2012 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; 6 7 import android.app.Activity; 8 import android.content.Context; 9 import android.graphics.Bitmap; 10 import android.graphics.Color; 11 import android.view.ContextMenu; 12 import android.view.View; 13 14 import org.chromium.base.CalledByNative; 15 import org.chromium.base.ObserverList; 16 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuItemDelegate; 17 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulator; 18 import org.chromium.chrome.browser.contextmenu.ContextMenuParams; 19 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulator; 20 import org.chromium.chrome.browser.contextmenu.ContextMenuPopulatorWrapper; 21 import org.chromium.chrome.browser.contextmenu.EmptyChromeContextMenuItemDelegate; 22 import org.chromium.chrome.browser.infobar.AutoLoginProcessor; 23 import org.chromium.chrome.browser.infobar.InfoBarContainer; 24 import org.chromium.chrome.browser.profiles.Profile; 25 import org.chromium.chrome.browser.ui.toolbar.ToolbarModelSecurityLevel; 26 import org.chromium.content.browser.ContentView; 27 import org.chromium.content.browser.ContentViewClient; 28 import org.chromium.content.browser.ContentViewCore; 29 import org.chromium.content.browser.NavigationClient; 30 import org.chromium.content.browser.NavigationHistory; 31 import org.chromium.content.browser.PageInfo; 32 import org.chromium.content.browser.WebContentsObserverAndroid; 33 import org.chromium.ui.base.Clipboard; 34 import org.chromium.ui.base.WindowAndroid; 35 36 import java.util.concurrent.atomic.AtomicInteger; 37 38 /** 39 * The basic Java representation of a tab. Contains and manages a {@link ContentView}. 40 * 41 * TabBase provides common functionality for ChromiumTestshell's Tab as well as Chrome on Android's 42 * tab. It's intended to be extended both on Java and C++, with ownership managed by the subclass. 43 * Because of the inner-workings of JNI, the subclass is responsible for constructing the native 44 * subclass which in turn constructs TabAndroid (the native counterpart to TabBase) which in turn 45 * sets the native pointer for TabBase. The same is true for destruction. The Java subclass must be 46 * destroyed which will call into the native subclass and finally lead to the destruction of the 47 * parent classes. 48 */ 49 public abstract class TabBase implements NavigationClient { 50 public static final int INVALID_TAB_ID = -1; 51 52 /** Used for automatically generating tab ids. */ 53 private static final AtomicInteger sIdCounter = new AtomicInteger(); 54 55 private long mNativeTabAndroid; 56 57 /** Unique id of this tab (within its container). */ 58 private final int mId; 59 60 /** Whether or not this tab is an incognito tab. */ 61 private final boolean mIncognito; 62 63 /** An Application {@link Context}. Unlike {@link #mContext}, this is the only one that is 64 * publicly exposed to help prevent leaking the {@link Activity}. */ 65 private final Context mApplicationContext; 66 67 /** The {@link Context} used to create {@link View}s and other Android components. Unlike 68 * {@link #mApplicationContext}, this is not publicly exposed to help prevent leaking the 69 * {@link Activity}. */ 70 private final Context mContext; 71 72 /** Gives {@link TabBase} a way to interact with the Android window. */ 73 private final WindowAndroid mWindowAndroid; 74 75 /** The current native page (e.g. chrome-native://newtab), or {@code null} if there is none. */ 76 private NativePage mNativePage; 77 78 /** The {@link ContentView} showing the current page or {@code null} if the tab is frozen. */ 79 private ContentView mContentView; 80 81 /** InfoBar container to show InfoBars for this tab. */ 82 private InfoBarContainer mInfoBarContainer; 83 84 /** The sync id of the TabBase if session sync is enabled. */ 85 private int mSyncId; 86 87 /** 88 * The {@link ContentViewCore} for the current page, provided for convenience. This always 89 * equals {@link ContentView#getContentViewCore()}, or {@code null} if mContentView is 90 * {@code null}. 91 */ 92 private ContentViewCore mContentViewCore; 93 94 /** 95 * A list of TabBase observers. These are used to broadcast TabBase events to listeners. 96 */ 97 private final ObserverList<TabObserver> mObservers = new ObserverList<TabObserver>(); 98 99 // Content layer Observers and Delegates 100 private ContentViewClient mContentViewClient; 101 private WebContentsObserverAndroid mWebContentsObserver; 102 private TabBaseChromeWebContentsDelegateAndroid mWebContentsDelegate; 103 104 /** 105 * A default {@link ChromeContextMenuItemDelegate} that supports some of the context menu 106 * functionality. 107 */ 108 protected class TabBaseChromeContextMenuItemDelegate 109 extends EmptyChromeContextMenuItemDelegate { 110 private final Clipboard mClipboard; 111 112 /** 113 * Builds a {@link TabBaseChromeContextMenuItemDelegate} instance. 114 */ TabBaseChromeContextMenuItemDelegate()115 public TabBaseChromeContextMenuItemDelegate() { 116 mClipboard = new Clipboard(getApplicationContext()); 117 } 118 119 @Override isIncognito()120 public boolean isIncognito() { 121 return mIncognito; 122 } 123 124 @Override onSaveToClipboard(String text, boolean isUrl)125 public void onSaveToClipboard(String text, boolean isUrl) { 126 mClipboard.setText(text, text); 127 } 128 129 @Override onSaveImageToClipboard(String url)130 public void onSaveImageToClipboard(String url) { 131 mClipboard.setHTMLText("<img src=\"" + url + "\">", url, url); 132 } 133 } 134 135 /** 136 * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered 137 * {@link TabObserver}s. Meant to be overridden by subclasses. 138 */ 139 public class TabBaseChromeWebContentsDelegateAndroid 140 extends ChromeWebContentsDelegateAndroid { 141 @Override onLoadProgressChanged(int progress)142 public void onLoadProgressChanged(int progress) { 143 for (TabObserver observer : mObservers) { 144 observer.onLoadProgressChanged(TabBase.this, progress); 145 } 146 } 147 148 @Override onUpdateUrl(String url)149 public void onUpdateUrl(String url) { 150 for (TabObserver observer : mObservers) observer.onUpdateUrl(TabBase.this, url); 151 } 152 153 @Override showRepostFormWarningDialog(final ContentViewCore contentViewCore)154 public void showRepostFormWarningDialog(final ContentViewCore contentViewCore) { 155 RepostFormWarningDialog warningDialog = new RepostFormWarningDialog( 156 new Runnable() { 157 @Override 158 public void run() { 159 contentViewCore.cancelPendingReload(); 160 } 161 }, new Runnable() { 162 @Override 163 public void run() { 164 contentViewCore.continuePendingReload(); 165 } 166 }); 167 Activity activity = (Activity) mContext; 168 warningDialog.show(activity.getFragmentManager(), null); 169 } 170 171 @Override toggleFullscreenModeForTab(boolean enableFullscreen)172 public void toggleFullscreenModeForTab(boolean enableFullscreen) { 173 for (TabObserver observer : mObservers) { 174 observer.onToggleFullscreenMode(TabBase.this, enableFullscreen); 175 } 176 } 177 } 178 179 private class TabBaseContextMenuPopulator extends ContextMenuPopulatorWrapper { TabBaseContextMenuPopulator(ContextMenuPopulator populator)180 public TabBaseContextMenuPopulator(ContextMenuPopulator populator) { 181 super(populator); 182 } 183 184 @Override buildContextMenu(ContextMenu menu, Context context, ContextMenuParams params)185 public void buildContextMenu(ContextMenu menu, Context context, ContextMenuParams params) { 186 super.buildContextMenu(menu, context, params); 187 for (TabObserver observer : mObservers) observer.onContextMenuShown(TabBase.this, menu); 188 } 189 } 190 191 private class TabBaseWebContentsObserverAndroid extends WebContentsObserverAndroid { TabBaseWebContentsObserverAndroid(ContentViewCore contentViewCore)192 public TabBaseWebContentsObserverAndroid(ContentViewCore contentViewCore) { 193 super(contentViewCore); 194 } 195 196 @Override navigationEntryCommitted()197 public void navigationEntryCommitted() { 198 if (getNativePage() != null) { 199 pushNativePageStateToNavigationEntry(); 200 } 201 } 202 203 @Override didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode, String description, String failingUrl)204 public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode, 205 String description, String failingUrl) { 206 for (TabObserver observer : mObservers) { 207 observer.onDidFailLoad(TabBase.this, isProvisionalLoad, isMainFrame, errorCode, 208 description, failingUrl); 209 } 210 } 211 } 212 213 /** 214 * Creates an instance of a {@link TabBase} with no id. 215 * @param incognito Whether or not this tab is incognito. 216 * @param context An instance of a {@link Context}. 217 * @param window An instance of a {@link WindowAndroid}. 218 */ TabBase(boolean incognito, Context context, WindowAndroid window)219 public TabBase(boolean incognito, Context context, WindowAndroid window) { 220 this(INVALID_TAB_ID, incognito, context, window); 221 } 222 223 /** 224 * Creates an instance of a {@link TabBase}. 225 * @param id The id this tab should be identified with. 226 * @param incognito Whether or not this tab is incognito. 227 * @param context An instance of a {@link Context}. 228 * @param window An instance of a {@link WindowAndroid}. 229 */ TabBase(int id, boolean incognito, Context context, WindowAndroid window)230 public TabBase(int id, boolean incognito, Context context, WindowAndroid window) { 231 // We need a valid Activity Context to build the ContentView with. 232 assert context == null || context instanceof Activity; 233 234 mId = generateValidId(id); 235 mIncognito = incognito; 236 // TODO(dtrainor): Only store application context here. 237 mContext = context; 238 mApplicationContext = context != null ? context.getApplicationContext() : null; 239 mWindowAndroid = window; 240 } 241 242 /** 243 * Adds a {@link TabObserver} to be notified on {@link TabBase} changes. 244 * @param observer The {@link TabObserver} to add. 245 */ addObserver(TabObserver observer)246 public final void addObserver(TabObserver observer) { 247 mObservers.addObserver(observer); 248 } 249 250 /** 251 * Removes a {@link TabObserver}. 252 * @param observer The {@link TabObserver} to remove. 253 */ removeObserver(TabObserver observer)254 public final void removeObserver(TabObserver observer) { 255 mObservers.removeObserver(observer); 256 } 257 258 /** 259 * @return Whether or not this tab has a previous navigation entry. 260 */ canGoBack()261 public boolean canGoBack() { 262 return mContentViewCore != null && mContentViewCore.canGoBack(); 263 } 264 265 /** 266 * @return Whether or not this tab has a navigation entry after the current one. 267 */ canGoForward()268 public boolean canGoForward() { 269 return mContentViewCore != null && mContentViewCore.canGoForward(); 270 } 271 272 /** 273 * Goes to the navigation entry before the current one. 274 */ goBack()275 public void goBack() { 276 if (mContentViewCore != null) mContentViewCore.goBack(); 277 } 278 279 /** 280 * Goes to the navigation entry after the current one. 281 */ goForward()282 public void goForward() { 283 if (mContentViewCore != null) mContentViewCore.goForward(); 284 } 285 286 @Override getDirectedNavigationHistory(boolean isForward, int itemLimit)287 public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) { 288 if (mContentViewCore != null) { 289 return mContentViewCore.getDirectedNavigationHistory(isForward, itemLimit); 290 } else { 291 return new NavigationHistory(); 292 } 293 } 294 295 @Override goToNavigationIndex(int index)296 public void goToNavigationIndex(int index) { 297 if (mContentViewCore != null) mContentViewCore.goToNavigationIndex(index); 298 } 299 300 /** 301 * Loads the current navigation if there is a pending lazy load (after tab restore). 302 */ loadIfNecessary()303 public void loadIfNecessary() { 304 if (mContentViewCore != null) mContentViewCore.loadIfNecessary(); 305 } 306 307 /** 308 * Requests the current navigation to be loaded upon the next call to loadIfNecessary(). 309 */ requestRestoreLoad()310 protected void requestRestoreLoad() { 311 if (mContentViewCore != null) mContentViewCore.requestRestoreLoad(); 312 } 313 314 /** 315 * @return Whether or not the {@link TabBase} is currently showing an interstitial page, such as 316 * a bad HTTPS page. 317 */ isShowingInterstitialPage()318 public boolean isShowingInterstitialPage() { 319 ContentViewCore contentViewCore = getContentViewCore(); 320 return contentViewCore != null && contentViewCore.isShowingInterstitialPage(); 321 } 322 323 /** 324 * @return Whether or not the tab has something valid to render. 325 */ isReady()326 public boolean isReady() { 327 return mNativePage != null || (mContentViewCore != null && mContentViewCore.isReady()); 328 } 329 330 /** 331 * @return The {@link View} displaying the current page in the tab. This might be a 332 * {@link ContentView} but could potentially be any instance of {@link View}. This can 333 * be {@code null}, if the tab is frozen or being initialized or destroyed. 334 */ getView()335 public View getView() { 336 PageInfo pageInfo = getPageInfo(); 337 return pageInfo != null ? pageInfo.getView() : null; 338 } 339 340 /** 341 * @return The width of the content of this tab. Can be 0 if there is no content. 342 */ getWidth()343 public int getWidth() { 344 View view = getView(); 345 return view != null ? view.getWidth() : 0; 346 } 347 348 /** 349 * @return The height of the content of this tab. Can be 0 if there is no content. 350 */ getHeight()351 public int getHeight() { 352 View view = getView(); 353 return view != null ? view.getHeight() : 0; 354 } 355 356 /** 357 * @return The application {@link Context} associated with this tab. 358 */ getApplicationContext()359 protected Context getApplicationContext() { 360 return mApplicationContext; 361 } 362 363 /** 364 * @return The infobar container. 365 */ getInfoBarContainer()366 public final InfoBarContainer getInfoBarContainer() { 367 return mInfoBarContainer; 368 } 369 370 /** 371 * Create an {@code AutoLoginProcessor} to decide how to handle login 372 * requests. 373 */ createAutoLoginProcessor()374 protected abstract AutoLoginProcessor createAutoLoginProcessor(); 375 376 /** 377 * Prints the current page. 378 * 379 * @return Whether the printing process is started successfully. 380 **/ print()381 public boolean print() { 382 assert mNativeTabAndroid != 0; 383 return nativePrint(mNativeTabAndroid); 384 } 385 386 /** 387 * Reloads the current page content if it is a {@link ContentView}. 388 */ reload()389 public void reload() { 390 // TODO(dtrainor): Should we try to rebuild the ContentView if it's frozen? 391 if (mContentViewCore != null) mContentViewCore.reload(true); 392 } 393 394 /** 395 * Reloads the current page content if it is a {@link ContentView}. 396 * This version ignores the cache and reloads from the network. 397 */ reloadIgnoringCache()398 public void reloadIgnoringCache() { 399 if (mContentViewCore != null) mContentViewCore.reloadIgnoringCache(true); 400 } 401 402 /** Stop the current navigation. */ stopLoading()403 public void stopLoading() { 404 if (mContentViewCore != null) mContentViewCore.stopLoading(); 405 } 406 407 /** 408 * @return The background color of the tab. 409 */ getBackgroundColor()410 public int getBackgroundColor() { 411 return getPageInfo() != null ? getPageInfo().getBackgroundColor() : Color.WHITE; 412 } 413 414 /** 415 * @return The profile associated with this tab. 416 */ getProfile()417 public Profile getProfile() { 418 if (mNativeTabAndroid == 0) return null; 419 return nativeGetProfileAndroid(mNativeTabAndroid); 420 } 421 422 /** 423 * @return The id representing this tab. 424 */ 425 @CalledByNative getId()426 public int getId() { 427 return mId; 428 } 429 430 /** 431 * @return Whether or not this tab is incognito. 432 */ isIncognito()433 public boolean isIncognito() { 434 return mIncognito; 435 } 436 437 /** 438 * @return The {@link ContentView} associated with the current page, or {@code null} if 439 * there is no current page or the current page is displayed using something besides a 440 * {@link ContentView}. 441 */ getContentView()442 public ContentView getContentView() { 443 return mNativePage == null ? mContentView : null; 444 } 445 446 /** 447 * @return The {@link ContentViewCore} associated with the current page, or {@code null} if 448 * there is no current page or the current page is displayed using something besides a 449 * {@link ContentView}. 450 */ getContentViewCore()451 public ContentViewCore getContentViewCore() { 452 return mNativePage == null ? mContentViewCore : null; 453 } 454 455 /** 456 * @return A {@link PageInfo} describing the current page. This is always not {@code null} 457 * except during initialization, destruction, and when the tab is frozen. 458 */ getPageInfo()459 public PageInfo getPageInfo() { 460 return mNativePage != null ? mNativePage : mContentView; 461 } 462 463 /** 464 * @return The {@link NativePage} associated with the current page, or {@code null} if there is 465 * no current page or the current page is displayed using something besides 466 * {@link NativePage}. 467 */ getNativePage()468 public NativePage getNativePage() { 469 return mNativePage; 470 } 471 472 /** 473 * @return Whether or not the {@link TabBase} represents a {@link NativePage}. 474 */ isNativePage()475 public boolean isNativePage() { 476 return mNativePage != null; 477 } 478 479 /** 480 * Set whether or not the {@link ContentViewCore} should be using a desktop user agent for the 481 * currently loaded page. 482 * @param useDesktop If {@code true}, use a desktop user agent. Otherwise use a mobile one. 483 * @param reloadOnChange Reload the page if the user agent has changed. 484 */ setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange)485 public void setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange) { 486 if (mContentViewCore != null) { 487 mContentViewCore.setUseDesktopUserAgent(useDesktop, reloadOnChange); 488 } 489 } 490 491 /** 492 * @return Whether or not the {@link ContentViewCore} is using a desktop user agent. 493 */ getUseDesktopUserAgent()494 public boolean getUseDesktopUserAgent() { 495 return mContentViewCore != null && mContentViewCore.getUseDesktopUserAgent(); 496 } 497 498 /** 499 * @return The current {ToolbarModelSecurityLevel} for the tab. 500 */ getSecurityLevel()501 public int getSecurityLevel() { 502 if (mNativeTabAndroid == 0) return ToolbarModelSecurityLevel.NONE; 503 return nativeGetSecurityLevel(mNativeTabAndroid); 504 } 505 506 /** 507 * @return The sync id of the tab if session sync is enabled, {@code 0} otherwise. 508 */ 509 @CalledByNative getSyncId()510 protected int getSyncId() { 511 return mSyncId; 512 } 513 514 /** 515 * @param syncId The sync id of the tab if session sync is enabled. 516 */ 517 @CalledByNative setSyncId(int syncId)518 protected void setSyncId(int syncId) { 519 mSyncId = syncId; 520 } 521 522 /** 523 * @return An {@link ObserverList.RewindableIterator} instance that points to all of 524 * the current {@link TabObserver}s on this class. Note that calling 525 * {@link java.util.Iterator#remove()} will throw an 526 * {@link UnsupportedOperationException}. 527 */ getTabObservers()528 protected ObserverList.RewindableIterator<TabObserver> getTabObservers() { 529 return mObservers.rewindableIterator(); 530 } 531 532 /** 533 * @return The {@link ContentViewClient} currently bound to any {@link ContentViewCore} 534 * associated with the current page. There can still be a {@link ContentViewClient} 535 * even when there is no {@link ContentViewCore}. 536 */ getContentViewClient()537 protected ContentViewClient getContentViewClient() { 538 return mContentViewClient; 539 } 540 541 /** 542 * @param client The {@link ContentViewClient} to be bound to any current or new 543 * {@link ContentViewCore}s associated with this {@link TabBase}. 544 */ setContentViewClient(ContentViewClient client)545 protected void setContentViewClient(ContentViewClient client) { 546 if (mContentViewClient == client) return; 547 548 ContentViewClient oldClient = mContentViewClient; 549 mContentViewClient = client; 550 551 if (mContentViewCore == null) return; 552 553 if (mContentViewClient != null) { 554 mContentViewCore.setContentViewClient(mContentViewClient); 555 } else if (oldClient != null) { 556 // We can't set a null client, but we should clear references to the last one. 557 mContentViewCore.setContentViewClient(new ContentViewClient()); 558 } 559 } 560 561 /** 562 * Shows the given {@code nativePage} if it's not already showing. 563 * @param nativePage The {@link NativePage} to show. 564 */ showNativePage(NativePage nativePage)565 protected void showNativePage(NativePage nativePage) { 566 if (mNativePage == nativePage) return; 567 NativePage previousNativePage = mNativePage; 568 mNativePage = nativePage; 569 pushNativePageStateToNavigationEntry(); 570 for (TabObserver observer : mObservers) observer.onContentChanged(this); 571 destroyNativePageInternal(previousNativePage); 572 } 573 574 /** 575 * Hides the current {@link NativePage}, if any, and shows the {@link ContentView}. 576 */ showRenderedPage()577 protected void showRenderedPage() { 578 if (mNativePage == null) return; 579 NativePage previousNativePage = mNativePage; 580 mNativePage = null; 581 for (TabObserver observer : mObservers) observer.onContentChanged(this); 582 destroyNativePageInternal(previousNativePage); 583 } 584 585 /** 586 * Initializes this {@link TabBase}. 587 */ initialize()588 public void initialize() { } 589 590 /** 591 * A helper method to initialize a {@link ContentView} without any native WebContents pointer. 592 */ initContentView()593 protected final void initContentView() { 594 initContentView(ContentViewUtil.createNativeWebContents(mIncognito)); 595 } 596 597 /** 598 * Completes the {@link ContentView} specific initialization around a native WebContents 599 * pointer. {@link #getPageInfo()} will still return the {@link NativePage} if there is one. 600 * All initialization that needs to reoccur after a web contents swap should be added here. 601 * <p /> 602 * NOTE: If you attempt to pass a native WebContents that does not have the same incognito 603 * state as this tab this call will fail. 604 * 605 * @param nativeWebContents The native web contents pointer. 606 */ initContentView(long nativeWebContents)607 protected void initContentView(long nativeWebContents) { 608 NativePage previousNativePage = mNativePage; 609 mNativePage = null; 610 destroyNativePageInternal(previousNativePage); 611 612 mContentView = ContentView.newInstance(mContext, nativeWebContents, getWindowAndroid()); 613 614 mContentViewCore = mContentView.getContentViewCore(); 615 mWebContentsDelegate = createWebContentsDelegate(); 616 mWebContentsObserver = new TabBaseWebContentsObserverAndroid(mContentViewCore); 617 618 if (mContentViewClient != null) mContentViewCore.setContentViewClient(mContentViewClient); 619 620 assert mNativeTabAndroid != 0; 621 nativeInitWebContents( 622 mNativeTabAndroid, mIncognito, mContentViewCore, mWebContentsDelegate, 623 new TabBaseContextMenuPopulator(createContextMenuPopulator())); 624 625 // In the case where restoring a Tab or showing a prerendered one we already have a 626 // valid infobar container, no need to recreate one. 627 if (mInfoBarContainer == null) { 628 // The InfoBarContainer needs to be created after the ContentView has been natively 629 // initialized. 630 mInfoBarContainer = new InfoBarContainer( 631 (Activity) mContext, createAutoLoginProcessor(), getId(), getContentView(), 632 nativeWebContents); 633 } else { 634 mInfoBarContainer.onParentViewChanged(getId(), getContentView()); 635 } 636 } 637 638 /** 639 * Cleans up all internal state, destroying any {@link NativePage} or {@link ContentView} 640 * currently associated with this {@link TabBase}. Typically, pnce this call is made this 641 * {@link TabBase} should no longer be used as subclasses usually destroy the native component. 642 */ destroy()643 public void destroy() { 644 for (TabObserver observer : mObservers) observer.onDestroyed(this); 645 646 NativePage currentNativePage = mNativePage; 647 mNativePage = null; 648 destroyNativePageInternal(currentNativePage); 649 destroyContentView(true); 650 if (mInfoBarContainer != null) { 651 mInfoBarContainer.destroy(); 652 mInfoBarContainer = null; 653 } 654 } 655 656 /** 657 * @return Whether or not this Tab has a live native component. 658 */ isInitialized()659 public boolean isInitialized() { 660 return mNativeTabAndroid != 0; 661 } 662 663 /** 664 * @return The url associated with the tab. 665 */ 666 @CalledByNative getUrl()667 public String getUrl() { 668 return mContentView != null ? mContentView.getUrl() : ""; 669 } 670 671 /** 672 * @return The tab title. 673 */ 674 @CalledByNative getTitle()675 public String getTitle() { 676 return getPageInfo() != null ? getPageInfo().getTitle() : ""; 677 } 678 679 /** 680 * @return The bitmap of the favicon scaled to 16x16dp. null if no favicon 681 * is specified or it requires the default favicon. 682 * TODO(bauerb): Upstream implementation. 683 */ getFavicon()684 public Bitmap getFavicon() { 685 return null; 686 } 687 688 /** 689 * Restores the tab if it is frozen or crashed. 690 * @return true iff tab restore was triggered. 691 */ 692 @CalledByNative restoreIfNeeded()693 public boolean restoreIfNeeded() { 694 return false; 695 } 696 destroyNativePageInternal(NativePage nativePage)697 private void destroyNativePageInternal(NativePage nativePage) { 698 if (nativePage == null) return; 699 assert getPageInfo() != nativePage : "Attempting to destroy active page."; 700 701 nativePage.destroy(); 702 } 703 704 /** 705 * Destroys the current {@link ContentView}. 706 * @param deleteNativeWebContents Whether or not to delete the native WebContents pointer. 707 */ destroyContentView(boolean deleteNativeWebContents)708 protected final void destroyContentView(boolean deleteNativeWebContents) { 709 if (mContentView == null) return; 710 711 destroyContentViewInternal(mContentView); 712 713 if (mInfoBarContainer != null && mInfoBarContainer.getParent() != null) { 714 mInfoBarContainer.removeFromParentView(); 715 } 716 if (mContentViewCore != null) mContentViewCore.destroy(); 717 718 mContentView = null; 719 mContentViewCore = null; 720 mWebContentsDelegate = null; 721 mWebContentsObserver = null; 722 723 assert mNativeTabAndroid != 0; 724 nativeDestroyWebContents(mNativeTabAndroid, deleteNativeWebContents); 725 } 726 727 /** 728 * Gives subclasses the chance to clean up some state associated with this {@link ContentView}. 729 * This is because {@link #getContentView()} can return {@code null} if a {@link NativePage} 730 * is showing. 731 * @param contentView The {@link ContentView} that should have associated state cleaned up. 732 */ destroyContentViewInternal(ContentView contentView)733 protected void destroyContentViewInternal(ContentView contentView) { 734 } 735 736 /** 737 * A helper method to allow subclasses to build their own delegate. 738 * @return An instance of a {@link TabBaseChromeWebContentsDelegateAndroid}. 739 */ createWebContentsDelegate()740 protected TabBaseChromeWebContentsDelegateAndroid createWebContentsDelegate() { 741 return new TabBaseChromeWebContentsDelegateAndroid(); 742 } 743 744 /** 745 * A helper method to allow subclasses to build their own menu populator. 746 * @return An instance of a {@link ContextMenuPopulator}. 747 */ createContextMenuPopulator()748 protected ContextMenuPopulator createContextMenuPopulator() { 749 return new ChromeContextMenuPopulator(new TabBaseChromeContextMenuItemDelegate()); 750 } 751 752 /** 753 * @return The {@link WindowAndroid} associated with this {@link TabBase}. 754 */ getWindowAndroid()755 protected WindowAndroid getWindowAndroid() { 756 return mWindowAndroid; 757 } 758 759 /** 760 * @return The current {@link TabBaseChromeWebContentsDelegateAndroid} instance. 761 */ getChromeWebContentsDelegateAndroid()762 protected TabBaseChromeWebContentsDelegateAndroid getChromeWebContentsDelegateAndroid() { 763 return mWebContentsDelegate; 764 } 765 766 /** 767 * Called when the favicon of the content this tab represents changes. 768 */ 769 @CalledByNative onFaviconUpdated()770 protected void onFaviconUpdated() { 771 for (TabObserver observer : mObservers) observer.onFaviconUpdated(this); 772 } 773 774 /** 775 * @return The native pointer representing the native side of this {@link TabBase} object. 776 */ 777 @CalledByNative getNativePtr()778 protected long getNativePtr() { 779 return mNativeTabAndroid; 780 } 781 782 /** This is currently called when committing a pre-rendered page. */ 783 @CalledByNative swapWebContents(final long newWebContents)784 private void swapWebContents(final long newWebContents) { 785 if (mContentViewCore != null) mContentViewCore.onHide(); 786 destroyContentView(false); 787 NativePage previousNativePage = mNativePage; 788 mNativePage = null; 789 initContentView(newWebContents); 790 mContentViewCore.onShow(); 791 mContentViewCore.attachImeAdapter(); 792 for (TabObserver observer : mObservers) observer.onContentChanged(this); 793 destroyNativePageInternal(previousNativePage); 794 for (TabObserver observer : mObservers) observer.onWebContentsSwapped(this); 795 } 796 797 @CalledByNative clearNativePtr()798 private void clearNativePtr() { 799 assert mNativeTabAndroid != 0; 800 mNativeTabAndroid = 0; 801 } 802 803 @CalledByNative setNativePtr(long nativePtr)804 private void setNativePtr(long nativePtr) { 805 assert mNativeTabAndroid == 0; 806 mNativeTabAndroid = nativePtr; 807 } 808 809 @CalledByNative getNativeInfoBarContainer()810 private long getNativeInfoBarContainer() { 811 return getInfoBarContainer().getNative(); 812 } 813 814 /** 815 * Validates {@code id} and increments the internal counter to make sure future ids don't 816 * collide. 817 * @param id The current id. Maybe {@link #INVALID_TAB_ID}. 818 * @return A new id if {@code id} was {@link #INVALID_TAB_ID}, or {@code id}. 819 */ generateValidId(int id)820 private static int generateValidId(int id) { 821 if (id == INVALID_TAB_ID) id = generateNextId(); 822 incrementIdCounterTo(id + 1); 823 824 return id; 825 } 826 827 /** 828 * @return An unused id. 829 */ generateNextId()830 private static int generateNextId() { 831 return sIdCounter.getAndIncrement(); 832 } 833 pushNativePageStateToNavigationEntry()834 private void pushNativePageStateToNavigationEntry() { 835 assert mNativeTabAndroid != 0 && getNativePage() != null; 836 nativeSetActiveNavigationEntryTitleForUrl(mNativeTabAndroid, getNativePage().getUrl(), 837 getNativePage().getTitle()); 838 } 839 840 /** 841 * Ensures the counter is at least as high as the specified value. The counter should always 842 * point to an unused ID (which will be handed out next time a request comes in). Exposed so 843 * that anything externally loading tabs and ids can set enforce new tabs start at the correct 844 * id. 845 * TODO(aurimas): Investigate reducing the visiblity of this method. 846 * @param id The minimum id we should hand out to the next new tab. 847 */ incrementIdCounterTo(int id)848 public static void incrementIdCounterTo(int id) { 849 int diff = id - sIdCounter.get(); 850 if (diff <= 0) return; 851 // It's possible idCounter has been incremented between the get above and the add below 852 // but that's OK, because in the worst case we'll overly increment idCounter. 853 sIdCounter.addAndGet(diff); 854 } 855 nativeInitWebContents(long nativeTabAndroid, boolean incognito, ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate, ContextMenuPopulator contextMenuPopulator)856 private native void nativeInitWebContents(long nativeTabAndroid, boolean incognito, 857 ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate, 858 ContextMenuPopulator contextMenuPopulator); nativeDestroyWebContents(long nativeTabAndroid, boolean deleteNative)859 private native void nativeDestroyWebContents(long nativeTabAndroid, boolean deleteNative); nativeGetProfileAndroid(long nativeTabAndroid)860 private native Profile nativeGetProfileAndroid(long nativeTabAndroid); nativeGetSecurityLevel(long nativeTabAndroid)861 private native int nativeGetSecurityLevel(long nativeTabAndroid); nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url, String title)862 private native void nativeSetActiveNavigationEntryTitleForUrl(long nativeTabAndroid, String url, 863 String title); nativePrint(long nativeTabAndroid)864 private native boolean nativePrint(long nativeTabAndroid); 865 } 866