1 // Copyright (c) 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.
5 #include "chrome/browser/android/tab_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/debug/trace_event.h"
11 #include "chrome/browser/android/chrome_web_contents_delegate_android.h"
12 #include "chrome/browser/browser_about_handler.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
15 #include "chrome/browser/google/google_url_tracker.h"
16 #include "chrome/browser/google/google_util.h"
17 #include "chrome/browser/infobars/infobar_service.h"
18 #include "chrome/browser/prerender/prerender_contents.h"
19 #include "chrome/browser/prerender/prerender_manager.h"
20 #include "chrome/browser/prerender/prerender_manager_factory.h"
21 #include "chrome/browser/printing/print_view_manager_basic.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/profiles/profile_android.h"
24 #include "chrome/browser/search/search.h"
25 #include "chrome/browser/sessions/session_tab_helper.h"
26 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
27 #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
28 #include "chrome/browser/ui/android/context_menu_helper.h"
29 #include "chrome/browser/ui/android/infobars/infobar_container_android.h"
30 #include "chrome/browser/ui/android/tab_model/tab_model.h"
31 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
32 #include "chrome/browser/ui/android/window_android_helper.h"
33 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
34 #include "chrome/browser/ui/search/search_tab_helper.h"
35 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
36 #include "chrome/browser/ui/tab_helpers.h"
37 #include "chrome/browser/ui/toolbar/toolbar_model_impl.h"
38 #include "chrome/common/net/url_fixer_upper.h"
39 #include "chrome/common/url_constants.h"
40 #include "components/infobars/core/infobar_container.h"
41 #include "content/public/browser/android/content_view_core.h"
42 #include "content/public/browser/navigation_entry.h"
43 #include "content/public/browser/notification_service.h"
44 #include "content/public/browser/user_metrics.h"
45 #include "content/public/browser/web_contents.h"
46 #include "jni/Tab_jni.h"
47 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
49 TabAndroid
* TabAndroid::FromWebContents(content::WebContents
* web_contents
) {
50 CoreTabHelper
* core_tab_helper
= CoreTabHelper::FromWebContents(web_contents
);
54 CoreTabHelperDelegate
* core_delegate
= core_tab_helper
->delegate();
58 return static_cast<TabAndroid
*>(core_delegate
);
61 TabAndroid
* TabAndroid::GetNativeTab(JNIEnv
* env
, jobject obj
) {
62 return reinterpret_cast<TabAndroid
*>(Java_Tab_getNativePtr(env
, obj
));
65 void TabAndroid::AttachTabHelpers(content::WebContents
* web_contents
) {
68 TabHelpers::AttachTabHelpers(web_contents
);
71 TabAndroid::TabAndroid(JNIEnv
* env
, jobject obj
)
72 : weak_java_tab_(env
, obj
),
73 synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) {
74 Java_Tab_setNativePtr(env
, obj
, reinterpret_cast<intptr_t>(this));
77 TabAndroid::~TabAndroid() {
78 JNIEnv
* env
= base::android::AttachCurrentThread();
79 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
83 Java_Tab_clearNativePtr(env
, obj
.obj());
86 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetJavaObject() {
87 JNIEnv
* env
= base::android::AttachCurrentThread();
88 return weak_java_tab_
.get(env
);
91 int TabAndroid::GetAndroidId() const {
92 JNIEnv
* env
= base::android::AttachCurrentThread();
93 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
96 return Java_Tab_getId(env
, obj
.obj());
99 int TabAndroid::GetSyncId() const {
100 JNIEnv
* env
= base::android::AttachCurrentThread();
101 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
104 return Java_Tab_getSyncId(env
, obj
.obj());
107 base::string16
TabAndroid::GetTitle() const {
108 JNIEnv
* env
= base::android::AttachCurrentThread();
109 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
111 return base::string16();
112 return base::android::ConvertJavaStringToUTF16(
113 Java_Tab_getTitle(env
, obj
.obj()));
116 GURL
TabAndroid::GetURL() const {
117 JNIEnv
* env
= base::android::AttachCurrentThread();
118 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
120 return GURL::EmptyGURL();
121 return GURL(base::android::ConvertJavaStringToUTF8(
122 Java_Tab_getUrl(env
, obj
.obj())));
125 bool TabAndroid::LoadIfNeeded() {
126 JNIEnv
* env
= base::android::AttachCurrentThread();
127 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
130 return Java_Tab_loadIfNeeded(env
, obj
.obj());
133 content::ContentViewCore
* TabAndroid::GetContentViewCore() const {
137 return content::ContentViewCore::FromWebContents(web_contents());
140 Profile
* TabAndroid::GetProfile() const {
144 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
147 browser_sync::SyncedTabDelegate
* TabAndroid::GetSyncedTabDelegate() const {
148 return synced_tab_delegate_
.get();
151 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id
) {
152 session_window_id_
.set_id(window_id
);
157 SessionTabHelper
* session_tab_helper
=
158 SessionTabHelper::FromWebContents(web_contents());
159 session_tab_helper
->SetWindowID(session_window_id_
);
162 void TabAndroid::SetSyncId(int sync_id
) {
163 JNIEnv
* env
= base::android::AttachCurrentThread();
164 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
167 Java_Tab_setSyncId(env
, obj
.obj(), sync_id
);
170 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams
* params
) {
174 void TabAndroid::OnReceivedHttpAuthRequest(jobject auth_handler
,
175 const base::string16
& host
,
176 const base::string16
& realm
) {
180 bool TabAndroid::ShouldWelcomePageLinkToTermsOfService() {
185 bool TabAndroid::HasPrerenderedUrl(GURL gurl
) {
186 prerender::PrerenderManager
* prerender_manager
= GetPrerenderManager();
187 if (!prerender_manager
)
190 std::vector
<content::WebContents
*> contents
=
191 prerender_manager
->GetAllPrerenderingContents();
192 prerender::PrerenderContents
* prerender_contents
;
193 for (size_t i
= 0; i
< contents
.size(); ++i
) {
194 prerender_contents
= prerender_manager
->
195 GetPrerenderContents(contents
.at(i
));
196 if (prerender_contents
->prerender_url() == gurl
&&
197 prerender_contents
->has_finished_loading()) {
204 void TabAndroid::SwapTabContents(content::WebContents
* old_contents
,
205 content::WebContents
* new_contents
,
207 bool did_finish_load
) {
208 JNIEnv
* env
= base::android::AttachCurrentThread();
210 // We need to notify the native InfobarContainer so infobars can be swapped.
211 InfoBarContainerAndroid
* infobar_container
=
212 reinterpret_cast<InfoBarContainerAndroid
*>(
213 Java_Tab_getNativeInfoBarContainer(
215 weak_java_tab_
.get(env
).obj()));
216 InfoBarService
* new_infobar_service
=
217 new_contents
? InfoBarService::FromWebContents(new_contents
) : NULL
;
218 infobar_container
->ChangeInfoBarManager(new_infobar_service
);
220 Java_Tab_swapWebContents(
222 weak_java_tab_
.get(env
).obj(),
223 reinterpret_cast<intptr_t>(new_contents
),
228 void TabAndroid::Observe(int type
,
229 const content::NotificationSource
& source
,
230 const content::NotificationDetails
& details
) {
231 JNIEnv
* env
= base::android::AttachCurrentThread();
233 case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
: {
234 TabSpecificContentSettings
* settings
=
235 TabSpecificContentSettings::FromWebContents(web_contents());
236 if (!settings
->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS
)) {
237 // TODO(dfalcantara): Create an InfoBarDelegate to keep the
238 // PopupBlockedInfoBar logic native-side instead of straddling the JNI
241 PopupBlockerTabHelper
* popup_blocker_helper
=
242 PopupBlockerTabHelper::FromWebContents(web_contents());
243 if (popup_blocker_helper
)
244 num_popups
= popup_blocker_helper
->GetBlockedPopupsCount();
247 PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups
);
249 settings
->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS
);
253 case chrome::NOTIFICATION_FAVICON_UPDATED
:
254 Java_Tab_onFaviconUpdated(env
, weak_java_tab_
.get(env
).obj());
256 case content::NOTIFICATION_NAV_ENTRY_CHANGED
:
257 Java_Tab_onNavEntryChanged(env
, weak_java_tab_
.get(env
).obj());
260 NOTREACHED() << "Unexpected notification " << type
;
265 void TabAndroid::Destroy(JNIEnv
* env
, jobject obj
) {
269 void TabAndroid::InitWebContents(JNIEnv
* env
,
272 jobject jcontent_view_core
,
273 jobject jweb_contents_delegate
,
274 jobject jcontext_menu_populator
) {
275 content::ContentViewCore
* content_view_core
=
276 content::ContentViewCore::GetNativeContentViewCore(env
,
278 DCHECK(content_view_core
);
279 DCHECK(content_view_core
->GetWebContents());
281 web_contents_
.reset(content_view_core
->GetWebContents());
282 AttachTabHelpers(web_contents_
.get());
284 SetWindowSessionID(session_window_id_
.id());
286 session_tab_id_
.set_id(
287 SessionTabHelper::FromWebContents(web_contents())->session_id().id());
288 ContextMenuHelper::FromWebContents(web_contents())->SetPopulator(
289 jcontext_menu_populator
);
290 WindowAndroidHelper::FromWebContents(web_contents())->
291 SetWindowAndroid(content_view_core
->GetWindowAndroid());
292 CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
293 web_contents_delegate_
.reset(
294 new chrome::android::ChromeWebContentsDelegateAndroid(
295 env
, jweb_contents_delegate
));
296 web_contents_delegate_
->LoadProgressChanged(web_contents(), 0);
297 web_contents()->SetDelegate(web_contents_delegate_
.get());
299 notification_registrar_
.Add(
301 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
,
302 content::Source
<content::WebContents
>(web_contents()));
303 notification_registrar_
.Add(
305 chrome::NOTIFICATION_FAVICON_UPDATED
,
306 content::Source
<content::WebContents
>(web_contents()));
307 notification_registrar_
.Add(
309 content::NOTIFICATION_NAV_ENTRY_CHANGED
,
310 content::Source
<content::NavigationController
>(
311 &web_contents()->GetController()));
313 synced_tab_delegate_
->SetWebContents(web_contents());
315 // Verify that the WebContents this tab represents matches the expected
316 // off the record state.
317 CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito
);
320 void TabAndroid::DestroyWebContents(JNIEnv
* env
,
322 jboolean delete_native
) {
323 DCHECK(web_contents());
325 notification_registrar_
.Remove(
327 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
,
328 content::Source
<content::WebContents
>(web_contents()));
329 notification_registrar_
.Remove(
331 chrome::NOTIFICATION_FAVICON_UPDATED
,
332 content::Source
<content::WebContents
>(web_contents()));
333 notification_registrar_
.Remove(
335 content::NOTIFICATION_NAV_ENTRY_CHANGED
,
336 content::Source
<content::NavigationController
>(
337 &web_contents()->GetController()));
339 web_contents()->SetDelegate(NULL
);
342 web_contents_
.reset();
343 synced_tab_delegate_
->ResetWebContents();
345 // Release the WebContents so it does not get deleted by the scoped_ptr.
346 ignore_result(web_contents_
.release());
350 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetWebContents(
353 if (!web_contents_
.get())
354 return base::android::ScopedJavaLocalRef
<jobject
>();
355 return web_contents_
->GetJavaWebContents();
358 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetProfileAndroid(
361 Profile
* profile
= GetProfile();
363 return base::android::ScopedJavaLocalRef
<jobject
>();
364 ProfileAndroid
* profile_android
= ProfileAndroid::FromProfile(profile
);
365 if (!profile_android
)
366 return base::android::ScopedJavaLocalRef
<jobject
>();
368 return profile_android
->GetJavaObject();
371 TabAndroid::TabLoadStatus
TabAndroid::LoadUrl(JNIEnv
* env
,
374 jstring j_extra_headers
,
375 jbyteArray j_post_data
,
376 jint page_transition
,
377 jstring j_referrer_url
,
378 jint referrer_policy
) {
379 content::ContentViewCore
* content_view
= GetContentViewCore();
381 return PAGE_LOAD_FAILED
;
383 GURL
gurl(base::android::ConvertJavaStringToUTF8(env
, url
));
385 return PAGE_LOAD_FAILED
;
387 // If the page was prerendered, use it.
388 // Note in incognito mode, we don't have a PrerenderManager.
390 prerender::PrerenderManager
* prerender_manager
=
391 prerender::PrerenderManagerFactory::GetForProfile(GetProfile());
392 if (prerender_manager
) {
393 bool prefetched_page_loaded
= HasPrerenderedUrl(gurl
);
394 // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
395 chrome::NavigateParams
params(NULL
, web_contents());
396 if (prerender_manager
->MaybeUsePrerenderedPage(gurl
, ¶ms
)) {
397 return prefetched_page_loaded
?
398 FULL_PRERENDERED_PAGE_LOAD
: PARTIAL_PRERENDERED_PAGE_LOAD
;
402 GURL
fixed_url(URLFixerUpper::FixupURL(gurl
.possibly_invalid_spec(),
404 if (!fixed_url
.is_valid())
405 return PAGE_LOAD_FAILED
;
407 if (!HandleNonNavigationAboutURL(fixed_url
)) {
408 // Notify the GoogleURLTracker of searches, it might want to change the
409 // actual Google site used (for instance when in the UK, google.co.uk, when
410 // in the US google.com).
411 // Note that this needs to happen before we initiate the navigation as the
412 // GoogleURLTracker uses the navigation pending notification to trigger the
414 if (google_util::IsGoogleSearchUrl(fixed_url
) &&
415 (page_transition
& content::PAGE_TRANSITION_GENERATED
)) {
416 GoogleURLTracker::GoogleURLSearchCommitted(GetProfile());
419 // Record UMA "ShowHistory" here. That way it'll pick up both user
420 // typing chrome://history as well as selecting from the drop down menu.
421 if (fixed_url
.spec() == chrome::kChromeUIHistoryURL
) {
422 content::RecordAction(base::UserMetricsAction("ShowHistory"));
425 content::NavigationController::LoadURLParams
load_params(fixed_url
);
426 if (j_extra_headers
) {
427 load_params
.extra_headers
= base::android::ConvertJavaStringToUTF8(
432 load_params
.load_type
=
433 content::NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
434 std::vector
<uint8
> post_data
;
435 base::android::JavaByteArrayToByteVector(env
, j_post_data
, &post_data
);
436 load_params
.browser_initiated_post_data
=
437 base::RefCountedBytes::TakeVector(&post_data
);
439 load_params
.transition_type
=
440 content::PageTransitionFromInt(page_transition
);
441 if (j_referrer_url
) {
442 load_params
.referrer
= content::Referrer(
443 GURL(base::android::ConvertJavaStringToUTF8(env
, j_referrer_url
)),
444 static_cast<blink::WebReferrerPolicy
>(referrer_policy
));
446 const base::string16 search_terms
=
447 chrome::ExtractSearchTermsFromURL(GetProfile(), gurl
);
448 SearchTabHelper
* search_tab_helper
=
449 SearchTabHelper::FromWebContents(web_contents_
.get());
450 if (!search_terms
.empty() && search_tab_helper
&&
451 search_tab_helper
->SupportsInstant()) {
452 search_tab_helper
->Submit(search_terms
);
453 return DEFAULT_PAGE_LOAD
;
455 content_view
->LoadUrl(load_params
);
457 return DEFAULT_PAGE_LOAD
;
460 ToolbarModel::SecurityLevel
TabAndroid::GetSecurityLevel(JNIEnv
* env
,
462 return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
465 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv
* env
,
469 DCHECK(web_contents());
471 base::string16 title
;
473 title
= base::android::ConvertJavaStringToUTF16(env
, jtitle
);
477 url
= base::android::ConvertJavaStringToUTF8(env
, jurl
);
479 content::NavigationEntry
* entry
=
480 web_contents()->GetController().GetVisibleEntry();
481 if (entry
&& url
== entry
->GetVirtualURL().spec())
482 entry
->SetTitle(title
);
485 bool TabAndroid::Print(JNIEnv
* env
, jobject obj
) {
489 printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
490 printing::PrintViewManagerBasic
* print_view_manager
=
491 printing::PrintViewManagerBasic::FromWebContents(web_contents());
492 if (print_view_manager
== NULL
)
495 print_view_manager
->PrintNow();
499 prerender::PrerenderManager
* TabAndroid::GetPrerenderManager() const {
500 Profile
* profile
= GetProfile();
503 return prerender::PrerenderManagerFactory::GetForProfile(profile
);
506 static void Init(JNIEnv
* env
, jobject obj
) {
507 TRACE_EVENT0("native", "TabAndroid::Init");
508 // This will automatically bind to the Java object and pass ownership there.
509 new TabAndroid(env
, obj
);
512 bool TabAndroid::RegisterTabAndroid(JNIEnv
* env
) {
513 return RegisterNativesImpl(env
);