// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/android/tab_android.h" #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "chrome/browser/android/chrome_web_contents_delegate_android.h" #include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/favicon/favicon_tab_helper.h" #include "chrome/browser/history/history_tab_helper.h" #include "chrome/browser/infobars/infobar_service.h" #include "chrome/browser/net/net_error_tab_helper.h" #include "chrome/browser/password_manager/password_manager.h" #include "chrome/browser/password_manager/password_manager_delegate_impl.h" #include "chrome/browser/predictors/resource_prefetch_predictor_factory.h" #include "chrome/browser/predictors/resource_prefetch_predictor_tab_helper.h" #include "chrome/browser/prerender/prerender_tab_helper.h" #include "chrome/browser/printing/print_view_manager_basic.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_android.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/ssl/ssl_tab_helper.h" #include "chrome/browser/sync/glue/synced_tab_delegate_android.h" #include "chrome/browser/tab_contents/navigation_metrics_recorder.h" #include "chrome/browser/translate/translate_tab_helper.h" #include "chrome/browser/ui/alternate_error_tab_observer.h" #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h" #include "chrome/browser/ui/android/context_menu_helper.h" #include "chrome/browser/ui/android/infobars/infobar_container_android.h" #include "chrome/browser/ui/android/tab_model/tab_model.h" #include "chrome/browser/ui/android/tab_model/tab_model_list.h" #include "chrome/browser/ui/android/window_android_helper.h" #include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h" #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h" #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h" #include "chrome/browser/ui/browser_tab_contents.h" #include "chrome/browser/ui/find_bar/find_tab_helper.h" #include "chrome/browser/ui/prefs/prefs_tab_helper.h" #include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/toolbar/toolbar_model_impl.h" #include "components/autofill/content/browser/autofill_driver_impl.h" #include "content/public/browser/android/content_view_core.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/view_type_utils.h" #include "jni/TabBase_jni.h" #if defined(ENABLE_MANAGED_USERS) #include "chrome/browser/managed_mode/managed_mode_navigation_observer.h" #endif namespace { const char kTabHelpersInitializedUserDataKey[] = "TabAndroidTabHelpersInitialized"; } // namespace void BrowserTabContents::AttachTabHelpers(content::WebContents* contents) { // If already initialized, nothing to be done. base::SupportsUserData::Data* initialization_tag = contents->GetUserData(&kTabHelpersInitializedUserDataKey); if (initialization_tag) return; // Mark as initialized. contents->SetUserData(&kTabHelpersInitializedUserDataKey, new base::SupportsUserData::Data()); // Set the view type. extensions::SetViewType(contents, extensions::VIEW_TYPE_TAB_CONTENTS); Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); // SessionTabHelper comes first because it sets up the tab ID, and other // helpers may rely on that. SessionTabHelper::CreateForWebContents(contents); AlternateErrorPageTabObserver::CreateForWebContents(contents); autofill::TabAutofillManagerDelegate::CreateForWebContents(contents); autofill::AutofillDriverImpl::CreateForWebContentsAndDelegate( contents, autofill::TabAutofillManagerDelegate::FromWebContents(contents), g_browser_process->GetApplicationLocale(), autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER); BookmarkTabHelper::CreateForWebContents(contents); ContextMenuHelper::CreateForWebContents(contents); CoreTabHelper::CreateForWebContents(contents); extensions::TabHelper::CreateForWebContents(contents); FaviconTabHelper::CreateForWebContents(contents); FindTabHelper::CreateForWebContents(contents); HistoryTabHelper::CreateForWebContents(contents); InfoBarService::CreateForWebContents(contents); NavigationMetricsRecorder::CreateForWebContents(contents); chrome_browser_net::NetErrorTabHelper::CreateForWebContents(contents); PasswordManagerDelegateImpl::CreateForWebContents(contents); PasswordManager::CreateForWebContentsAndDelegate( contents, PasswordManagerDelegateImpl::FromWebContents(contents)); PopupBlockerTabHelper::CreateForWebContents(contents); PrefsTabHelper::CreateForWebContents(contents); prerender::PrerenderTabHelper::CreateForWebContentsWithPasswordManager( contents, PasswordManager::FromWebContents(contents)); SingleTabModeTabHelper::CreateForWebContents(contents); SSLTabHelper::CreateForWebContents(contents); TabSpecificContentSettings::CreateForWebContents(contents); TranslateTabHelper::CreateForWebContents(contents); WindowAndroidHelper::CreateForWebContents(contents); if (predictors::ResourcePrefetchPredictorFactory::GetForProfile(profile)) { predictors::ResourcePrefetchPredictorTabHelper::CreateForWebContents( contents); } #if defined(ENABLE_MANAGED_USERS) if (profile->IsManaged()) ManagedModeNavigationObserver::CreateForWebContents(contents); #endif } // TODO(dtrainor): Refactor so we do not need this method. void TabAndroid::InitTabHelpers(content::WebContents* contents) { BrowserTabContents::AttachTabHelpers(contents); } TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) { CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents); if (!core_tab_helper) return NULL; CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate(); if (!core_delegate) return NULL; return static_cast(core_delegate); } TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) { return reinterpret_cast(Java_TabBase_getNativePtr(env, obj)); } TabAndroid::TabAndroid(JNIEnv* env, jobject obj) : weak_java_tab_(env, obj), session_tab_id_(), synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) { Java_TabBase_setNativePtr(env, obj, reinterpret_cast(this)); } TabAndroid::~TabAndroid() { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef obj = weak_java_tab_.get(env); if (obj.is_null()) return; Java_TabBase_clearNativePtr(env, obj.obj()); } int TabAndroid::GetAndroidId() const { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef obj = weak_java_tab_.get(env); if (obj.is_null()) return -1; return Java_TabBase_getId(env, obj.obj()); } int TabAndroid::GetSyncId() const { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef obj = weak_java_tab_.get(env); if (obj.is_null()) return 0; return Java_TabBase_getSyncId(env, obj.obj()); } base::string16 TabAndroid::GetTitle() const { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef obj = weak_java_tab_.get(env); if (obj.is_null()) return base::string16(); return base::android::ConvertJavaStringToUTF16( Java_TabBase_getTitle(env, obj.obj())); } GURL TabAndroid::GetURL() const { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef obj = weak_java_tab_.get(env); if (obj.is_null()) return GURL::EmptyGURL(); return GURL(base::android::ConvertJavaStringToUTF8( Java_TabBase_getUrl(env, obj.obj()))); } bool TabAndroid::RestoreIfNeeded() { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef obj = weak_java_tab_.get(env); if (obj.is_null()) return false; return Java_TabBase_restoreIfNeeded(env, obj.obj()); } content::ContentViewCore* TabAndroid::GetContentViewCore() const { if (!web_contents()) return NULL; return content::ContentViewCore::FromWebContents(web_contents()); } Profile* TabAndroid::GetProfile() const { if (!web_contents()) return NULL; return Profile::FromBrowserContext(web_contents()->GetBrowserContext()); } browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const { return synced_tab_delegate_.get(); } void TabAndroid::SetSyncId(int sync_id) { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef obj = weak_java_tab_.get(env); if (obj.is_null()) return; Java_TabBase_setSyncId(env, obj.obj(), sync_id); } void TabAndroid::SwapTabContents(content::WebContents* old_contents, content::WebContents* new_contents) { JNIEnv* env = base::android::AttachCurrentThread(); // We need to notify the native InfobarContainer so infobars can be swapped. InfoBarContainerAndroid* infobar_container = reinterpret_cast( Java_TabBase_getNativeInfoBarContainer( env, weak_java_tab_.get(env).obj())); InfoBarService* new_infobar_service = new_contents ? InfoBarService::FromWebContents(new_contents) : NULL; infobar_container->ChangeInfoBarService(new_infobar_service); Java_TabBase_swapWebContents( env, weak_java_tab_.get(env).obj(), reinterpret_cast(new_contents)); } void TabAndroid::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { JNIEnv* env = base::android::AttachCurrentThread(); switch (type) { case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED: { TabSpecificContentSettings* settings = TabSpecificContentSettings::FromWebContents(web_contents()); if (!settings->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS)) { // TODO(dfalcantara): Create an InfoBarDelegate to keep the // PopupBlockedInfoBar logic native-side instead of straddling the JNI // boundary. int num_popups = 0; PopupBlockerTabHelper* popup_blocker_helper = PopupBlockerTabHelper::FromWebContents(web_contents()); if (popup_blocker_helper) num_popups = popup_blocker_helper->GetBlockedPopupsCount(); if (num_popups > 0) { PopupBlockedInfoBarDelegate::Create( InfoBarService::FromWebContents(web_contents()), num_popups); } settings->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS); } break; } case chrome::NOTIFICATION_FAVICON_UPDATED: Java_TabBase_onFaviconUpdated(env, weak_java_tab_.get(env).obj()); break; default: NOTREACHED() << "Unexpected notification " << type; break; } } void TabAndroid::InitWebContents(JNIEnv* env, jobject obj, jboolean incognito, jobject jcontent_view_core, jobject jweb_contents_delegate, jobject jcontext_menu_populator) { content::ContentViewCore* content_view_core = content::ContentViewCore::GetNativeContentViewCore(env, jcontent_view_core); DCHECK(content_view_core); DCHECK(content_view_core->GetWebContents()); web_contents_.reset(content_view_core->GetWebContents()); InitTabHelpers(web_contents_.get()); session_tab_id_.set_id( SessionTabHelper::FromWebContents(web_contents())->session_id().id()); ContextMenuHelper::FromWebContents(web_contents())->SetPopulator( jcontext_menu_populator); WindowAndroidHelper::FromWebContents(web_contents())-> SetWindowAndroid(content_view_core->GetWindowAndroid()); CoreTabHelper::FromWebContents(web_contents())->set_delegate(this); web_contents_delegate_.reset( new chrome::android::ChromeWebContentsDelegateAndroid( env, jweb_contents_delegate)); web_contents_delegate_->LoadProgressChanged(web_contents(), 0); web_contents()->SetDelegate(web_contents_delegate_.get()); notification_registrar_.Add( this, chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, content::Source(web_contents())); notification_registrar_.Add( this, chrome::NOTIFICATION_FAVICON_UPDATED, content::Source(web_contents())); synced_tab_delegate_->SetWebContents(web_contents()); // Set the window ID if there is a valid TabModel. TabModel* model = TabModelList::GetTabModelWithProfile(GetProfile()); if (model) { SessionID window_id; window_id.set_id(model->GetSessionId()); SessionTabHelper* session_tab_helper = SessionTabHelper::FromWebContents(web_contents()); session_tab_helper->SetWindowID(window_id); } // Verify that the WebContents this tab represents matches the expected // off the record state. CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito); } void TabAndroid::DestroyWebContents(JNIEnv* env, jobject obj, jboolean delete_native) { DCHECK(web_contents()); notification_registrar_.Remove( this, chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, content::Source(web_contents())); notification_registrar_.Remove( this, chrome::NOTIFICATION_FAVICON_UPDATED, content::Source(web_contents())); web_contents()->SetDelegate(NULL); if (delete_native) { web_contents_.reset(); synced_tab_delegate_->ResetWebContents(); } else { // Release the WebContents so it does not get deleted by the scoped_ptr. ignore_result(web_contents_.release()); } } base::android::ScopedJavaLocalRef TabAndroid::GetProfileAndroid( JNIEnv* env, jobject obj) { Profile* profile = GetProfile(); if (!profile) return base::android::ScopedJavaLocalRef(); ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile); if (!profile_android) return base::android::ScopedJavaLocalRef(); return profile_android->GetJavaObject(); } ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env, jobject obj) { return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents()); } void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv* env, jobject obj, jstring jurl, jstring jtitle) { DCHECK(web_contents()); base::string16 title; if (jtitle) title = base::android::ConvertJavaStringToUTF16(env, jtitle); std::string url; if (jurl) url = base::android::ConvertJavaStringToUTF8(env, jurl); content::NavigationEntry* entry = web_contents()->GetController().GetVisibleEntry(); if (entry && url == entry->GetVirtualURL().spec()) entry->SetTitle(title); } bool TabAndroid::Print(JNIEnv* env, jobject obj) { if (!web_contents()) return false; printing::PrintViewManagerBasic::CreateForWebContents(web_contents()); printing::PrintViewManagerBasic* print_view_manager = printing::PrintViewManagerBasic::FromWebContents(web_contents()); if (print_view_manager == NULL) return false; print_view_manager->PrintNow(); return true; } bool TabAndroid::RegisterTabAndroid(JNIEnv* env) { return RegisterNativesImpl(env); }