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
63using content::GlobalRequestID;
64using content::NavigationController;
65using content::WebContents;
66
67namespace {
68
69WebContents* 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
92bool 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
105TabAndroid* 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
117TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
118  return reinterpret_cast<TabAndroid*>(Java_Tab_getNativePtr(env, obj));
119}
120
121void TabAndroid::AttachTabHelpers(content::WebContents* web_contents) {
122  DCHECK(web_contents);
123
124  TabHelpers::AttachTabHelpers(web_contents);
125}
126
127TabAndroid::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
133TabAndroid::~TabAndroid() {
134  JNIEnv* env = base::android::AttachCurrentThread();
135  Java_Tab_clearNativePtr(env, weak_java_tab_.get(env).obj());
136}
137
138base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetJavaObject() {
139  JNIEnv* env = base::android::AttachCurrentThread();
140  return weak_java_tab_.get(env);
141}
142
143int TabAndroid::GetAndroidId() const {
144  JNIEnv* env = base::android::AttachCurrentThread();
145  return Java_Tab_getId(env, weak_java_tab_.get(env).obj());
146}
147
148int TabAndroid::GetSyncId() const {
149  JNIEnv* env = base::android::AttachCurrentThread();
150  return Java_Tab_getSyncId(env, weak_java_tab_.get(env).obj());
151}
152
153base::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
159GURL 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
165bool TabAndroid::LoadIfNeeded() {
166  JNIEnv* env = base::android::AttachCurrentThread();
167  return Java_Tab_loadIfNeeded(env, weak_java_tab_.get(env).obj());
168}
169
170content::ContentViewCore* TabAndroid::GetContentViewCore() const {
171  if (!web_contents())
172    return NULL;
173
174  return content::ContentViewCore::FromWebContents(web_contents());
175}
176
177Profile* TabAndroid::GetProfile() const {
178  if (!web_contents())
179    return NULL;
180
181  return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
182}
183
184browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const {
185  return synced_tab_delegate_.get();
186}
187
188void 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
199void 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
204void 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
238bool TabAndroid::ShouldWelcomePageLinkToTermsOfService() {
239  NOTIMPLEMENTED();
240  return false;
241}
242
243bool 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
262void 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
288void 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
312void 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
340void 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
351void 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
388void TabAndroid::Destroy(JNIEnv* env, jobject obj) {
389  delete this;
390}
391
392void 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
449void 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
484base::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
497TabAndroid::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
604ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env,
605                                                         jobject obj) {
606  return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
607}
608
609void 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
629bool 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
643ScopedJavaLocalRef<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
682jboolean TabAndroid::IsFaviconValid(JNIEnv* env, jobject jobj) {
683  return web_contents() &&
684      FaviconTabHelper::FromWebContents(web_contents())->FaviconIsValid();
685}
686
687prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
688  Profile* profile = GetProfile();
689  if (!profile)
690    return NULL;
691  return prerender::PrerenderManagerFactory::GetForProfile(profile);
692}
693
694static 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
700bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
701  return RegisterNativesImpl(env);
702}
703