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