0e40ea06c81f9cf24afb65a8a8f3d6a1f696f03b
[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/favicon/favicon_tab_helper.h"
16 #include "chrome/browser/google/google_url_tracker_factory.h"
17 #include "chrome/browser/infobars/infobar_service.h"
18 #include "chrome/browser/prerender/prerender_contents.h"
19 #include "chrome/browser/prerender/prerender_manager.h"
20 #include "chrome/browser/prerender/prerender_manager_factory.h"
21 #include "chrome/browser/printing/print_view_manager_basic.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/profiles/profile_android.h"
24 #include "chrome/browser/search/search.h"
25 #include "chrome/browser/sessions/session_tab_helper.h"
26 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
27 #include "chrome/browser/tab_contents/tab_util.h"
28 #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
29 #include "chrome/browser/ui/android/context_menu_helper.h"
30 #include "chrome/browser/ui/android/infobars/infobar_container_android.h"
31 #include "chrome/browser/ui/android/tab_model/tab_model.h"
32 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
33 #include "chrome/browser/ui/android/window_android_helper.h"
34 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
35 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
36 #include "chrome/browser/ui/search/search_tab_helper.h"
37 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
38 #include "chrome/browser/ui/tab_helpers.h"
39 #include "chrome/browser/ui/toolbar/toolbar_model_impl.h"
40 #include "chrome/common/url_constants.h"
41 #include "components/google/core/browser/google_url_tracker.h"
42 #include "components/google/core/browser/google_util.h"
43 #include "components/infobars/core/infobar_container.h"
44 #include "components/url_fixer/url_fixer.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/user_metrics.h"
49 #include "content/public/browser/web_contents.h"
50 #include "jni/Tab_jni.h"
51 #include "skia/ext/image_operations.h"
52 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
53 #include "ui/base/resource/resource_bundle.h"
54 #include "ui/base/window_open_disposition.h"
55 #include "ui/gfx/android/device_display_info.h"
56 #include "ui/gfx/android/java_bitmap.h"
57 #include "ui/gfx/favicon_size.h"
58 #include "ui/gfx/image/image_skia.h"
59
60 using content::GlobalRequestID;
61 using content::NavigationController;
62 using content::WebContents;
63
64 namespace {
65
66 WebContents* CreateTargetContents(const chrome::NavigateParams& params,
67                                   const GURL& url) {
68   Profile* profile = params.initiating_profile;
69
70   if (profile->IsOffTheRecord() || params.disposition == OFF_THE_RECORD) {
71     profile = profile->GetOffTheRecordProfile();
72   }
73   WebContents::CreateParams create_params(
74       profile, tab_util::GetSiteInstanceForNewTab(profile, url));
75   if (params.source_contents) {
76     create_params.initial_size =
77         params.source_contents->GetContainerBounds().size();
78     if (params.should_set_opener)
79       create_params.opener = params.source_contents;
80   }
81   if (params.disposition == NEW_BACKGROUND_TAB)
82     create_params.initially_hidden = true;
83
84   WebContents* target_contents = WebContents::Create(create_params);
85
86   return target_contents;
87 }
88
89 bool MaybeSwapWithPrerender(const GURL& url, chrome::NavigateParams* params) {
90   Profile* profile =
91       Profile::FromBrowserContext(params->target_contents->GetBrowserContext());
92
93   prerender::PrerenderManager* prerender_manager =
94       prerender::PrerenderManagerFactory::GetForProfile(profile);
95   if (!prerender_manager)
96     return false;
97   return prerender_manager->MaybeUsePrerenderedPage(url, params);
98 }
99
100 }  // namespace
101
102 TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
103   CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents);
104   if (!core_tab_helper)
105     return NULL;
106
107   CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
108   if (!core_delegate)
109     return NULL;
110
111   return static_cast<TabAndroid*>(core_delegate);
112 }
113
114 TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
115   return reinterpret_cast<TabAndroid*>(Java_Tab_getNativePtr(env, obj));
116 }
117
118 void TabAndroid::AttachTabHelpers(content::WebContents* web_contents) {
119   DCHECK(web_contents);
120
121   TabHelpers::AttachTabHelpers(web_contents);
122 }
123
124 TabAndroid::TabAndroid(JNIEnv* env, jobject obj)
125     : weak_java_tab_(env, obj),
126       synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) {
127   Java_Tab_setNativePtr(env, obj, reinterpret_cast<intptr_t>(this));
128 }
129
130 TabAndroid::~TabAndroid() {
131   JNIEnv* env = base::android::AttachCurrentThread();
132   Java_Tab_clearNativePtr(env, weak_java_tab_.get(env).obj());
133 }
134
135 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetJavaObject() {
136   JNIEnv* env = base::android::AttachCurrentThread();
137   return weak_java_tab_.get(env);
138 }
139
140 int TabAndroid::GetAndroidId() const {
141   JNIEnv* env = base::android::AttachCurrentThread();
142   return Java_Tab_getId(env, weak_java_tab_.get(env).obj());
143 }
144
145 int TabAndroid::GetSyncId() const {
146   JNIEnv* env = base::android::AttachCurrentThread();
147   return Java_Tab_getSyncId(env, weak_java_tab_.get(env).obj());
148 }
149
150 base::string16 TabAndroid::GetTitle() const {
151   JNIEnv* env = base::android::AttachCurrentThread();
152   return base::android::ConvertJavaStringToUTF16(
153       Java_Tab_getTitle(env, weak_java_tab_.get(env).obj()));
154 }
155
156 GURL TabAndroid::GetURL() const {
157   JNIEnv* env = base::android::AttachCurrentThread();
158   return GURL(base::android::ConvertJavaStringToUTF8(
159       Java_Tab_getUrl(env, weak_java_tab_.get(env).obj())));
160 }
161
162 bool TabAndroid::LoadIfNeeded() {
163   JNIEnv* env = base::android::AttachCurrentThread();
164   return Java_Tab_loadIfNeeded(env, weak_java_tab_.get(env).obj());
165 }
166
167 content::ContentViewCore* TabAndroid::GetContentViewCore() const {
168   if (!web_contents())
169     return NULL;
170
171   return content::ContentViewCore::FromWebContents(web_contents());
172 }
173
174 Profile* TabAndroid::GetProfile() const {
175   if (!web_contents())
176     return NULL;
177
178   return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
179 }
180
181 browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const {
182   return synced_tab_delegate_.get();
183 }
184
185 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id) {
186   session_window_id_.set_id(window_id);
187
188   if (!web_contents())
189     return;
190
191   SessionTabHelper* session_tab_helper =
192           SessionTabHelper::FromWebContents(web_contents());
193   session_tab_helper->SetWindowID(session_window_id_);
194 }
195
196 void TabAndroid::SetSyncId(int sync_id) {
197   JNIEnv* env = base::android::AttachCurrentThread();
198   Java_Tab_setSyncId(env, weak_java_tab_.get(env).obj(), sync_id);
199 }
200
201 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams* params) {
202   if (params->disposition != SUPPRESS_OPEN &&
203       params->disposition != SAVE_TO_DISK &&
204       params->disposition != IGNORE_ACTION) {
205     if (!params->url.is_empty()) {
206       bool was_blocked = false;
207       GURL url(params->url);
208       if (params->disposition == CURRENT_TAB) {
209         params->target_contents = web_contents_.get();
210         if (!MaybeSwapWithPrerender(url, params)) {
211           NavigationController::LoadURLParams load_url_params(url);
212           MakeLoadURLParams(params, &load_url_params);
213           params->target_contents->GetController().LoadURLWithParams(
214               load_url_params);
215         }
216       } else {
217         params->target_contents = CreateTargetContents(*params, url);
218         NavigationController::LoadURLParams load_url_params(url);
219         MakeLoadURLParams(params, &load_url_params);
220         params->target_contents->GetController().LoadURLWithParams(
221             load_url_params);
222         web_contents_delegate_->AddNewContents(params->source_contents,
223                                                params->target_contents,
224                                                params->disposition,
225                                                params->window_bounds,
226                                                params->user_gesture,
227                                                &was_blocked);
228         if (was_blocked)
229           params->target_contents = NULL;
230       }
231     }
232   }
233 }
234
235 bool TabAndroid::ShouldWelcomePageLinkToTermsOfService() {
236   NOTIMPLEMENTED();
237   return false;
238 }
239
240 bool TabAndroid::HasPrerenderedUrl(GURL gurl) {
241   prerender::PrerenderManager* prerender_manager = GetPrerenderManager();
242   if (!prerender_manager)
243     return false;
244
245   std::vector<content::WebContents*> contents =
246       prerender_manager->GetAllPrerenderingContents();
247   prerender::PrerenderContents* prerender_contents;
248   for (size_t i = 0; i < contents.size(); ++i) {
249     prerender_contents = prerender_manager->
250         GetPrerenderContents(contents.at(i));
251     if (prerender_contents->prerender_url() == gurl &&
252         prerender_contents->has_finished_loading()) {
253       return true;
254     }
255   }
256   return false;
257 }
258
259 void TabAndroid::MakeLoadURLParams(
260     chrome::NavigateParams* params,
261     NavigationController::LoadURLParams* load_url_params) {
262   load_url_params->referrer = params->referrer;
263   load_url_params->frame_tree_node_id = params->frame_tree_node_id;
264   load_url_params->redirect_chain = params->redirect_chain;
265   load_url_params->transition_type = params->transition;
266   load_url_params->extra_headers = params->extra_headers;
267   load_url_params->should_replace_current_entry =
268       params->should_replace_current_entry;
269
270   if (params->transferred_global_request_id != GlobalRequestID()) {
271     load_url_params->transferred_global_request_id =
272         params->transferred_global_request_id;
273   }
274   load_url_params->is_renderer_initiated = params->is_renderer_initiated;
275
276   // Only allows the browser-initiated navigation to use POST.
277   if (params->uses_post && !params->is_renderer_initiated) {
278     load_url_params->load_type =
279         NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
280     load_url_params->browser_initiated_post_data =
281         params->browser_initiated_post_data;
282   }
283 }
284
285 void TabAndroid::SwapTabContents(content::WebContents* old_contents,
286                                  content::WebContents* new_contents,
287                                  bool did_start_load,
288                                  bool did_finish_load) {
289   JNIEnv* env = base::android::AttachCurrentThread();
290
291   // We need to notify the native InfobarContainer so infobars can be swapped.
292   InfoBarContainerAndroid* infobar_container =
293       reinterpret_cast<InfoBarContainerAndroid*>(
294           Java_Tab_getNativeInfoBarContainer(
295               env,
296               weak_java_tab_.get(env).obj()));
297   InfoBarService* new_infobar_service =
298       new_contents ? InfoBarService::FromWebContents(new_contents) : NULL;
299   infobar_container->ChangeInfoBarManager(new_infobar_service);
300
301   Java_Tab_swapWebContents(
302       env,
303       weak_java_tab_.get(env).obj(),
304       reinterpret_cast<intptr_t>(new_contents),
305       did_start_load,
306       did_finish_load);
307 }
308
309 void TabAndroid::Observe(int type,
310                          const content::NotificationSource& source,
311                          const content::NotificationDetails& details) {
312   JNIEnv* env = base::android::AttachCurrentThread();
313   switch (type) {
314     case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED: {
315       TabSpecificContentSettings* settings =
316           TabSpecificContentSettings::FromWebContents(web_contents());
317       if (!settings->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS)) {
318         // TODO(dfalcantara): Create an InfoBarDelegate to keep the
319         // PopupBlockedInfoBar logic native-side instead of straddling the JNI
320         // boundary.
321         int num_popups = 0;
322         PopupBlockerTabHelper* popup_blocker_helper =
323             PopupBlockerTabHelper::FromWebContents(web_contents());
324         if (popup_blocker_helper)
325           num_popups = popup_blocker_helper->GetBlockedPopupsCount();
326
327         if (num_popups > 0)
328           PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups);
329
330         settings->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS);
331       }
332       break;
333     }
334     case chrome::NOTIFICATION_FAVICON_UPDATED:
335       Java_Tab_onFaviconUpdated(env, weak_java_tab_.get(env).obj());
336       break;
337     case content::NOTIFICATION_NAV_ENTRY_CHANGED:
338       Java_Tab_onNavEntryChanged(env, weak_java_tab_.get(env).obj());
339       break;
340     default:
341       NOTREACHED() << "Unexpected notification " << type;
342       break;
343   }
344 }
345
346 void TabAndroid::Destroy(JNIEnv* env, jobject obj) {
347   delete this;
348 }
349
350 void TabAndroid::InitWebContents(JNIEnv* env,
351                                  jobject obj,
352                                  jboolean incognito,
353                                  jobject jcontent_view_core,
354                                  jobject jweb_contents_delegate,
355                                  jobject jcontext_menu_populator) {
356   content::ContentViewCore* content_view_core =
357       content::ContentViewCore::GetNativeContentViewCore(env,
358                                                          jcontent_view_core);
359   DCHECK(content_view_core);
360   DCHECK(content_view_core->GetWebContents());
361
362   web_contents_.reset(content_view_core->GetWebContents());
363   AttachTabHelpers(web_contents_.get());
364
365   SetWindowSessionID(session_window_id_.id());
366
367   session_tab_id_.set_id(
368       SessionTabHelper::FromWebContents(web_contents())->session_id().id());
369   ContextMenuHelper::FromWebContents(web_contents())->SetPopulator(
370       jcontext_menu_populator);
371   WindowAndroidHelper::FromWebContents(web_contents())->
372       SetWindowAndroid(content_view_core->GetWindowAndroid());
373   CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
374   web_contents_delegate_.reset(
375       new chrome::android::ChromeWebContentsDelegateAndroid(
376           env, jweb_contents_delegate));
377   web_contents_delegate_->LoadProgressChanged(web_contents(), 0);
378   web_contents()->SetDelegate(web_contents_delegate_.get());
379
380   notification_registrar_.Add(
381       this,
382       chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
383       content::Source<content::WebContents>(web_contents()));
384   notification_registrar_.Add(
385       this,
386       chrome::NOTIFICATION_FAVICON_UPDATED,
387       content::Source<content::WebContents>(web_contents()));
388   notification_registrar_.Add(
389       this,
390       content::NOTIFICATION_NAV_ENTRY_CHANGED,
391       content::Source<content::NavigationController>(
392            &web_contents()->GetController()));
393
394   synced_tab_delegate_->SetWebContents(web_contents());
395
396   // Verify that the WebContents this tab represents matches the expected
397   // off the record state.
398   CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito);
399 }
400
401 void TabAndroid::DestroyWebContents(JNIEnv* env,
402                                     jobject obj,
403                                     jboolean delete_native) {
404   DCHECK(web_contents());
405
406   notification_registrar_.Remove(
407       this,
408       chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
409       content::Source<content::WebContents>(web_contents()));
410   notification_registrar_.Remove(
411       this,
412       chrome::NOTIFICATION_FAVICON_UPDATED,
413       content::Source<content::WebContents>(web_contents()));
414   notification_registrar_.Remove(
415       this,
416       content::NOTIFICATION_NAV_ENTRY_CHANGED,
417       content::Source<content::NavigationController>(
418            &web_contents()->GetController()));
419
420   web_contents()->SetDelegate(NULL);
421
422   if (delete_native) {
423     web_contents_.reset();
424     synced_tab_delegate_->ResetWebContents();
425   } else {
426     // Release the WebContents so it does not get deleted by the scoped_ptr.
427     ignore_result(web_contents_.release());
428   }
429 }
430
431 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetWebContents(
432     JNIEnv* env,
433     jobject obj) {
434   if (!web_contents_.get())
435     return base::android::ScopedJavaLocalRef<jobject>();
436   return web_contents_->GetJavaWebContents();
437 }
438
439 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetProfileAndroid(
440     JNIEnv* env,
441     jobject obj) {
442   Profile* profile = GetProfile();
443   if (!profile)
444     return base::android::ScopedJavaLocalRef<jobject>();
445   ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile);
446   if (!profile_android)
447     return base::android::ScopedJavaLocalRef<jobject>();
448
449   return profile_android->GetJavaObject();
450 }
451
452 TabAndroid::TabLoadStatus TabAndroid::LoadUrl(JNIEnv* env,
453                                               jobject obj,
454                                               jstring url,
455                                               jstring j_extra_headers,
456                                               jbyteArray j_post_data,
457                                               jint page_transition,
458                                               jstring j_referrer_url,
459                                               jint referrer_policy,
460                                               jboolean is_renderer_initiated) {
461   content::ContentViewCore* content_view = GetContentViewCore();
462   if (!content_view)
463     return PAGE_LOAD_FAILED;
464
465   GURL gurl(base::android::ConvertJavaStringToUTF8(env, url));
466   if (gurl.is_empty())
467     return PAGE_LOAD_FAILED;
468
469   // If the page was prerendered, use it.
470   // Note in incognito mode, we don't have a PrerenderManager.
471
472   prerender::PrerenderManager* prerender_manager =
473       prerender::PrerenderManagerFactory::GetForProfile(GetProfile());
474   if (prerender_manager) {
475     bool prefetched_page_loaded = HasPrerenderedUrl(gurl);
476     // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
477     chrome::NavigateParams params(NULL, web_contents());
478     InstantSearchPrerenderer* prerenderer =
479         InstantSearchPrerenderer::GetForProfile(GetProfile());
480     if (prerenderer) {
481       const base::string16& search_terms =
482           chrome::ExtractSearchTermsFromURL(GetProfile(), gurl);
483       if (!search_terms.empty() &&
484           prerenderer->CanCommitQuery(web_contents_.get(), search_terms)) {
485         prerenderer->Commit(search_terms);
486
487         if (prerenderer->UsePrerenderedPage(gurl, &params))
488           return FULL_PRERENDERED_PAGE_LOAD;
489       }
490       prerenderer->Cancel();
491     }
492     if (prerender_manager->MaybeUsePrerenderedPage(gurl, &params)) {
493       return prefetched_page_loaded ?
494           FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
495     }
496   }
497
498   GURL fixed_url(
499       url_fixer::FixupURL(gurl.possibly_invalid_spec(), std::string()));
500   if (!fixed_url.is_valid())
501     return PAGE_LOAD_FAILED;
502
503   if (!HandleNonNavigationAboutURL(fixed_url)) {
504     // Notify the GoogleURLTracker of searches, it might want to change the
505     // actual Google site used (for instance when in the UK, google.co.uk, when
506     // in the US google.com).
507     // Note that this needs to happen before we initiate the navigation as the
508     // GoogleURLTracker uses the navigation pending notification to trigger the
509     // infobar.
510     if (google_util::IsGoogleSearchUrl(fixed_url) &&
511         (page_transition & content::PAGE_TRANSITION_GENERATED)) {
512       GoogleURLTracker* tracker =
513           GoogleURLTrackerFactory::GetForProfile(GetProfile());
514       if (tracker)
515         tracker->SearchCommitted();
516     }
517
518     // Record UMA "ShowHistory" here. That way it'll pick up both user
519     // typing chrome://history as well as selecting from the drop down menu.
520     if (fixed_url.spec() == chrome::kChromeUIHistoryURL) {
521       content::RecordAction(base::UserMetricsAction("ShowHistory"));
522     }
523
524     content::NavigationController::LoadURLParams load_params(fixed_url);
525     if (j_extra_headers) {
526       load_params.extra_headers = base::android::ConvertJavaStringToUTF8(
527           env,
528           j_extra_headers);
529     }
530     if (j_post_data) {
531       load_params.load_type =
532           content::NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
533       std::vector<uint8> post_data;
534       base::android::JavaByteArrayToByteVector(env, j_post_data, &post_data);
535       load_params.browser_initiated_post_data =
536           base::RefCountedBytes::TakeVector(&post_data);
537     }
538     load_params.transition_type =
539         content::PageTransitionFromInt(page_transition);
540     if (j_referrer_url) {
541       load_params.referrer = content::Referrer(
542           GURL(base::android::ConvertJavaStringToUTF8(env, j_referrer_url)),
543           static_cast<blink::WebReferrerPolicy>(referrer_policy));
544     }
545     const base::string16 search_terms =
546         chrome::ExtractSearchTermsFromURL(GetProfile(), gurl);
547     SearchTabHelper* search_tab_helper =
548         SearchTabHelper::FromWebContents(web_contents_.get());
549     if (!search_terms.empty() && search_tab_helper &&
550         search_tab_helper->SupportsInstant()) {
551       search_tab_helper->Submit(search_terms);
552       return DEFAULT_PAGE_LOAD;
553     }
554     load_params.is_renderer_initiated = is_renderer_initiated;
555     content_view->LoadUrl(load_params);
556   }
557   return DEFAULT_PAGE_LOAD;
558 }
559
560 ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env,
561                                                          jobject obj) {
562   return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
563 }
564
565 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv* env,
566                                                      jobject obj,
567                                                      jstring jurl,
568                                                      jstring jtitle) {
569   DCHECK(web_contents());
570
571   base::string16 title;
572   if (jtitle)
573     title = base::android::ConvertJavaStringToUTF16(env, jtitle);
574
575   std::string url;
576   if (jurl)
577     url = base::android::ConvertJavaStringToUTF8(env, jurl);
578
579   content::NavigationEntry* entry =
580       web_contents()->GetController().GetVisibleEntry();
581   if (entry && url == entry->GetVirtualURL().spec())
582     entry->SetTitle(title);
583 }
584
585 bool TabAndroid::Print(JNIEnv* env, jobject obj) {
586   if (!web_contents())
587     return false;
588
589   printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
590   printing::PrintViewManagerBasic* print_view_manager =
591       printing::PrintViewManagerBasic::FromWebContents(web_contents());
592   if (print_view_manager == NULL)
593     return false;
594
595   print_view_manager->PrintNow();
596   return true;
597 }
598
599 ScopedJavaLocalRef<jobject> TabAndroid::GetFavicon(JNIEnv* env, jobject obj) {
600   ScopedJavaLocalRef<jobject> bitmap;
601   FaviconTabHelper* favicon_tab_helper =
602       FaviconTabHelper::FromWebContents(web_contents_.get());
603
604   if (!favicon_tab_helper)
605     return bitmap;
606   if (!favicon_tab_helper->FaviconIsValid())
607     return bitmap;
608
609   SkBitmap favicon =
610       favicon_tab_helper->GetFavicon()
611           .AsImageSkia()
612           .GetRepresentation(
613                ResourceBundle::GetSharedInstance().GetMaxScaleFactor())
614           .sk_bitmap();
615
616   if (favicon.empty()) {
617     favicon = favicon_tab_helper->GetFavicon().AsBitmap();
618   }
619
620   if (!favicon.empty()) {
621     gfx::DeviceDisplayInfo device_info;
622     const float device_scale_factor = device_info.GetDIPScale();
623     int target_size_dip = device_scale_factor * gfx::kFaviconSize;
624     if (favicon.width() != target_size_dip ||
625         favicon.height() != target_size_dip) {
626       favicon =
627           skia::ImageOperations::Resize(favicon,
628                                         skia::ImageOperations::RESIZE_BEST,
629                                         target_size_dip,
630                                         target_size_dip);
631     }
632
633     bitmap = gfx::ConvertToJavaBitmap(&favicon);
634   }
635   return bitmap;
636 }
637
638 prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
639   Profile* profile = GetProfile();
640   if (!profile)
641     return NULL;
642   return prerender::PrerenderManagerFactory::GetForProfile(profile);
643 }
644
645 static void Init(JNIEnv* env, jobject obj) {
646   TRACE_EVENT0("native", "TabAndroid::Init");
647   // This will automatically bind to the Java object and pass ownership there.
648   new TabAndroid(env, obj);
649 }
650
651 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
652   return RegisterNativesImpl(env);
653 }