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
AttachTabHelpers(content::WebContents * contents)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.
InitTabHelpers(content::WebContents * contents)128 void TabAndroid::InitTabHelpers(content::WebContents* contents) {
129 BrowserTabContents::AttachTabHelpers(contents);
130 }
131
FromWebContents(content::WebContents * web_contents)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
GetNativeTab(JNIEnv * env,jobject obj)144 TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
145 return reinterpret_cast<TabAndroid*>(Java_TabBase_getNativePtr(env, obj));
146 }
147
TabAndroid(JNIEnv * env,jobject obj)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
~TabAndroid()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
GetAndroidId() const164 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
GetSyncId() const172 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
GetTitle() const180 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
GetURL() const189 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
RestoreIfNeeded()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
GetContentViewCore() const206 content::ContentViewCore* TabAndroid::GetContentViewCore() const {
207 if (!web_contents())
208 return NULL;
209
210 return content::ContentViewCore::FromWebContents(web_contents());
211 }
212
GetProfile() const213 Profile* TabAndroid::GetProfile() const {
214 if (!web_contents())
215 return NULL;
216
217 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
218 }
219
GetSyncedTabDelegate() const220 browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const {
221 return synced_tab_delegate_.get();
222 }
223
SetSyncId(int sync_id)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
SwapTabContents(content::WebContents * old_contents,content::WebContents * new_contents)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
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
InitWebContents(JNIEnv * env,jobject obj,jboolean incognito,jobject jcontent_view_core,jobject jweb_contents_delegate,jobject jcontext_menu_populator)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
DestroyWebContents(JNIEnv * env,jobject obj,jboolean delete_native)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
GetProfileAndroid(JNIEnv * env,jobject obj)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
GetSecurityLevel(JNIEnv * env,jobject obj)382 ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env,
383 jobject obj) {
384 return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
385 }
386
SetActiveNavigationEntryTitleForUrl(JNIEnv * env,jobject obj,jstring jurl,jstring jtitle)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
Print(JNIEnv * env,jobject obj)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
RegisterTabAndroid(JNIEnv * env)421 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
422 return RegisterNativesImpl(env);
423 }
424