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