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. 4 5 #include "chrome/browser/android/tab_android.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_string.h" 9 #include "chrome/browser/android/chrome_web_contents_delegate_android.h" 10 #include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h" 11 #include "chrome/browser/browser_process.h" 12 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/content_settings/tab_specific_content_settings.h" 14 #include "chrome/browser/extensions/tab_helper.h" 15 #include "chrome/browser/favicon/favicon_tab_helper.h" 16 #include "chrome/browser/history/history_tab_helper.h" 17 #include "chrome/browser/infobars/infobar_service.h" 18 #include "chrome/browser/net/net_error_tab_helper.h" 19 #include "chrome/browser/password_manager/password_manager.h" 20 #include "chrome/browser/password_manager/password_manager_delegate_impl.h" 21 #include "chrome/browser/predictors/resource_prefetch_predictor_factory.h" 22 #include "chrome/browser/predictors/resource_prefetch_predictor_tab_helper.h" 23 #include "chrome/browser/prerender/prerender_tab_helper.h" 24 #include "chrome/browser/printing/print_view_manager_basic.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/browser/profiles/profile_android.h" 27 #include "chrome/browser/sessions/session_tab_helper.h" 28 #include "chrome/browser/ssl/ssl_tab_helper.h" 29 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h" 30 #include "chrome/browser/tab_contents/navigation_metrics_recorder.h" 31 #include "chrome/browser/translate/translate_tab_helper.h" 32 #include "chrome/browser/ui/alternate_error_tab_observer.h" 33 #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h" 34 #include "chrome/browser/ui/android/context_menu_helper.h" 35 #include "chrome/browser/ui/android/infobars/infobar_container_android.h" 36 #include "chrome/browser/ui/android/tab_model/tab_model.h" 37 #include "chrome/browser/ui/android/tab_model/tab_model_list.h" 38 #include "chrome/browser/ui/android/window_android_helper.h" 39 #include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h" 40 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h" 41 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h" 42 #include "chrome/browser/ui/browser_tab_contents.h" 43 #include "chrome/browser/ui/find_bar/find_tab_helper.h" 44 #include "chrome/browser/ui/prefs/prefs_tab_helper.h" 45 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" 46 #include "chrome/browser/ui/toolbar/toolbar_model_impl.h" 47 #include "components/autofill/content/browser/autofill_driver_impl.h" 48 #include "content/public/browser/android/content_view_core.h" 49 #include "content/public/browser/navigation_entry.h" 50 #include "content/public/browser/notification_service.h" 51 #include "content/public/browser/web_contents.h" 52 #include "extensions/browser/view_type_utils.h" 53 #include "jni/TabBase_jni.h" 54 55 #if defined(ENABLE_MANAGED_USERS) 56 #include "chrome/browser/managed_mode/managed_mode_navigation_observer.h" 57 #endif 58 59 namespace { 60 61 const char kTabHelpersInitializedUserDataKey[] = 62 "TabAndroidTabHelpersInitialized"; 63 64 } // namespace 65 66 void BrowserTabContents::AttachTabHelpers(content::WebContents* contents) { 67 // If already initialized, nothing to be done. 68 base::SupportsUserData::Data* initialization_tag = 69 contents->GetUserData(&kTabHelpersInitializedUserDataKey); 70 if (initialization_tag) 71 return; 72 73 // Mark as initialized. 74 contents->SetUserData(&kTabHelpersInitializedUserDataKey, 75 new base::SupportsUserData::Data()); 76 77 // Set the view type. 78 extensions::SetViewType(contents, extensions::VIEW_TYPE_TAB_CONTENTS); 79 80 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); 81 82 // SessionTabHelper comes first because it sets up the tab ID, and other 83 // helpers may rely on that. 84 SessionTabHelper::CreateForWebContents(contents); 85 86 AlternateErrorPageTabObserver::CreateForWebContents(contents); 87 autofill::TabAutofillManagerDelegate::CreateForWebContents(contents); 88 autofill::AutofillDriverImpl::CreateForWebContentsAndDelegate( 89 contents, 90 autofill::TabAutofillManagerDelegate::FromWebContents(contents), 91 g_browser_process->GetApplicationLocale(), 92 autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER); 93 BookmarkTabHelper::CreateForWebContents(contents); 94 ContextMenuHelper::CreateForWebContents(contents); 95 CoreTabHelper::CreateForWebContents(contents); 96 extensions::TabHelper::CreateForWebContents(contents); 97 FaviconTabHelper::CreateForWebContents(contents); 98 FindTabHelper::CreateForWebContents(contents); 99 HistoryTabHelper::CreateForWebContents(contents); 100 InfoBarService::CreateForWebContents(contents); 101 NavigationMetricsRecorder::CreateForWebContents(contents); 102 chrome_browser_net::NetErrorTabHelper::CreateForWebContents(contents); 103 PasswordManagerDelegateImpl::CreateForWebContents(contents); 104 PasswordManager::CreateForWebContentsAndDelegate( 105 contents, PasswordManagerDelegateImpl::FromWebContents(contents)); 106 PopupBlockerTabHelper::CreateForWebContents(contents); 107 PrefsTabHelper::CreateForWebContents(contents); 108 prerender::PrerenderTabHelper::CreateForWebContentsWithPasswordManager( 109 contents, PasswordManager::FromWebContents(contents)); 110 SingleTabModeTabHelper::CreateForWebContents(contents); 111 SSLTabHelper::CreateForWebContents(contents); 112 TabSpecificContentSettings::CreateForWebContents(contents); 113 TranslateTabHelper::CreateForWebContents(contents); 114 WindowAndroidHelper::CreateForWebContents(contents); 115 116 if (predictors::ResourcePrefetchPredictorFactory::GetForProfile(profile)) { 117 predictors::ResourcePrefetchPredictorTabHelper::CreateForWebContents( 118 contents); 119 } 120 121 #if defined(ENABLE_MANAGED_USERS) 122 if (profile->IsManaged()) 123 ManagedModeNavigationObserver::CreateForWebContents(contents); 124 #endif 125 } 126 127 // TODO(dtrainor): Refactor so we do not need this method. 128 void TabAndroid::InitTabHelpers(content::WebContents* contents) { 129 BrowserTabContents::AttachTabHelpers(contents); 130 } 131 132 TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) { 133 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents); 134 if (!core_tab_helper) 135 return NULL; 136 137 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate(); 138 if (!core_delegate) 139 return NULL; 140 141 return static_cast<TabAndroid*>(core_delegate); 142 } 143 144 TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) { 145 return reinterpret_cast<TabAndroid*>(Java_TabBase_getNativePtr(env, obj)); 146 } 147 148 TabAndroid::TabAndroid(JNIEnv* env, jobject obj) 149 : weak_java_tab_(env, obj), 150 session_tab_id_(), 151 synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) { 152 Java_TabBase_setNativePtr(env, obj, reinterpret_cast<intptr_t>(this)); 153 } 154 155 TabAndroid::~TabAndroid() { 156 JNIEnv* env = base::android::AttachCurrentThread(); 157 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); 158 if (obj.is_null()) 159 return; 160 161 Java_TabBase_clearNativePtr(env, obj.obj()); 162 } 163 164 int TabAndroid::GetAndroidId() const { 165 JNIEnv* env = base::android::AttachCurrentThread(); 166 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); 167 if (obj.is_null()) 168 return -1; 169 return Java_TabBase_getId(env, obj.obj()); 170 } 171 172 int TabAndroid::GetSyncId() const { 173 JNIEnv* env = base::android::AttachCurrentThread(); 174 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); 175 if (obj.is_null()) 176 return 0; 177 return Java_TabBase_getSyncId(env, obj.obj()); 178 } 179 180 base::string16 TabAndroid::GetTitle() const { 181 JNIEnv* env = base::android::AttachCurrentThread(); 182 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); 183 if (obj.is_null()) 184 return base::string16(); 185 return base::android::ConvertJavaStringToUTF16( 186 Java_TabBase_getTitle(env, obj.obj())); 187 } 188 189 GURL TabAndroid::GetURL() const { 190 JNIEnv* env = base::android::AttachCurrentThread(); 191 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); 192 if (obj.is_null()) 193 return GURL::EmptyGURL(); 194 return GURL(base::android::ConvertJavaStringToUTF8( 195 Java_TabBase_getUrl(env, obj.obj()))); 196 } 197 198 bool TabAndroid::RestoreIfNeeded() { 199 JNIEnv* env = base::android::AttachCurrentThread(); 200 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); 201 if (obj.is_null()) 202 return false; 203 return Java_TabBase_restoreIfNeeded(env, obj.obj()); 204 } 205 206 content::ContentViewCore* TabAndroid::GetContentViewCore() const { 207 if (!web_contents()) 208 return NULL; 209 210 return content::ContentViewCore::FromWebContents(web_contents()); 211 } 212 213 Profile* TabAndroid::GetProfile() const { 214 if (!web_contents()) 215 return NULL; 216 217 return Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 218 } 219 220 browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const { 221 return synced_tab_delegate_.get(); 222 } 223 224 void TabAndroid::SetSyncId(int sync_id) { 225 JNIEnv* env = base::android::AttachCurrentThread(); 226 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env); 227 if (obj.is_null()) 228 return; 229 Java_TabBase_setSyncId(env, obj.obj(), sync_id); 230 } 231 232 void TabAndroid::SwapTabContents(content::WebContents* old_contents, 233 content::WebContents* new_contents) { 234 JNIEnv* env = base::android::AttachCurrentThread(); 235 236 // We need to notify the native InfobarContainer so infobars can be swapped. 237 InfoBarContainerAndroid* infobar_container = 238 reinterpret_cast<InfoBarContainerAndroid*>( 239 Java_TabBase_getNativeInfoBarContainer( 240 env, 241 weak_java_tab_.get(env).obj())); 242 InfoBarService* new_infobar_service = new_contents ? 243 InfoBarService::FromWebContents(new_contents) : NULL; 244 infobar_container->ChangeInfoBarService(new_infobar_service); 245 246 Java_TabBase_swapWebContents( 247 env, 248 weak_java_tab_.get(env).obj(), 249 reinterpret_cast<intptr_t>(new_contents)); 250 } 251 252 void TabAndroid::Observe(int type, 253 const content::NotificationSource& source, 254 const content::NotificationDetails& details) { 255 JNIEnv* env = base::android::AttachCurrentThread(); 256 switch (type) { 257 case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED: { 258 TabSpecificContentSettings* settings = 259 TabSpecificContentSettings::FromWebContents(web_contents()); 260 if (!settings->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS)) { 261 // TODO(dfalcantara): Create an InfoBarDelegate to keep the 262 // PopupBlockedInfoBar logic native-side instead of straddling the JNI 263 // boundary. 264 int num_popups = 0; 265 PopupBlockerTabHelper* popup_blocker_helper = 266 PopupBlockerTabHelper::FromWebContents(web_contents()); 267 if (popup_blocker_helper) 268 num_popups = popup_blocker_helper->GetBlockedPopupsCount(); 269 270 if (num_popups > 0) { 271 PopupBlockedInfoBarDelegate::Create( 272 InfoBarService::FromWebContents(web_contents()), 273 num_popups); 274 } 275 276 settings->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS); 277 } 278 break; 279 } 280 case chrome::NOTIFICATION_FAVICON_UPDATED: 281 Java_TabBase_onFaviconUpdated(env, weak_java_tab_.get(env).obj()); 282 break; 283 default: 284 NOTREACHED() << "Unexpected notification " << type; 285 break; 286 } 287 } 288 289 void TabAndroid::InitWebContents(JNIEnv* env, 290 jobject obj, 291 jboolean incognito, 292 jobject jcontent_view_core, 293 jobject jweb_contents_delegate, 294 jobject jcontext_menu_populator) { 295 content::ContentViewCore* content_view_core = 296 content::ContentViewCore::GetNativeContentViewCore(env, 297 jcontent_view_core); 298 DCHECK(content_view_core); 299 DCHECK(content_view_core->GetWebContents()); 300 301 web_contents_.reset(content_view_core->GetWebContents()); 302 InitTabHelpers(web_contents_.get()); 303 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()); 316 317 notification_registrar_.Add( 318 this, 319 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, 320 content::Source<content::WebContents>(web_contents())); 321 notification_registrar_.Add( 322 this, 323 chrome::NOTIFICATION_FAVICON_UPDATED, 324 content::Source<content::WebContents>(web_contents())); 325 326 synced_tab_delegate_->SetWebContents(web_contents()); 327 328 // Set the window ID if there is a valid TabModel. 329 TabModel* model = TabModelList::GetTabModelWithProfile(GetProfile()); 330 if (model) { 331 SessionID window_id; 332 window_id.set_id(model->GetSessionId()); 333 334 SessionTabHelper* session_tab_helper = 335 SessionTabHelper::FromWebContents(web_contents()); 336 session_tab_helper->SetWindowID(window_id); 337 } 338 339 // Verify that the WebContents this tab represents matches the expected 340 // off the record state. 341 CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito); 342 } 343 344 void TabAndroid::DestroyWebContents(JNIEnv* env, 345 jobject obj, 346 jboolean delete_native) { 347 DCHECK(web_contents()); 348 349 notification_registrar_.Remove( 350 this, 351 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, 352 content::Source<content::WebContents>(web_contents())); 353 notification_registrar_.Remove( 354 this, 355 chrome::NOTIFICATION_FAVICON_UPDATED, 356 content::Source<content::WebContents>(web_contents())); 357 358 web_contents()->SetDelegate(NULL); 359 360 if (delete_native) { 361 web_contents_.reset(); 362 synced_tab_delegate_->ResetWebContents(); 363 } else { 364 // Release the WebContents so it does not get deleted by the scoped_ptr. 365 ignore_result(web_contents_.release()); 366 } 367 } 368 369 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetProfileAndroid( 370 JNIEnv* env, 371 jobject obj) { 372 Profile* profile = GetProfile(); 373 if (!profile) 374 return base::android::ScopedJavaLocalRef<jobject>(); 375 ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile); 376 if (!profile_android) 377 return base::android::ScopedJavaLocalRef<jobject>(); 378 379 return profile_android->GetJavaObject(); 380 } 381 382 ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env, 383 jobject obj) { 384 return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents()); 385 } 386 387 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv* env, 388 jobject obj, 389 jstring jurl, 390 jstring jtitle) { 391 DCHECK(web_contents()); 392 393 base::string16 title; 394 if (jtitle) 395 title = base::android::ConvertJavaStringToUTF16(env, jtitle); 396 397 std::string url; 398 if (jurl) 399 url = base::android::ConvertJavaStringToUTF8(env, jurl); 400 401 content::NavigationEntry* entry = 402 web_contents()->GetController().GetVisibleEntry(); 403 if (entry && url == entry->GetVirtualURL().spec()) 404 entry->SetTitle(title); 405 } 406 407 bool TabAndroid::Print(JNIEnv* env, jobject obj) { 408 if (!web_contents()) 409 return false; 410 411 printing::PrintViewManagerBasic::CreateForWebContents(web_contents()); 412 printing::PrintViewManagerBasic* print_view_manager = 413 printing::PrintViewManagerBasic::FromWebContents(web_contents()); 414 if (print_view_manager == NULL) 415 return false; 416 417 print_view_manager->PrintNow(); 418 return true; 419 } 420 421 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) { 422 return RegisterNativesImpl(env); 423 } 424