Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / android / tab_android.cc
blobf2c77dcf5016a9ef580d66cd09ab785aa771c8d5
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.
5 #include "chrome/browser/android/tab_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/metrics/histogram.h"
11 #include "base/trace_event/trace_event.h"
12 #include "cc/layers/layer.h"
13 #include "chrome/browser/android/chrome_web_contents_delegate_android.h"
14 #include "chrome/browser/android/compositor/tab_content_manager.h"
15 #include "chrome/browser/android/hung_renderer_infobar_delegate.h"
16 #include "chrome/browser/android/metrics/uma_utils.h"
17 #include "chrome/browser/android/offline_pages/offline_page_bridge.h"
18 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h"
19 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
20 #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
21 #include "chrome/browser/browser_about_handler.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
24 #include "chrome/browser/infobars/infobar_service.h"
25 #include "chrome/browser/prerender/prerender_contents.h"
26 #include "chrome/browser/prerender/prerender_manager.h"
27 #include "chrome/browser/prerender/prerender_manager_factory.h"
28 #include "chrome/browser/printing/print_view_manager_basic.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_android.h"
31 #include "chrome/browser/search/instant_service.h"
32 #include "chrome/browser/search/instant_service_factory.h"
33 #include "chrome/browser/search/search.h"
34 #include "chrome/browser/sessions/session_tab_helper.h"
35 #include "chrome/browser/sessions/tab_restore_service_factory.h"
36 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
37 #include "chrome/browser/tab_contents/tab_util.h"
38 #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
39 #include "chrome/browser/ui/android/context_menu_helper.h"
40 #include "chrome/browser/ui/android/infobars/infobar_container_android.h"
41 #include "chrome/browser/ui/android/tab_model/tab_model.h"
42 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
43 #include "chrome/browser/ui/android/window_android_helper.h"
44 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
45 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
46 #include "chrome/browser/ui/search/search_tab_helper.h"
47 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
48 #include "chrome/browser/ui/tab_helpers.h"
49 #include "chrome/common/instant_types.h"
50 #include "chrome/common/render_messages.h"
51 #include "chrome/common/url_constants.h"
52 #include "components/bookmarks/browser/bookmark_model.h"
53 #include "components/bookmarks/browser/bookmark_node.h"
54 #include "components/bookmarks/browser/bookmark_utils.h"
55 #include "components/bookmarks/managed/managed_bookmark_service.h"
56 #include "components/dom_distiller/core/url_utils.h"
57 #include "components/favicon/content/content_favicon_driver.h"
58 #include "components/infobars/core/infobar.h"
59 #include "components/infobars/core/infobar_container.h"
60 #include "components/navigation_interception/intercept_navigation_delegate.h"
61 #include "components/navigation_interception/navigation_params.h"
62 #include "components/offline_pages/offline_page_feature.h"
63 #include "components/offline_pages/offline_page_item.h"
64 #include "components/offline_pages/offline_page_model.h"
65 #include "components/sessions/content/content_live_tab.h"
66 #include "components/sessions/core/tab_restore_service.h"
67 #include "components/url_formatter/url_fixer.h"
68 #include "content/public/browser/android/compositor.h"
69 #include "content/public/browser/android/content_view_core.h"
70 #include "content/public/browser/browser_thread.h"
71 #include "content/public/browser/interstitial_page.h"
72 #include "content/public/browser/navigation_entry.h"
73 #include "content/public/browser/notification_service.h"
74 #include "content/public/browser/render_frame_host.h"
75 #include "content/public/browser/render_process_host.h"
76 #include "content/public/browser/render_view_host.h"
77 #include "content/public/browser/user_metrics.h"
78 #include "content/public/browser/web_contents.h"
79 #include "content/public/common/top_controls_state.h"
80 #include "jni/Tab_jni.h"
81 #include "net/base/escape.h"
82 #include "skia/ext/image_operations.h"
83 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
84 #include "ui/base/resource/resource_bundle.h"
85 #include "ui/base/window_open_disposition.h"
86 #include "ui/gfx/android/device_display_info.h"
87 #include "ui/gfx/android/java_bitmap.h"
88 #include "ui/gfx/favicon_size.h"
89 #include "ui/gfx/image/image_skia.h"
91 using base::android::AttachCurrentThread;
92 using base::android::ConvertUTF8ToJavaString;
93 using base::android::ToJavaByteArray;
94 using content::BrowserThread;
95 using content::GlobalRequestID;
96 using content::NavigationController;
97 using content::WebContents;
98 using navigation_interception::InterceptNavigationDelegate;
99 using navigation_interception::NavigationParams;
101 namespace {
103 const int kImageSearchThumbnailMinSize = 300 * 300;
104 const int kImageSearchThumbnailMaxWidth = 600;
105 const int kImageSearchThumbnailMaxHeight = 600;
107 infobars::InfoBar* FindHungRendererInfoBar(InfoBarService* infobar_service) {
108 DCHECK(infobar_service);
109 for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
110 infobars::InfoBar* infobar = infobar_service->infobar_at(i);
111 if (infobar->delegate()->AsHungRendererInfoBarDelegate())
112 return infobar;
114 return nullptr;
117 } // namespace
119 TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
120 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents);
121 if (!core_tab_helper)
122 return NULL;
124 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
125 if (!core_delegate)
126 return NULL;
128 return static_cast<TabAndroid*>(core_delegate);
131 TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
132 return reinterpret_cast<TabAndroid*>(Java_Tab_getNativePtr(env, obj));
135 void TabAndroid::AttachTabHelpers(content::WebContents* web_contents) {
136 DCHECK(web_contents);
138 TabHelpers::AttachTabHelpers(web_contents);
141 TabAndroid::TabAndroid(JNIEnv* env, jobject obj)
142 : weak_java_tab_(env, obj),
143 content_layer_(cc::Layer::Create(content::Compositor::LayerSettings())),
144 tab_content_manager_(NULL),
145 synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) {
146 Java_Tab_setNativePtr(env, obj, reinterpret_cast<intptr_t>(this));
149 TabAndroid::~TabAndroid() {
150 GetContentLayer()->RemoveAllChildren();
151 JNIEnv* env = base::android::AttachCurrentThread();
152 Java_Tab_clearNativePtr(env, weak_java_tab_.get(env).obj());
155 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetJavaObject() {
156 JNIEnv* env = base::android::AttachCurrentThread();
157 return weak_java_tab_.get(env);
160 scoped_refptr<cc::Layer> TabAndroid::GetContentLayer() const {
161 return content_layer_;
164 int TabAndroid::GetAndroidId() const {
165 JNIEnv* env = base::android::AttachCurrentThread();
166 return Java_Tab_getId(env, weak_java_tab_.get(env).obj());
169 int TabAndroid::GetSyncId() const {
170 JNIEnv* env = base::android::AttachCurrentThread();
171 return Java_Tab_getSyncId(env, weak_java_tab_.get(env).obj());
174 base::string16 TabAndroid::GetTitle() const {
175 JNIEnv* env = base::android::AttachCurrentThread();
176 return base::android::ConvertJavaStringToUTF16(
177 Java_Tab_getTitle(env, weak_java_tab_.get(env).obj()));
180 GURL TabAndroid::GetURL() const {
181 JNIEnv* env = base::android::AttachCurrentThread();
182 return GURL(base::android::ConvertJavaStringToUTF8(
183 Java_Tab_getUrl(env, weak_java_tab_.get(env).obj())));
186 bool TabAndroid::LoadIfNeeded() {
187 JNIEnv* env = base::android::AttachCurrentThread();
188 return Java_Tab_loadIfNeeded(env, weak_java_tab_.get(env).obj());
191 content::ContentViewCore* TabAndroid::GetContentViewCore() const {
192 if (!web_contents())
193 return NULL;
195 return content::ContentViewCore::FromWebContents(web_contents());
198 Profile* TabAndroid::GetProfile() const {
199 if (!web_contents())
200 return NULL;
202 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
205 browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const {
206 return synced_tab_delegate_.get();
209 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id) {
210 session_window_id_.set_id(window_id);
212 if (!web_contents())
213 return;
215 SessionTabHelper* session_tab_helper =
216 SessionTabHelper::FromWebContents(web_contents());
217 session_tab_helper->SetWindowID(session_window_id_);
220 void TabAndroid::SetSyncId(int sync_id) {
221 JNIEnv* env = base::android::AttachCurrentThread();
222 Java_Tab_setSyncId(env, weak_java_tab_.get(env).obj(), sync_id);
225 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams* params) {
226 DCHECK(params->source_contents == web_contents());
227 DCHECK(params->target_contents == NULL ||
228 params->target_contents == web_contents());
230 WindowOpenDisposition disposition = params->disposition;
231 const GURL& url = params->url;
233 if (disposition == NEW_POPUP ||
234 disposition == NEW_FOREGROUND_TAB ||
235 disposition == NEW_BACKGROUND_TAB ||
236 disposition == NEW_WINDOW ||
237 disposition == OFF_THE_RECORD) {
238 JNIEnv* env = AttachCurrentThread();
239 ScopedJavaLocalRef<jobject> jobj = weak_java_tab_.get(env);
240 ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(env, url.spec()));
241 ScopedJavaLocalRef<jstring> jheaders(
242 ConvertUTF8ToJavaString(env, params->extra_headers));
243 ScopedJavaLocalRef<jbyteArray> jpost_data;
244 if (params->uses_post &&
245 params->browser_initiated_post_data.get() &&
246 params->browser_initiated_post_data.get()->size()) {
247 jpost_data = ToJavaByteArray(
248 env,
249 reinterpret_cast<const uint8*>(
250 params->browser_initiated_post_data.get()->front()),
251 params->browser_initiated_post_data.get()->size());
253 Java_Tab_openNewTab(env,
254 jobj.obj(),
255 jurl.obj(),
256 jheaders.obj(),
257 jpost_data.obj(),
258 disposition,
259 params->created_with_opener,
260 params->is_renderer_initiated);
261 } else {
262 NOTIMPLEMENTED();
266 bool TabAndroid::HasPrerenderedUrl(GURL gurl) {
267 prerender::PrerenderManager* prerender_manager = GetPrerenderManager();
268 if (!prerender_manager)
269 return false;
271 std::vector<content::WebContents*> contents =
272 prerender_manager->GetAllPrerenderingContents();
273 prerender::PrerenderContents* prerender_contents;
274 for (size_t i = 0; i < contents.size(); ++i) {
275 prerender_contents = prerender_manager->
276 GetPrerenderContents(contents.at(i));
277 if (prerender_contents->prerender_url() == gurl &&
278 prerender_contents->has_finished_loading()) {
279 return true;
282 return false;
285 void TabAndroid::MakeLoadURLParams(
286 chrome::NavigateParams* params,
287 NavigationController::LoadURLParams* load_url_params) {
288 load_url_params->referrer = params->referrer;
289 load_url_params->frame_tree_node_id = params->frame_tree_node_id;
290 load_url_params->redirect_chain = params->redirect_chain;
291 load_url_params->transition_type = params->transition;
292 load_url_params->extra_headers = params->extra_headers;
293 load_url_params->should_replace_current_entry =
294 params->should_replace_current_entry;
296 if (params->transferred_global_request_id != GlobalRequestID()) {
297 load_url_params->transferred_global_request_id =
298 params->transferred_global_request_id;
300 load_url_params->is_renderer_initiated = params->is_renderer_initiated;
302 // Only allows the browser-initiated navigation to use POST.
303 if (params->uses_post && !params->is_renderer_initiated) {
304 load_url_params->load_type =
305 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
306 load_url_params->browser_initiated_post_data =
307 params->browser_initiated_post_data;
311 void TabAndroid::SwapTabContents(content::WebContents* old_contents,
312 content::WebContents* new_contents,
313 bool did_start_load,
314 bool did_finish_load) {
315 JNIEnv* env = base::android::AttachCurrentThread();
316 Java_Tab_swapWebContents(
317 env,
318 weak_java_tab_.get(env).obj(),
319 new_contents->GetJavaWebContents().obj(),
320 did_start_load,
321 did_finish_load);
324 void TabAndroid::DefaultSearchProviderChanged(
325 bool google_base_url_domain_changed) {
326 // TODO(kmadhusu): Move this function definition to a common place and update
327 // BrowserInstantController::DefaultSearchProviderChanged to use the same.
328 if (!web_contents())
329 return;
331 InstantService* instant_service =
332 InstantServiceFactory::GetForProfile(GetProfile());
333 if (!instant_service)
334 return;
336 // Send new search URLs to the renderer.
337 content::RenderProcessHost* rph = web_contents()->GetRenderProcessHost();
338 instant_service->SendSearchURLsToRenderer(rph);
340 // Reload the contents to ensure that it gets assigned to a non-previledged
341 // renderer.
342 if (!instant_service->IsInstantProcess(rph->GetID()))
343 return;
344 web_contents()->GetController().Reload(false);
346 // As the reload was not triggered by the user we don't want to close any
347 // infobars. We have to tell the InfoBarService after the reload, otherwise it
348 // would ignore this call when
349 // WebContentsObserver::DidStartNavigationToPendingEntry is invoked.
350 InfoBarService::FromWebContents(web_contents())->set_ignore_next_reload();
353 void TabAndroid::OnWebContentsInstantSupportDisabled(
354 const content::WebContents* contents) {
355 DCHECK(contents);
356 if (web_contents() != contents)
357 return;
359 JNIEnv* env = base::android::AttachCurrentThread();
360 Java_Tab_onWebContentsInstantSupportDisabled(env,
361 weak_java_tab_.get(env).obj());
364 void TabAndroid::Observe(int type,
365 const content::NotificationSource& source,
366 const content::NotificationDetails& details) {
367 JNIEnv* env = base::android::AttachCurrentThread();
368 switch (type) {
369 case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED: {
370 TabSpecificContentSettings* settings =
371 TabSpecificContentSettings::FromWebContents(web_contents());
372 if (!settings->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS)) {
373 // TODO(dfalcantara): Create an InfoBarDelegate to keep the
374 // PopupBlockedInfoBar logic native-side instead of straddling the JNI
375 // boundary.
376 int num_popups = 0;
377 PopupBlockerTabHelper* popup_blocker_helper =
378 PopupBlockerTabHelper::FromWebContents(web_contents());
379 if (popup_blocker_helper)
380 num_popups = popup_blocker_helper->GetBlockedPopupsCount();
382 if (num_popups > 0)
383 PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups);
385 settings->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS);
387 break;
389 case content::NOTIFICATION_NAV_ENTRY_CHANGED:
390 Java_Tab_onNavEntryChanged(env, weak_java_tab_.get(env).obj());
391 break;
392 default:
393 NOTREACHED() << "Unexpected notification " << type;
394 break;
398 void TabAndroid::OnFaviconAvailable(const gfx::Image& image) {
399 SkBitmap favicon = image.AsImageSkia().GetRepresentation(1.0f).sk_bitmap();
400 if (favicon.empty())
401 return;
403 JNIEnv *env = base::android::AttachCurrentThread();
404 Java_Tab_onFaviconAvailable(env, weak_java_tab_.get(env).obj(),
405 gfx::ConvertToJavaBitmap(&favicon).obj());
408 void TabAndroid::OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
409 bool icon_url_changed) {
412 void TabAndroid::Destroy(JNIEnv* env, jobject obj) {
413 delete this;
416 void TabAndroid::InitWebContents(JNIEnv* env,
417 jobject obj,
418 jboolean incognito,
419 jobject jcontent_view_core,
420 jobject jweb_contents_delegate,
421 jobject jcontext_menu_populator) {
422 content::ContentViewCore* content_view_core =
423 content::ContentViewCore::GetNativeContentViewCore(env,
424 jcontent_view_core);
425 DCHECK(content_view_core);
426 DCHECK(content_view_core->GetWebContents());
428 web_contents_.reset(content_view_core->GetWebContents());
429 AttachTabHelpers(web_contents_.get());
431 SetWindowSessionID(session_window_id_.id());
433 session_tab_id_.set_id(
434 SessionTabHelper::FromWebContents(web_contents())->session_id().id());
435 ContextMenuHelper::FromWebContents(web_contents())->SetPopulator(
436 jcontext_menu_populator);
437 WindowAndroidHelper::FromWebContents(web_contents())->
438 SetWindowAndroid(content_view_core->GetWindowAndroid());
439 CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
440 SearchTabHelper::FromWebContents(web_contents())->set_delegate(this);
441 web_contents_delegate_.reset(
442 new chrome::android::ChromeWebContentsDelegateAndroid(
443 env, jweb_contents_delegate));
444 web_contents_delegate_->LoadProgressChanged(web_contents(), 0);
445 web_contents()->SetDelegate(web_contents_delegate_.get());
447 notification_registrar_.Add(
448 this,
449 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
450 content::Source<content::WebContents>(web_contents()));
451 notification_registrar_.Add(
452 this,
453 content::NOTIFICATION_NAV_ENTRY_CHANGED,
454 content::Source<content::NavigationController>(
455 &web_contents()->GetController()));
457 favicon::FaviconDriver* favicon_driver =
458 favicon::ContentFaviconDriver::FromWebContents(web_contents_.get());
460 if (favicon_driver)
461 favicon_driver->AddObserver(this);
463 synced_tab_delegate_->SetWebContents(web_contents());
465 // Verify that the WebContents this tab represents matches the expected
466 // off the record state.
467 CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito);
469 InstantService* instant_service =
470 InstantServiceFactory::GetForProfile(GetProfile());
471 if (instant_service)
472 instant_service->AddObserver(this);
474 content_layer_->InsertChild(content_view_core->GetLayer(), 0);
477 void TabAndroid::DestroyWebContents(JNIEnv* env,
478 jobject obj,
479 jboolean delete_native) {
480 DCHECK(web_contents());
482 content::ContentViewCore* content_view_core = GetContentViewCore();
483 if (content_view_core)
484 content_view_core->GetLayer()->RemoveFromParent();
486 notification_registrar_.Remove(
487 this,
488 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
489 content::Source<content::WebContents>(web_contents()));
490 notification_registrar_.Remove(
491 this,
492 content::NOTIFICATION_NAV_ENTRY_CHANGED,
493 content::Source<content::NavigationController>(
494 &web_contents()->GetController()));
496 favicon::FaviconDriver* favicon_driver =
497 favicon::ContentFaviconDriver::FromWebContents(web_contents_.get());
499 if (favicon_driver)
500 favicon_driver->RemoveObserver(this);
502 InstantService* instant_service =
503 InstantServiceFactory::GetForProfile(GetProfile());
504 if (instant_service)
505 instant_service->RemoveObserver(this);
507 web_contents()->SetDelegate(NULL);
509 if (delete_native) {
510 // Terminate the renderer process if this is the last tab.
511 // If there's no unload listener, FastShutdownForPageCount kills the
512 // renderer process. Otherwise, we go with the slow path where renderer
513 // process shuts down itself when ref count becomes 0.
514 // This helps the render process exit quickly which avoids some issues
515 // during shutdown. See https://codereview.chromium.org/146693011/
516 // and http://crbug.com/338709 for details.
517 content::RenderProcessHost* process =
518 web_contents()->GetRenderProcessHost();
519 if (process)
520 process->FastShutdownForPageCount(1);
522 web_contents_.reset();
523 synced_tab_delegate_->ResetWebContents();
524 } else {
525 // Release the WebContents so it does not get deleted by the scoped_ptr.
526 ignore_result(web_contents_.release());
530 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetProfileAndroid(
531 JNIEnv* env,
532 jobject obj) {
533 Profile* profile = GetProfile();
534 if (!profile)
535 return base::android::ScopedJavaLocalRef<jobject>();
536 ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile);
537 if (!profile_android)
538 return base::android::ScopedJavaLocalRef<jobject>();
540 return profile_android->GetJavaObject();
543 TabAndroid::TabLoadStatus TabAndroid::LoadUrl(
544 JNIEnv* env,
545 jobject obj,
546 jstring url,
547 jstring j_extra_headers,
548 jbyteArray j_post_data,
549 jint page_transition,
550 jstring j_referrer_url,
551 jint referrer_policy,
552 jboolean is_renderer_initiated,
553 jboolean should_replace_current_entry,
554 jlong intent_received_timestamp,
555 jboolean has_user_gesture) {
556 if (!web_contents())
557 return PAGE_LOAD_FAILED;
559 GURL gurl(base::android::ConvertJavaStringToUTF8(env, url));
560 if (gurl.is_empty())
561 return PAGE_LOAD_FAILED;
563 // If the page was prerendered, use it.
564 // Note in incognito mode, we don't have a PrerenderManager.
566 prerender::PrerenderManager* prerender_manager =
567 prerender::PrerenderManagerFactory::GetForProfile(GetProfile());
568 if (prerender_manager) {
569 bool prefetched_page_loaded = HasPrerenderedUrl(gurl);
570 // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
571 chrome::NavigateParams params(NULL, web_contents());
572 InstantSearchPrerenderer* prerenderer =
573 InstantSearchPrerenderer::GetForProfile(GetProfile());
574 if (prerenderer) {
575 const base::string16& search_terms =
576 search::ExtractSearchTermsFromURL(GetProfile(), gurl);
577 if (!search_terms.empty() &&
578 prerenderer->CanCommitQuery(web_contents_.get(), search_terms)) {
579 EmbeddedSearchRequestParams request_params(gurl);
580 prerenderer->Commit(search_terms, request_params);
582 if (prerenderer->UsePrerenderedPage(gurl, &params))
583 return FULL_PRERENDERED_PAGE_LOAD;
585 prerenderer->Cancel();
587 if (prerender_manager->MaybeUsePrerenderedPage(gurl, &params)) {
588 return prefetched_page_loaded ?
589 FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
593 GURL fixed_url(
594 url_formatter::FixupURL(gurl.possibly_invalid_spec(), std::string()));
595 if (!fixed_url.is_valid())
596 return PAGE_LOAD_FAILED;
598 if (!HandleNonNavigationAboutURL(fixed_url)) {
599 // Record UMA "ShowHistory" here. That way it'll pick up both user
600 // typing chrome://history as well as selecting from the drop down menu.
601 if (fixed_url.spec() == chrome::kChromeUIHistoryURL) {
602 content::RecordAction(base::UserMetricsAction("ShowHistory"));
605 content::NavigationController::LoadURLParams load_params(fixed_url);
606 if (j_extra_headers) {
607 load_params.extra_headers = base::android::ConvertJavaStringToUTF8(
608 env,
609 j_extra_headers);
611 if (j_post_data) {
612 load_params.load_type =
613 content::NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
614 std::vector<uint8> post_data;
615 base::android::JavaByteArrayToByteVector(env, j_post_data, &post_data);
616 load_params.browser_initiated_post_data =
617 base::RefCountedBytes::TakeVector(&post_data);
619 load_params.transition_type =
620 ui::PageTransitionFromInt(page_transition);
621 if (j_referrer_url) {
622 load_params.referrer = content::Referrer(
623 GURL(base::android::ConvertJavaStringToUTF8(env, j_referrer_url)),
624 static_cast<blink::WebReferrerPolicy>(referrer_policy));
626 const base::string16 search_terms =
627 search::ExtractSearchTermsFromURL(GetProfile(), gurl);
628 SearchTabHelper* search_tab_helper =
629 SearchTabHelper::FromWebContents(web_contents_.get());
630 if (!search_terms.empty() && search_tab_helper &&
631 search_tab_helper->SupportsInstant()) {
632 EmbeddedSearchRequestParams request_params(gurl);
633 search_tab_helper->Submit(search_terms, request_params);
634 return DEFAULT_PAGE_LOAD;
636 load_params.is_renderer_initiated = is_renderer_initiated;
637 load_params.should_replace_current_entry = should_replace_current_entry;
638 load_params.intent_received_timestamp = intent_received_timestamp;
639 load_params.has_user_gesture = has_user_gesture;
640 web_contents()->GetController().LoadURLWithParams(load_params);
642 return DEFAULT_PAGE_LOAD;
645 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv* env,
646 jobject obj,
647 jstring jurl,
648 jstring jtitle) {
649 DCHECK(web_contents());
651 base::string16 title;
652 if (jtitle)
653 title = base::android::ConvertJavaStringToUTF16(env, jtitle);
655 std::string url;
656 if (jurl)
657 url = base::android::ConvertJavaStringToUTF8(env, jurl);
659 content::NavigationEntry* entry =
660 web_contents()->GetController().GetVisibleEntry();
661 if (entry && url == entry->GetVirtualURL().spec())
662 entry->SetTitle(title);
665 bool TabAndroid::Print(JNIEnv* env, jobject obj) {
666 if (!web_contents())
667 return false;
669 printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
670 printing::PrintViewManagerBasic* print_view_manager =
671 printing::PrintViewManagerBasic::FromWebContents(web_contents());
672 if (print_view_manager == NULL)
673 return false;
675 print_view_manager->PrintNow();
676 return true;
679 void TabAndroid::SetPendingPrint() {
680 JNIEnv* env = base::android::AttachCurrentThread();
681 Java_Tab_setPendingPrint(env, weak_java_tab_.get(env).obj());
684 ScopedJavaLocalRef<jobject> TabAndroid::GetFavicon(JNIEnv* env,
685 jobject obj) {
687 ScopedJavaLocalRef<jobject> bitmap;
688 favicon::FaviconDriver* favicon_driver =
689 favicon::ContentFaviconDriver::FromWebContents(web_contents_.get());
691 if (!favicon_driver)
692 return bitmap;
694 // Always return the default favicon in Android.
695 SkBitmap favicon = favicon_driver->GetFavicon().AsBitmap();
696 if (!favicon.empty()) {
697 gfx::DeviceDisplayInfo device_info;
698 const float device_scale_factor = device_info.GetDIPScale();
699 int target_size_dip = device_scale_factor * gfx::kFaviconSize;
700 if (favicon.width() != target_size_dip ||
701 favicon.height() != target_size_dip) {
702 favicon =
703 skia::ImageOperations::Resize(favicon,
704 skia::ImageOperations::RESIZE_BEST,
705 target_size_dip,
706 target_size_dip);
709 bitmap = gfx::ConvertToJavaBitmap(&favicon);
711 return bitmap;
714 prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
715 Profile* profile = GetProfile();
716 if (!profile)
717 return NULL;
718 return prerender::PrerenderManagerFactory::GetForProfile(profile);
721 // static
722 void TabAndroid::CreateHistoricalTabFromContents(WebContents* web_contents) {
723 DCHECK(web_contents);
725 sessions::TabRestoreService* service =
726 TabRestoreServiceFactory::GetForProfile(
727 Profile::FromBrowserContext(web_contents->GetBrowserContext()));
728 if (!service)
729 return;
731 // Exclude internal pages from being marked as recent when they are closed.
732 const GURL& tab_url = web_contents->GetURL();
733 if (tab_url.SchemeIs(content::kChromeUIScheme) ||
734 tab_url.SchemeIs(chrome::kChromeNativeScheme) ||
735 tab_url.SchemeIs(url::kAboutScheme)) {
736 return;
739 sessions::ContentLiveTab::CreateForWebContents(web_contents);
740 // TODO(jcivelli): is the index important?
741 service->CreateHistoricalTab(
742 sessions::ContentLiveTab::FromWebContents(web_contents), -1);
745 void TabAndroid::CreateHistoricalTab(JNIEnv* env, jobject obj) {
746 TabAndroid::CreateHistoricalTabFromContents(web_contents());
749 void TabAndroid::UpdateTopControlsState(JNIEnv* env,
750 jobject obj,
751 jint constraints,
752 jint current,
753 jboolean animate) {
754 content::TopControlsState constraints_state =
755 static_cast<content::TopControlsState>(constraints);
756 content::TopControlsState current_state =
757 static_cast<content::TopControlsState>(current);
758 WebContents* sender = web_contents();
759 sender->Send(new ChromeViewMsg_UpdateTopControlsState(
760 sender->GetRoutingID(), constraints_state, current_state, animate));
762 if (sender->ShowingInterstitialPage()) {
763 content::RenderViewHost* interstitial_view_host =
764 sender->GetInterstitialPage()->GetMainFrame()->GetRenderViewHost();
765 interstitial_view_host->Send(new ChromeViewMsg_UpdateTopControlsState(
766 interstitial_view_host->GetRoutingID(), constraints_state,
767 current_state, animate));
771 void TabAndroid::LoadOriginalImage(JNIEnv* env, jobject obj) {
772 content::RenderFrameHost* render_frame_host =
773 web_contents()->GetFocusedFrame();
774 render_frame_host->Send(new ChromeViewMsg_RequestReloadImageForContextNode(
775 render_frame_host->GetRoutingID()));
778 void TabAndroid::SearchByImageInNewTabAsync(JNIEnv* env, jobject obj) {
779 content::RenderFrameHost* render_frame_host =
780 web_contents()->GetMainFrame();
781 render_frame_host->Send(
782 new ChromeViewMsg_RequestThumbnailForContextNode(
783 render_frame_host->GetRoutingID(),
784 kImageSearchThumbnailMinSize,
785 gfx::Size(kImageSearchThumbnailMaxWidth,
786 kImageSearchThumbnailMaxHeight)));
789 jlong TabAndroid::GetBookmarkId(JNIEnv* env,
790 jobject obj,
791 jboolean only_editable) {
792 GURL url = dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
793 web_contents()->GetURL());
794 Profile* profile = GetProfile();
796 // If the url points to an offline page, replace it with the original url.
797 const offline_pages::OfflinePageItem* offline_page = GetOfflinePage(url);
798 if (offline_page)
799 url = offline_page->url;
801 // Get all the nodes for |url| and sort them by date added.
802 std::vector<const bookmarks::BookmarkNode*> nodes;
803 bookmarks::ManagedBookmarkService* managed =
804 ManagedBookmarkServiceFactory::GetForProfile(profile);
805 bookmarks::BookmarkModel* model =
806 BookmarkModelFactory::GetForProfile(profile);
807 model->GetNodesByURL(url, &nodes);
808 std::sort(nodes.begin(), nodes.end(), &bookmarks::MoreRecentlyAdded);
810 // Return the first node matching the search criteria.
811 for (size_t i = 0; i < nodes.size(); ++i) {
812 if (only_editable && !managed->CanBeEditedByUser(nodes[i]))
813 continue;
814 return nodes[i]->id();
817 return -1;
820 jboolean TabAndroid::HasOfflineCopy(JNIEnv* env, jobject obj) {
821 // Offline copy is only saved for a bookmarked page.
822 jlong bookmark_id = GetBookmarkId(env, obj, true);
823 if (bookmark_id == -1)
824 return false;
826 offline_pages::OfflinePageModel* offline_page_model =
827 offline_pages::OfflinePageModelFactory::GetForBrowserContext(
828 GetProfile());
829 const offline_pages::OfflinePageItem* offline_page =
830 offline_page_model->GetPageByBookmarkId(bookmark_id);
831 return offline_page && !offline_page->file_path.empty();
834 jboolean TabAndroid::IsOfflinePage(JNIEnv* env, jobject obj) {
835 GURL url = dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
836 web_contents()->GetURL());
837 return GetOfflinePage(url) != nullptr;
840 ScopedJavaLocalRef<jstring> TabAndroid::GetOfflinePageOriginalUrl(JNIEnv* env,
841 jobject obj) {
842 GURL url = dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
843 web_contents()->GetURL());
844 const offline_pages::OfflinePageItem* offline_page = GetOfflinePage(url);
845 if (offline_page == nullptr)
846 return ScopedJavaLocalRef<jstring>();
848 return ScopedJavaLocalRef<jstring>(
849 ConvertUTF8ToJavaString(env, offline_page->url.spec()));
852 const offline_pages::OfflinePageItem* TabAndroid::GetOfflinePage(
853 const GURL& url) const {
854 if (!offline_pages::IsOfflinePagesEnabled())
855 return nullptr;
857 // Note that we first check if the url likely points to an offline page
858 // before calling GetPageByOfflineURL in order to avoid unnecessary lookup
859 // cost.
860 if (!offline_pages::android::OfflinePageBridge::MightBeOfflineURL(url))
861 return nullptr;
863 offline_pages::OfflinePageModel* offline_page_model =
864 offline_pages::OfflinePageModelFactory::GetForBrowserContext(
865 GetProfile());
866 return offline_page_model->GetPageByOfflineURL(url);
869 bool TabAndroid::HasPrerenderedUrl(JNIEnv* env, jobject obj, jstring url) {
870 GURL gurl(base::android::ConvertJavaStringToUTF8(env, url));
871 return HasPrerenderedUrl(gurl);
874 void TabAndroid::OnRendererUnresponsive(JNIEnv* env, jobject obj) {
875 InfoBarService* infobar_service =
876 InfoBarService::FromWebContents(web_contents());
877 DCHECK(!FindHungRendererInfoBar(infobar_service));
878 HungRendererInfoBarDelegate::Create(infobar_service,
879 web_contents()->GetRenderProcessHost());
882 void TabAndroid::OnRendererResponsive(JNIEnv* env, jobject obj) {
883 InfoBarService* infobar_service =
884 InfoBarService::FromWebContents(web_contents());
885 infobars::InfoBar* hung_renderer_infobar =
886 FindHungRendererInfoBar(infobar_service);
887 if (!hung_renderer_infobar)
888 return;
890 hung_renderer_infobar->delegate()
891 ->AsHungRendererInfoBarDelegate()
892 ->OnRendererResponsive();
893 infobar_service->RemoveInfoBar(hung_renderer_infobar);
896 namespace {
898 class ChromeInterceptNavigationDelegate : public InterceptNavigationDelegate {
899 public:
900 ChromeInterceptNavigationDelegate(JNIEnv* env, jobject jdelegate)
901 : InterceptNavigationDelegate(env, jdelegate) {}
903 bool ShouldIgnoreNavigation(
904 const NavigationParams& navigation_params) override {
905 NavigationParams chrome_navigation_params(navigation_params);
906 chrome_navigation_params.url() =
907 GURL(net::EscapeExternalHandlerValue(navigation_params.url().spec()));
908 return InterceptNavigationDelegate::ShouldIgnoreNavigation(
909 chrome_navigation_params);
913 } // namespace
915 void TabAndroid::SetInterceptNavigationDelegate(JNIEnv* env, jobject obj,
916 jobject delegate) {
917 DCHECK_CURRENTLY_ON(BrowserThread::UI);
918 InterceptNavigationDelegate::Associate(
919 web_contents(),
920 make_scoped_ptr(new ChromeInterceptNavigationDelegate(env, delegate)));
923 void TabAndroid::AttachToTabContentManager(JNIEnv* env,
924 jobject obj,
925 jobject jtab_content_manager) {
926 chrome::android::TabContentManager* tab_content_manager =
927 chrome::android::TabContentManager::FromJavaObject(jtab_content_manager);
928 if (tab_content_manager == tab_content_manager_)
929 return;
931 if (tab_content_manager_)
932 tab_content_manager_->DetachLiveLayer(GetAndroidId(), GetContentLayer());
933 tab_content_manager_ = tab_content_manager;
934 if (tab_content_manager_)
935 tab_content_manager_->AttachLiveLayer(GetAndroidId(), GetContentLayer());
938 void TabAndroid::AttachOverlayContentViewCore(JNIEnv* env,
939 jobject obj,
940 jobject jcontent_view_core,
941 jboolean visible) {
942 content::ContentViewCore* content_view_core =
943 content::ContentViewCore::GetNativeContentViewCore(env,
944 jcontent_view_core);
945 DCHECK(content_view_core);
947 content_view_core->GetLayer()->SetHideLayerAndSubtree(!visible);
948 content_layer_->AddChild(content_view_core->GetLayer());
951 void TabAndroid::DetachOverlayContentViewCore(JNIEnv* env,
952 jobject obj,
953 jobject jcontent_view_core) {
954 content::ContentViewCore* content_view_core =
955 content::ContentViewCore::GetNativeContentViewCore(env,
956 jcontent_view_core);
957 DCHECK(content_view_core);
959 if (content_view_core->GetLayer()->parent() == content_layer_)
960 content_view_core->GetLayer()->RemoveFromParent();
963 static void Init(JNIEnv* env, const JavaParamRef<jobject>& obj) {
964 TRACE_EVENT0("native", "TabAndroid::Init");
965 // This will automatically bind to the Java object and pass ownership there.
966 new TabAndroid(env, obj);
969 // static
970 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
971 return RegisterNativesImpl(env);
974 static void RecordStartupToCommitUma(JNIEnv* env,
975 const JavaParamRef<jclass>& jcaller) {
976 // Currently it takes about 2000ms to commit a navigation if the measurement
977 // begins very early in the browser start. How many buckets (b) are needed to
978 // explore the _typical_ values with granularity 100ms and a maximum duration
979 // of 1 minute?
980 // s^{n+1} / s^{n} = 2100 / 2000
981 // s = 1.05
982 // s^b = 60000
983 // b = ln(60000) / ln(1.05) ~= 225
984 UMA_HISTOGRAM_CUSTOM_TIMES("Startup.FirstCommitNavigationTime",
985 base::Time::Now() - chrome::android::GetMainEntryPointTime(),
986 base::TimeDelta::FromMilliseconds(1),
987 base::TimeDelta::FromMinutes(1),
988 225);