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/sessions/session_tab_helper.h"
25 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
26 #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
27 #include "chrome/browser/ui/android/context_menu_helper.h"
28 #include "chrome/browser/ui/android/infobars/infobar_container_android.h"
29 #include "chrome/browser/ui/android/tab_model/tab_model.h"
30 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
31 #include "chrome/browser/ui/android/window_android_helper.h"
32 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
33 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
34 #include "chrome/browser/ui/tab_helpers.h"
35 #include "chrome/browser/ui/toolbar/toolbar_model_impl.h"
36 #include "chrome/common/net/url_fixer_upper.h"
37 #include "chrome/common/url_constants.h"
38 #include "components/infobars/core/infobar_container.h"
39 #include "content/public/browser/android/content_view_core.h"
40 #include "content/public/browser/navigation_entry.h"
41 #include "content/public/browser/notification_service.h"
42 #include "content/public/browser/user_metrics.h"
43 #include "content/public/browser/web_contents.h"
44 #include "jni/Tab_jni.h"
45 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
47 TabAndroid
* TabAndroid::FromWebContents(content::WebContents
* web_contents
) {
48 CoreTabHelper
* core_tab_helper
= CoreTabHelper::FromWebContents(web_contents
);
52 CoreTabHelperDelegate
* core_delegate
= core_tab_helper
->delegate();
56 return static_cast<TabAndroid
*>(core_delegate
);
59 TabAndroid
* TabAndroid::GetNativeTab(JNIEnv
* env
, jobject obj
) {
60 return reinterpret_cast<TabAndroid
*>(Java_Tab_getNativePtr(env
, obj
));
63 void TabAndroid::AttachTabHelpers(content::WebContents
* web_contents
) {
66 TabHelpers::AttachTabHelpers(web_contents
);
69 TabAndroid::TabAndroid(JNIEnv
* env
, jobject obj
)
70 : weak_java_tab_(env
, obj
),
71 synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) {
72 Java_Tab_setNativePtr(env
, obj
, reinterpret_cast<intptr_t>(this));
75 TabAndroid::~TabAndroid() {
76 JNIEnv
* env
= base::android::AttachCurrentThread();
77 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
81 Java_Tab_clearNativePtr(env
, obj
.obj());
84 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetJavaObject() {
85 JNIEnv
* env
= base::android::AttachCurrentThread();
86 return weak_java_tab_
.get(env
);
89 int TabAndroid::GetAndroidId() const {
90 JNIEnv
* env
= base::android::AttachCurrentThread();
91 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
94 return Java_Tab_getId(env
, obj
.obj());
97 int TabAndroid::GetSyncId() const {
98 JNIEnv
* env
= base::android::AttachCurrentThread();
99 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
102 return Java_Tab_getSyncId(env
, obj
.obj());
105 base::string16
TabAndroid::GetTitle() const {
106 JNIEnv
* env
= base::android::AttachCurrentThread();
107 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
109 return base::string16();
110 return base::android::ConvertJavaStringToUTF16(
111 Java_Tab_getTitle(env
, obj
.obj()));
114 GURL
TabAndroid::GetURL() const {
115 JNIEnv
* env
= base::android::AttachCurrentThread();
116 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
118 return GURL::EmptyGURL();
119 return GURL(base::android::ConvertJavaStringToUTF8(
120 Java_Tab_getUrl(env
, obj
.obj())));
123 bool TabAndroid::LoadIfNeeded() {
124 JNIEnv
* env
= base::android::AttachCurrentThread();
125 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
128 return Java_Tab_loadIfNeeded(env
, obj
.obj());
131 content::ContentViewCore
* TabAndroid::GetContentViewCore() const {
135 return content::ContentViewCore::FromWebContents(web_contents());
138 Profile
* TabAndroid::GetProfile() const {
142 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
145 browser_sync::SyncedTabDelegate
* TabAndroid::GetSyncedTabDelegate() const {
146 return synced_tab_delegate_
.get();
149 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id
) {
150 session_window_id_
.set_id(window_id
);
155 SessionTabHelper
* session_tab_helper
=
156 SessionTabHelper::FromWebContents(web_contents());
157 session_tab_helper
->SetWindowID(session_window_id_
);
160 void TabAndroid::SetSyncId(int sync_id
) {
161 JNIEnv
* env
= base::android::AttachCurrentThread();
162 ScopedJavaLocalRef
<jobject
> obj
= weak_java_tab_
.get(env
);
165 Java_Tab_setSyncId(env
, obj
.obj(), sync_id
);
168 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams
* params
) {
172 void TabAndroid::OnReceivedHttpAuthRequest(jobject auth_handler
,
173 const base::string16
& host
,
174 const base::string16
& realm
) {
178 void TabAndroid::AddShortcutToBookmark(const GURL
& url
,
179 const base::string16
& title
,
180 const SkBitmap
& skbitmap
,
187 void TabAndroid::EditBookmark(int64 node_id
,
188 const base::string16
& node_title
,
190 bool is_partner_bookmark
) {
194 void TabAndroid::OnNewTabPageReady() {
198 bool TabAndroid::ShouldWelcomePageLinkToTermsOfService() {
203 bool TabAndroid::HasPrerenderedUrl(GURL gurl
) {
204 prerender::PrerenderManager
* prerender_manager
= GetPrerenderManager();
205 if (!prerender_manager
)
208 std::vector
<content::WebContents
*> contents
=
209 prerender_manager
->GetAllPrerenderingContents();
210 prerender::PrerenderContents
* prerender_contents
;
211 for (size_t i
= 0; i
< contents
.size(); ++i
) {
212 prerender_contents
= prerender_manager
->
213 GetPrerenderContents(contents
.at(i
));
214 if (prerender_contents
->prerender_url() == gurl
&&
215 prerender_contents
->has_finished_loading()) {
222 void TabAndroid::SwapTabContents(content::WebContents
* old_contents
,
223 content::WebContents
* new_contents
,
225 bool did_finish_load
) {
226 JNIEnv
* env
= base::android::AttachCurrentThread();
228 // We need to notify the native InfobarContainer so infobars can be swapped.
229 InfoBarContainerAndroid
* infobar_container
=
230 reinterpret_cast<InfoBarContainerAndroid
*>(
231 Java_Tab_getNativeInfoBarContainer(
233 weak_java_tab_
.get(env
).obj()));
234 InfoBarService
* new_infobar_service
=
235 new_contents
? InfoBarService::FromWebContents(new_contents
) : NULL
;
236 infobar_container
->ChangeInfoBarManager(new_infobar_service
);
238 Java_Tab_swapWebContents(
240 weak_java_tab_
.get(env
).obj(),
241 reinterpret_cast<intptr_t>(new_contents
),
246 void TabAndroid::Observe(int type
,
247 const content::NotificationSource
& source
,
248 const content::NotificationDetails
& details
) {
249 JNIEnv
* env
= base::android::AttachCurrentThread();
251 case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
: {
252 TabSpecificContentSettings
* settings
=
253 TabSpecificContentSettings::FromWebContents(web_contents());
254 if (!settings
->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS
)) {
255 // TODO(dfalcantara): Create an InfoBarDelegate to keep the
256 // PopupBlockedInfoBar logic native-side instead of straddling the JNI
259 PopupBlockerTabHelper
* popup_blocker_helper
=
260 PopupBlockerTabHelper::FromWebContents(web_contents());
261 if (popup_blocker_helper
)
262 num_popups
= popup_blocker_helper
->GetBlockedPopupsCount();
265 PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups
);
267 settings
->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS
);
271 case chrome::NOTIFICATION_FAVICON_UPDATED
:
272 Java_Tab_onFaviconUpdated(env
, weak_java_tab_
.get(env
).obj());
274 case content::NOTIFICATION_NAV_ENTRY_CHANGED
:
275 Java_Tab_onNavEntryChanged(env
, weak_java_tab_
.get(env
).obj());
278 NOTREACHED() << "Unexpected notification " << type
;
283 void TabAndroid::Destroy(JNIEnv
* env
, jobject obj
) {
287 void TabAndroid::InitWebContents(JNIEnv
* env
,
290 jobject jcontent_view_core
,
291 jobject jweb_contents_delegate
,
292 jobject jcontext_menu_populator
) {
293 content::ContentViewCore
* content_view_core
=
294 content::ContentViewCore::GetNativeContentViewCore(env
,
296 DCHECK(content_view_core
);
297 DCHECK(content_view_core
->GetWebContents());
299 web_contents_
.reset(content_view_core
->GetWebContents());
300 AttachTabHelpers(web_contents_
.get());
302 SetWindowSessionID(session_window_id_
.id());
304 session_tab_id_
.set_id(
305 SessionTabHelper::FromWebContents(web_contents())->session_id().id());
306 ContextMenuHelper::FromWebContents(web_contents())->SetPopulator(
307 jcontext_menu_populator
);
308 WindowAndroidHelper::FromWebContents(web_contents())->
309 SetWindowAndroid(content_view_core
->GetWindowAndroid());
310 CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
311 web_contents_delegate_
.reset(
312 new chrome::android::ChromeWebContentsDelegateAndroid(
313 env
, jweb_contents_delegate
));
314 web_contents_delegate_
->LoadProgressChanged(web_contents(), 0);
315 web_contents()->SetDelegate(web_contents_delegate_
.get());
317 notification_registrar_
.Add(
319 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
,
320 content::Source
<content::WebContents
>(web_contents()));
321 notification_registrar_
.Add(
323 chrome::NOTIFICATION_FAVICON_UPDATED
,
324 content::Source
<content::WebContents
>(web_contents()));
325 notification_registrar_
.Add(
327 content::NOTIFICATION_NAV_ENTRY_CHANGED
,
328 content::Source
<content::NavigationController
>(
329 &web_contents()->GetController()));
331 synced_tab_delegate_
->SetWebContents(web_contents());
333 // Verify that the WebContents this tab represents matches the expected
334 // off the record state.
335 CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito
);
338 void TabAndroid::DestroyWebContents(JNIEnv
* env
,
340 jboolean delete_native
) {
341 DCHECK(web_contents());
343 notification_registrar_
.Remove(
345 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
,
346 content::Source
<content::WebContents
>(web_contents()));
347 notification_registrar_
.Remove(
349 chrome::NOTIFICATION_FAVICON_UPDATED
,
350 content::Source
<content::WebContents
>(web_contents()));
351 notification_registrar_
.Remove(
353 content::NOTIFICATION_NAV_ENTRY_CHANGED
,
354 content::Source
<content::NavigationController
>(
355 &web_contents()->GetController()));
357 web_contents()->SetDelegate(NULL
);
360 web_contents_
.reset();
361 synced_tab_delegate_
->ResetWebContents();
363 // Release the WebContents so it does not get deleted by the scoped_ptr.
364 ignore_result(web_contents_
.release());
368 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetWebContents(
371 if (!web_contents_
.get())
372 return base::android::ScopedJavaLocalRef
<jobject
>();
373 return web_contents_
->GetJavaWebContents();
376 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetProfileAndroid(
379 Profile
* profile
= GetProfile();
381 return base::android::ScopedJavaLocalRef
<jobject
>();
382 ProfileAndroid
* profile_android
= ProfileAndroid::FromProfile(profile
);
383 if (!profile_android
)
384 return base::android::ScopedJavaLocalRef
<jobject
>();
386 return profile_android
->GetJavaObject();
389 TabAndroid::TabLoadStatus
TabAndroid::LoadUrl(JNIEnv
* env
,
392 jstring j_extra_headers
,
393 jbyteArray j_post_data
,
394 jint page_transition
,
395 jstring j_referrer_url
,
396 jint referrer_policy
) {
397 content::ContentViewCore
* content_view
= GetContentViewCore();
399 return PAGE_LOAD_FAILED
;
401 GURL
gurl(base::android::ConvertJavaStringToUTF8(env
, url
));
403 return PAGE_LOAD_FAILED
;
405 // If the page was prerendered, use it.
406 // Note in incognito mode, we don't have a PrerenderManager.
408 prerender::PrerenderManager
* prerender_manager
=
409 prerender::PrerenderManagerFactory::GetForProfile(GetProfile());
410 if (prerender_manager
) {
411 bool prefetched_page_loaded
= HasPrerenderedUrl(gurl
);
412 // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
413 chrome::NavigateParams
params(NULL
, web_contents());
414 if (prerender_manager
->MaybeUsePrerenderedPage(gurl
, ¶ms
)) {
415 return prefetched_page_loaded
?
416 FULL_PRERENDERED_PAGE_LOAD
: PARTIAL_PRERENDERED_PAGE_LOAD
;
420 GURL
fixed_url(URLFixerUpper::FixupURL(gurl
.possibly_invalid_spec(),
422 if (!fixed_url
.is_valid())
423 return PAGE_LOAD_FAILED
;
425 if (!HandleNonNavigationAboutURL(fixed_url
)) {
426 // Notify the GoogleURLTracker of searches, it might want to change the
427 // actual Google site used (for instance when in the UK, google.co.uk, when
428 // in the US google.com).
429 // Note that this needs to happen before we initiate the navigation as the
430 // GoogleURLTracker uses the navigation pending notification to trigger the
432 if (google_util::IsGoogleSearchUrl(fixed_url
) &&
433 (page_transition
& content::PAGE_TRANSITION_GENERATED
)) {
434 GoogleURLTracker::GoogleURLSearchCommitted(GetProfile());
437 // Record UMA "ShowHistory" here. That way it'll pick up both user
438 // typing chrome://history as well as selecting from the drop down menu.
439 if (fixed_url
.spec() == chrome::kChromeUIHistoryURL
) {
440 content::RecordAction(base::UserMetricsAction("ShowHistory"));
443 content::NavigationController::LoadURLParams
load_params(fixed_url
);
444 if (j_extra_headers
) {
445 load_params
.extra_headers
= base::android::ConvertJavaStringToUTF8(
450 load_params
.load_type
=
451 content::NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
452 std::vector
<uint8
> post_data
;
453 base::android::JavaByteArrayToByteVector(env
, j_post_data
, &post_data
);
454 load_params
.browser_initiated_post_data
=
455 base::RefCountedBytes::TakeVector(&post_data
);
457 load_params
.transition_type
=
458 content::PageTransitionFromInt(page_transition
);
459 if (j_referrer_url
) {
460 load_params
.referrer
= content::Referrer(
461 GURL(base::android::ConvertJavaStringToUTF8(env
, j_referrer_url
)),
462 static_cast<blink::WebReferrerPolicy
>(referrer_policy
));
464 content_view
->LoadUrl(load_params
);
466 return DEFAULT_PAGE_LOAD
;
469 ToolbarModel::SecurityLevel
TabAndroid::GetSecurityLevel(JNIEnv
* env
,
471 return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
474 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv
* env
,
478 DCHECK(web_contents());
480 base::string16 title
;
482 title
= base::android::ConvertJavaStringToUTF16(env
, jtitle
);
486 url
= base::android::ConvertJavaStringToUTF8(env
, jurl
);
488 content::NavigationEntry
* entry
=
489 web_contents()->GetController().GetVisibleEntry();
490 if (entry
&& url
== entry
->GetVirtualURL().spec())
491 entry
->SetTitle(title
);
494 bool TabAndroid::Print(JNIEnv
* env
, jobject obj
) {
498 printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
499 printing::PrintViewManagerBasic
* print_view_manager
=
500 printing::PrintViewManagerBasic::FromWebContents(web_contents());
501 if (print_view_manager
== NULL
)
504 print_view_manager
->PrintNow();
508 prerender::PrerenderManager
* TabAndroid::GetPrerenderManager() const {
509 Profile
* profile
= GetProfile();
512 return prerender::PrerenderManagerFactory::GetForProfile(profile
);
515 static void Init(JNIEnv
* env
, jobject obj
) {
516 TRACE_EVENT0("native", "TabAndroid::Init");
517 // This will automatically bind to the Java object and pass ownership there.
518 new TabAndroid(env
, obj
);
521 bool TabAndroid::RegisterTabAndroid(JNIEnv
* env
) {
522 return RegisterNativesImpl(env
);