baa0a000cb2aeef6856c7b08386b17534184d8da
[platform/framework/web/crosswalk.git] / src / chrome / browser / android / tab_android.cc
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_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"
50
51 TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
52   CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents);
53   if (!core_tab_helper)
54     return NULL;
55
56   CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
57   if (!core_delegate)
58     return NULL;
59
60   return static_cast<TabAndroid*>(core_delegate);
61 }
62
63 TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
64   return reinterpret_cast<TabAndroid*>(Java_Tab_getNativePtr(env, obj));
65 }
66
67 void TabAndroid::AttachTabHelpers(content::WebContents* web_contents) {
68   DCHECK(web_contents);
69
70   TabHelpers::AttachTabHelpers(web_contents);
71 }
72
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));
77 }
78
79 TabAndroid::~TabAndroid() {
80   JNIEnv* env = base::android::AttachCurrentThread();
81   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
82   if (obj.is_null())
83     return;
84
85   Java_Tab_clearNativePtr(env, obj.obj());
86 }
87
88 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetJavaObject() {
89   JNIEnv* env = base::android::AttachCurrentThread();
90   return weak_java_tab_.get(env);
91 }
92
93 int TabAndroid::GetAndroidId() const {
94   JNIEnv* env = base::android::AttachCurrentThread();
95   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
96   if (obj.is_null())
97     return -1;
98   return Java_Tab_getId(env, obj.obj());
99 }
100
101 int TabAndroid::GetSyncId() const {
102   JNIEnv* env = base::android::AttachCurrentThread();
103   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
104   if (obj.is_null())
105     return 0;
106   return Java_Tab_getSyncId(env, obj.obj());
107 }
108
109 base::string16 TabAndroid::GetTitle() const {
110   JNIEnv* env = base::android::AttachCurrentThread();
111   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
112   if (obj.is_null())
113     return base::string16();
114   return base::android::ConvertJavaStringToUTF16(
115       Java_Tab_getTitle(env, obj.obj()));
116 }
117
118 GURL TabAndroid::GetURL() const {
119   JNIEnv* env = base::android::AttachCurrentThread();
120   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
121   if (obj.is_null())
122     return GURL::EmptyGURL();
123   return GURL(base::android::ConvertJavaStringToUTF8(
124       Java_Tab_getUrl(env, obj.obj())));
125 }
126
127 bool TabAndroid::LoadIfNeeded() {
128   JNIEnv* env = base::android::AttachCurrentThread();
129   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
130   if (obj.is_null())
131     return false;
132   return Java_Tab_loadIfNeeded(env, obj.obj());
133 }
134
135 content::ContentViewCore* TabAndroid::GetContentViewCore() const {
136   if (!web_contents())
137     return NULL;
138
139   return content::ContentViewCore::FromWebContents(web_contents());
140 }
141
142 Profile* TabAndroid::GetProfile() const {
143   if (!web_contents())
144     return NULL;
145
146   return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
147 }
148
149 browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const {
150   return synced_tab_delegate_.get();
151 }
152
153 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id) {
154   session_window_id_.set_id(window_id);
155
156   if (!web_contents())
157     return;
158
159   SessionTabHelper* session_tab_helper =
160           SessionTabHelper::FromWebContents(web_contents());
161   session_tab_helper->SetWindowID(session_window_id_);
162 }
163
164 void TabAndroid::SetSyncId(int sync_id) {
165   JNIEnv* env = base::android::AttachCurrentThread();
166   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
167   if (obj.is_null())
168     return;
169   Java_Tab_setSyncId(env, obj.obj(), sync_id);
170 }
171
172 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams* params) {
173   NOTIMPLEMENTED();
174 }
175
176 void TabAndroid::OnReceivedHttpAuthRequest(jobject auth_handler,
177                                            const base::string16& host,
178                                            const base::string16& realm) {
179   NOTIMPLEMENTED();
180 }
181
182 bool TabAndroid::ShouldWelcomePageLinkToTermsOfService() {
183   NOTIMPLEMENTED();
184   return false;
185 }
186
187 bool TabAndroid::HasPrerenderedUrl(GURL gurl) {
188   prerender::PrerenderManager* prerender_manager = GetPrerenderManager();
189   if (!prerender_manager)
190     return false;
191
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()) {
200       return true;
201     }
202   }
203   return false;
204 }
205
206 void TabAndroid::SwapTabContents(content::WebContents* old_contents,
207                                  content::WebContents* new_contents,
208                                  bool did_start_load,
209                                  bool did_finish_load) {
210   JNIEnv* env = base::android::AttachCurrentThread();
211
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(
216               env,
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);
221
222   Java_Tab_swapWebContents(
223       env,
224       weak_java_tab_.get(env).obj(),
225       reinterpret_cast<intptr_t>(new_contents),
226       did_start_load,
227       did_finish_load);
228 }
229
230 void TabAndroid::Observe(int type,
231                          const content::NotificationSource& source,
232                          const content::NotificationDetails& details) {
233   JNIEnv* env = base::android::AttachCurrentThread();
234   switch (type) {
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
241         // boundary.
242         int num_popups = 0;
243         PopupBlockerTabHelper* popup_blocker_helper =
244             PopupBlockerTabHelper::FromWebContents(web_contents());
245         if (popup_blocker_helper)
246           num_popups = popup_blocker_helper->GetBlockedPopupsCount();
247
248         if (num_popups > 0)
249           PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups);
250
251         settings->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS);
252       }
253       break;
254     }
255     case chrome::NOTIFICATION_FAVICON_UPDATED:
256       Java_Tab_onFaviconUpdated(env, weak_java_tab_.get(env).obj());
257       break;
258     case content::NOTIFICATION_NAV_ENTRY_CHANGED:
259       Java_Tab_onNavEntryChanged(env, weak_java_tab_.get(env).obj());
260       break;
261     default:
262       NOTREACHED() << "Unexpected notification " << type;
263       break;
264   }
265 }
266
267 void TabAndroid::Destroy(JNIEnv* env, jobject obj) {
268   delete this;
269 }
270
271 void TabAndroid::InitWebContents(JNIEnv* env,
272                                  jobject obj,
273                                  jboolean incognito,
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,
279                                                          jcontent_view_core);
280   DCHECK(content_view_core);
281   DCHECK(content_view_core->GetWebContents());
282
283   web_contents_.reset(content_view_core->GetWebContents());
284   AttachTabHelpers(web_contents_.get());
285
286   SetWindowSessionID(session_window_id_.id());
287
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());
300
301   notification_registrar_.Add(
302       this,
303       chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
304       content::Source<content::WebContents>(web_contents()));
305   notification_registrar_.Add(
306       this,
307       chrome::NOTIFICATION_FAVICON_UPDATED,
308       content::Source<content::WebContents>(web_contents()));
309   notification_registrar_.Add(
310       this,
311       content::NOTIFICATION_NAV_ENTRY_CHANGED,
312       content::Source<content::NavigationController>(
313            &web_contents()->GetController()));
314
315   synced_tab_delegate_->SetWebContents(web_contents());
316
317   // Verify that the WebContents this tab represents matches the expected
318   // off the record state.
319   CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito);
320 }
321
322 void TabAndroid::DestroyWebContents(JNIEnv* env,
323                                     jobject obj,
324                                     jboolean delete_native) {
325   DCHECK(web_contents());
326
327   notification_registrar_.Remove(
328       this,
329       chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
330       content::Source<content::WebContents>(web_contents()));
331   notification_registrar_.Remove(
332       this,
333       chrome::NOTIFICATION_FAVICON_UPDATED,
334       content::Source<content::WebContents>(web_contents()));
335   notification_registrar_.Remove(
336       this,
337       content::NOTIFICATION_NAV_ENTRY_CHANGED,
338       content::Source<content::NavigationController>(
339            &web_contents()->GetController()));
340
341   web_contents()->SetDelegate(NULL);
342
343   if (delete_native) {
344     web_contents_.reset();
345     synced_tab_delegate_->ResetWebContents();
346   } else {
347     // Release the WebContents so it does not get deleted by the scoped_ptr.
348     ignore_result(web_contents_.release());
349   }
350 }
351
352 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetWebContents(
353     JNIEnv* env,
354     jobject obj) {
355   if (!web_contents_.get())
356     return base::android::ScopedJavaLocalRef<jobject>();
357   return web_contents_->GetJavaWebContents();
358 }
359
360 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetProfileAndroid(
361     JNIEnv* env,
362     jobject obj) {
363   Profile* profile = GetProfile();
364   if (!profile)
365     return base::android::ScopedJavaLocalRef<jobject>();
366   ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile);
367   if (!profile_android)
368     return base::android::ScopedJavaLocalRef<jobject>();
369
370   return profile_android->GetJavaObject();
371 }
372
373 TabAndroid::TabLoadStatus TabAndroid::LoadUrl(JNIEnv* env,
374                                               jobject obj,
375                                               jstring url,
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();
383   if (!content_view)
384     return PAGE_LOAD_FAILED;
385
386   GURL gurl(base::android::ConvertJavaStringToUTF8(env, url));
387   if (gurl.is_empty())
388     return PAGE_LOAD_FAILED;
389
390   // If the page was prerendered, use it.
391   // Note in incognito mode, we don't have a PrerenderManager.
392
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());
401     if (prerenderer) {
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);
407
408         if (prerenderer->UsePrerenderedPage(gurl, &params))
409           return FULL_PRERENDERED_PAGE_LOAD;
410       }
411       prerenderer->Cancel();
412     }
413     if (prerender_manager->MaybeUsePrerenderedPage(gurl, &params)) {
414       return prefetched_page_loaded ?
415           FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
416     }
417   }
418
419   GURL fixed_url(
420       url_fixer::FixupURL(gurl.possibly_invalid_spec(), std::string()));
421   if (!fixed_url.is_valid())
422     return PAGE_LOAD_FAILED;
423
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
430     // infobar.
431     if (google_util::IsGoogleSearchUrl(fixed_url) &&
432         (page_transition & content::PAGE_TRANSITION_GENERATED)) {
433       GoogleURLTracker* tracker =
434           GoogleURLTrackerFactory::GetForProfile(GetProfile());
435       if (tracker)
436         tracker->SearchCommitted();
437     }
438
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"));
443     }
444
445     content::NavigationController::LoadURLParams load_params(fixed_url);
446     if (j_extra_headers) {
447       load_params.extra_headers = base::android::ConvertJavaStringToUTF8(
448           env,
449           j_extra_headers);
450     }
451     if (j_post_data) {
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);
458     }
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));
465     }
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;
474     }
475     load_params.is_renderer_initiated = is_renderer_initiated;
476     content_view->LoadUrl(load_params);
477   }
478   return DEFAULT_PAGE_LOAD;
479 }
480
481 ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env,
482                                                          jobject obj) {
483   return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
484 }
485
486 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv* env,
487                                                      jobject obj,
488                                                      jstring jurl,
489                                                      jstring jtitle) {
490   DCHECK(web_contents());
491
492   base::string16 title;
493   if (jtitle)
494     title = base::android::ConvertJavaStringToUTF16(env, jtitle);
495
496   std::string url;
497   if (jurl)
498     url = base::android::ConvertJavaStringToUTF8(env, jurl);
499
500   content::NavigationEntry* entry =
501       web_contents()->GetController().GetVisibleEntry();
502   if (entry && url == entry->GetVirtualURL().spec())
503     entry->SetTitle(title);
504 }
505
506 bool TabAndroid::Print(JNIEnv* env, jobject obj) {
507   if (!web_contents())
508     return false;
509
510   printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
511   printing::PrintViewManagerBasic* print_view_manager =
512       printing::PrintViewManagerBasic::FromWebContents(web_contents());
513   if (print_view_manager == NULL)
514     return false;
515
516   print_view_manager->PrintNow();
517   return true;
518 }
519
520 prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
521   Profile* profile = GetProfile();
522   if (!profile)
523     return NULL;
524   return prerender::PrerenderManagerFactory::GetForProfile(profile);
525 }
526
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);
531 }
532
533 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
534   return RegisterNativesImpl(env);
535 }