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_factory.h"
16 #include "chrome/browser/infobars/infobar_service.h"
17 #include "chrome/browser/prerender/prerender_contents.h"
18 #include "chrome/browser/prerender/prerender_manager.h"
19 #include "chrome/browser/prerender/prerender_manager_factory.h"
20 #include "chrome/browser/printing/print_view_manager_basic.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/profiles/profile_android.h"
23 #include "chrome/browser/search/search.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/search/instant_search_prerenderer.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/url_constants.h"
39 #include "components/google/core/browser/google_url_tracker.h"
40 #include "components/google/core/browser/google_util.h"
41 #include "components/infobars/core/infobar_container.h"
42 #include "components/url_fixer/url_fixer.h"
43 #include "content/public/browser/android/content_view_core.h"
44 #include "content/public/browser/navigation_entry.h"
45 #include "content/public/browser/notification_service.h"
46 #include "content/public/browser/user_metrics.h"
47 #include "content/public/browser/web_contents.h"
48 #include "jni/Tab_jni.h"
49 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
51 TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
52 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents);
56 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
60 return static_cast<TabAndroid*>(core_delegate);
63 TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
64 return reinterpret_cast<TabAndroid*>(Java_Tab_getNativePtr(env, obj));
67 void TabAndroid::AttachTabHelpers(content::WebContents* web_contents) {
70 TabHelpers::AttachTabHelpers(web_contents);
73 TabAndroid::TabAndroid(JNIEnv* env, jobject obj)
74 : weak_java_tab_(env, obj),
75 synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) {
76 Java_Tab_setNativePtr(env, obj, reinterpret_cast<intptr_t>(this));
79 TabAndroid::~TabAndroid() {
80 JNIEnv* env = base::android::AttachCurrentThread();
81 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
85 Java_Tab_clearNativePtr(env, obj.obj());
88 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetJavaObject() {
89 JNIEnv* env = base::android::AttachCurrentThread();
90 return weak_java_tab_.get(env);
93 int TabAndroid::GetAndroidId() const {
94 JNIEnv* env = base::android::AttachCurrentThread();
95 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
98 return Java_Tab_getId(env, obj.obj());
101 int TabAndroid::GetSyncId() const {
102 JNIEnv* env = base::android::AttachCurrentThread();
103 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
106 return Java_Tab_getSyncId(env, obj.obj());
109 base::string16 TabAndroid::GetTitle() const {
110 JNIEnv* env = base::android::AttachCurrentThread();
111 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
113 return base::string16();
114 return base::android::ConvertJavaStringToUTF16(
115 Java_Tab_getTitle(env, obj.obj()));
118 GURL TabAndroid::GetURL() const {
119 JNIEnv* env = base::android::AttachCurrentThread();
120 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
122 return GURL::EmptyGURL();
123 return GURL(base::android::ConvertJavaStringToUTF8(
124 Java_Tab_getUrl(env, obj.obj())));
127 bool TabAndroid::LoadIfNeeded() {
128 JNIEnv* env = base::android::AttachCurrentThread();
129 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
132 return Java_Tab_loadIfNeeded(env, obj.obj());
135 content::ContentViewCore* TabAndroid::GetContentViewCore() const {
139 return content::ContentViewCore::FromWebContents(web_contents());
142 Profile* TabAndroid::GetProfile() const {
146 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
149 browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const {
150 return synced_tab_delegate_.get();
153 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id) {
154 session_window_id_.set_id(window_id);
159 SessionTabHelper* session_tab_helper =
160 SessionTabHelper::FromWebContents(web_contents());
161 session_tab_helper->SetWindowID(session_window_id_);
164 void TabAndroid::SetSyncId(int sync_id) {
165 JNIEnv* env = base::android::AttachCurrentThread();
166 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
169 Java_Tab_setSyncId(env, obj.obj(), sync_id);
172 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams* params) {
176 void TabAndroid::OnReceivedHttpAuthRequest(jobject auth_handler,
177 const base::string16& host,
178 const base::string16& realm) {
182 bool TabAndroid::ShouldWelcomePageLinkToTermsOfService() {
187 bool TabAndroid::HasPrerenderedUrl(GURL gurl) {
188 prerender::PrerenderManager* prerender_manager = GetPrerenderManager();
189 if (!prerender_manager)
192 std::vector<content::WebContents*> contents =
193 prerender_manager->GetAllPrerenderingContents();
194 prerender::PrerenderContents* prerender_contents;
195 for (size_t i = 0; i < contents.size(); ++i) {
196 prerender_contents = prerender_manager->
197 GetPrerenderContents(contents.at(i));
198 if (prerender_contents->prerender_url() == gurl &&
199 prerender_contents->has_finished_loading()) {
206 void TabAndroid::SwapTabContents(content::WebContents* old_contents,
207 content::WebContents* new_contents,
209 bool did_finish_load) {
210 JNIEnv* env = base::android::AttachCurrentThread();
212 // We need to notify the native InfobarContainer so infobars can be swapped.
213 InfoBarContainerAndroid* infobar_container =
214 reinterpret_cast<InfoBarContainerAndroid*>(
215 Java_Tab_getNativeInfoBarContainer(
217 weak_java_tab_.get(env).obj()));
218 InfoBarService* new_infobar_service =
219 new_contents ? InfoBarService::FromWebContents(new_contents) : NULL;
220 infobar_container->ChangeInfoBarManager(new_infobar_service);
222 Java_Tab_swapWebContents(
224 weak_java_tab_.get(env).obj(),
225 reinterpret_cast<intptr_t>(new_contents),
230 void TabAndroid::Observe(int type,
231 const content::NotificationSource& source,
232 const content::NotificationDetails& details) {
233 JNIEnv* env = base::android::AttachCurrentThread();
235 case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED: {
236 TabSpecificContentSettings* settings =
237 TabSpecificContentSettings::FromWebContents(web_contents());
238 if (!settings->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS)) {
239 // TODO(dfalcantara): Create an InfoBarDelegate to keep the
240 // PopupBlockedInfoBar logic native-side instead of straddling the JNI
243 PopupBlockerTabHelper* popup_blocker_helper =
244 PopupBlockerTabHelper::FromWebContents(web_contents());
245 if (popup_blocker_helper)
246 num_popups = popup_blocker_helper->GetBlockedPopupsCount();
249 PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups);
251 settings->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS);
255 case chrome::NOTIFICATION_FAVICON_UPDATED:
256 Java_Tab_onFaviconUpdated(env, weak_java_tab_.get(env).obj());
258 case content::NOTIFICATION_NAV_ENTRY_CHANGED:
259 Java_Tab_onNavEntryChanged(env, weak_java_tab_.get(env).obj());
262 NOTREACHED() << "Unexpected notification " << type;
267 void TabAndroid::Destroy(JNIEnv* env, jobject obj) {
271 void TabAndroid::InitWebContents(JNIEnv* env,
274 jobject jcontent_view_core,
275 jobject jweb_contents_delegate,
276 jobject jcontext_menu_populator) {
277 content::ContentViewCore* content_view_core =
278 content::ContentViewCore::GetNativeContentViewCore(env,
280 DCHECK(content_view_core);
281 DCHECK(content_view_core->GetWebContents());
283 web_contents_.reset(content_view_core->GetWebContents());
284 AttachTabHelpers(web_contents_.get());
286 SetWindowSessionID(session_window_id_.id());
288 session_tab_id_.set_id(
289 SessionTabHelper::FromWebContents(web_contents())->session_id().id());
290 ContextMenuHelper::FromWebContents(web_contents())->SetPopulator(
291 jcontext_menu_populator);
292 WindowAndroidHelper::FromWebContents(web_contents())->
293 SetWindowAndroid(content_view_core->GetWindowAndroid());
294 CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
295 web_contents_delegate_.reset(
296 new chrome::android::ChromeWebContentsDelegateAndroid(
297 env, jweb_contents_delegate));
298 web_contents_delegate_->LoadProgressChanged(web_contents(), 0);
299 web_contents()->SetDelegate(web_contents_delegate_.get());
301 notification_registrar_.Add(
303 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
304 content::Source<content::WebContents>(web_contents()));
305 notification_registrar_.Add(
307 chrome::NOTIFICATION_FAVICON_UPDATED,
308 content::Source<content::WebContents>(web_contents()));
309 notification_registrar_.Add(
311 content::NOTIFICATION_NAV_ENTRY_CHANGED,
312 content::Source<content::NavigationController>(
313 &web_contents()->GetController()));
315 synced_tab_delegate_->SetWebContents(web_contents());
317 // Verify that the WebContents this tab represents matches the expected
318 // off the record state.
319 CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito);
322 void TabAndroid::DestroyWebContents(JNIEnv* env,
324 jboolean delete_native) {
325 DCHECK(web_contents());
327 notification_registrar_.Remove(
329 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
330 content::Source<content::WebContents>(web_contents()));
331 notification_registrar_.Remove(
333 chrome::NOTIFICATION_FAVICON_UPDATED,
334 content::Source<content::WebContents>(web_contents()));
335 notification_registrar_.Remove(
337 content::NOTIFICATION_NAV_ENTRY_CHANGED,
338 content::Source<content::NavigationController>(
339 &web_contents()->GetController()));
341 web_contents()->SetDelegate(NULL);
344 web_contents_.reset();
345 synced_tab_delegate_->ResetWebContents();
347 // Release the WebContents so it does not get deleted by the scoped_ptr.
348 ignore_result(web_contents_.release());
352 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetWebContents(
355 if (!web_contents_.get())
356 return base::android::ScopedJavaLocalRef<jobject>();
357 return web_contents_->GetJavaWebContents();
360 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetProfileAndroid(
363 Profile* profile = GetProfile();
365 return base::android::ScopedJavaLocalRef<jobject>();
366 ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile);
367 if (!profile_android)
368 return base::android::ScopedJavaLocalRef<jobject>();
370 return profile_android->GetJavaObject();
373 TabAndroid::TabLoadStatus TabAndroid::LoadUrl(JNIEnv* env,
376 jstring j_extra_headers,
377 jbyteArray j_post_data,
378 jint page_transition,
379 jstring j_referrer_url,
380 jint referrer_policy,
381 jboolean is_renderer_initiated) {
382 content::ContentViewCore* content_view = GetContentViewCore();
384 return PAGE_LOAD_FAILED;
386 GURL gurl(base::android::ConvertJavaStringToUTF8(env, url));
388 return PAGE_LOAD_FAILED;
390 // If the page was prerendered, use it.
391 // Note in incognito mode, we don't have a PrerenderManager.
393 prerender::PrerenderManager* prerender_manager =
394 prerender::PrerenderManagerFactory::GetForProfile(GetProfile());
395 if (prerender_manager) {
396 bool prefetched_page_loaded = HasPrerenderedUrl(gurl);
397 // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
398 chrome::NavigateParams params(NULL, web_contents());
399 InstantSearchPrerenderer* prerenderer =
400 InstantSearchPrerenderer::GetForProfile(GetProfile());
402 const base::string16& search_terms =
403 chrome::ExtractSearchTermsFromURL(GetProfile(), gurl);
404 if (!search_terms.empty() &&
405 prerenderer->CanCommitQuery(web_contents_.get(), search_terms)) {
406 prerenderer->Commit(search_terms);
408 if (prerenderer->UsePrerenderedPage(gurl, ¶ms))
409 return FULL_PRERENDERED_PAGE_LOAD;
411 prerenderer->Cancel();
413 if (prerender_manager->MaybeUsePrerenderedPage(gurl, ¶ms)) {
414 return prefetched_page_loaded ?
415 FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
420 url_fixer::FixupURL(gurl.possibly_invalid_spec(), std::string()));
421 if (!fixed_url.is_valid())
422 return PAGE_LOAD_FAILED;
424 if (!HandleNonNavigationAboutURL(fixed_url)) {
425 // Notify the GoogleURLTracker of searches, it might want to change the
426 // actual Google site used (for instance when in the UK, google.co.uk, when
427 // in the US google.com).
428 // Note that this needs to happen before we initiate the navigation as the
429 // GoogleURLTracker uses the navigation pending notification to trigger the
431 if (google_util::IsGoogleSearchUrl(fixed_url) &&
432 (page_transition & content::PAGE_TRANSITION_GENERATED)) {
433 GoogleURLTracker* tracker =
434 GoogleURLTrackerFactory::GetForProfile(GetProfile());
436 tracker->SearchCommitted();
439 // Record UMA "ShowHistory" here. That way it'll pick up both user
440 // typing chrome://history as well as selecting from the drop down menu.
441 if (fixed_url.spec() == chrome::kChromeUIHistoryURL) {
442 content::RecordAction(base::UserMetricsAction("ShowHistory"));
445 content::NavigationController::LoadURLParams load_params(fixed_url);
446 if (j_extra_headers) {
447 load_params.extra_headers = base::android::ConvertJavaStringToUTF8(
452 load_params.load_type =
453 content::NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
454 std::vector<uint8> post_data;
455 base::android::JavaByteArrayToByteVector(env, j_post_data, &post_data);
456 load_params.browser_initiated_post_data =
457 base::RefCountedBytes::TakeVector(&post_data);
459 load_params.transition_type =
460 content::PageTransitionFromInt(page_transition);
461 if (j_referrer_url) {
462 load_params.referrer = content::Referrer(
463 GURL(base::android::ConvertJavaStringToUTF8(env, j_referrer_url)),
464 static_cast<blink::WebReferrerPolicy>(referrer_policy));
466 const base::string16 search_terms =
467 chrome::ExtractSearchTermsFromURL(GetProfile(), gurl);
468 SearchTabHelper* search_tab_helper =
469 SearchTabHelper::FromWebContents(web_contents_.get());
470 if (!search_terms.empty() && search_tab_helper &&
471 search_tab_helper->SupportsInstant()) {
472 search_tab_helper->Submit(search_terms);
473 return DEFAULT_PAGE_LOAD;
475 load_params.is_renderer_initiated = is_renderer_initiated;
476 content_view->LoadUrl(load_params);
478 return DEFAULT_PAGE_LOAD;
481 ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env,
483 return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
486 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv* env,
490 DCHECK(web_contents());
492 base::string16 title;
494 title = base::android::ConvertJavaStringToUTF16(env, jtitle);
498 url = base::android::ConvertJavaStringToUTF8(env, jurl);
500 content::NavigationEntry* entry =
501 web_contents()->GetController().GetVisibleEntry();
502 if (entry && url == entry->GetVirtualURL().spec())
503 entry->SetTitle(title);
506 bool TabAndroid::Print(JNIEnv* env, jobject obj) {
510 printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
511 printing::PrintViewManagerBasic* print_view_manager =
512 printing::PrintViewManagerBasic::FromWebContents(web_contents());
513 if (print_view_manager == NULL)
516 print_view_manager->PrintNow();
520 prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
521 Profile* profile = GetProfile();
524 return prerender::PrerenderManagerFactory::GetForProfile(profile);
527 static void Init(JNIEnv* env, jobject obj) {
528 TRACE_EVENT0("native", "TabAndroid::Init");
529 // This will automatically bind to the Java object and pass ownership there.
530 new TabAndroid(env, obj);
533 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
534 return RegisterNativesImpl(env);