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 |