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