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.h"
36 #include "chrome/browser/sessions/tab_restore_service_factory.h"
37 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
38 #include "chrome/browser/tab_contents/tab_util.h"
39 #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
40 #include "chrome/browser/ui/android/context_menu_helper.h"
41 #include "chrome/browser/ui/android/infobars/infobar_container_android.h"
42 #include "chrome/browser/ui/android/tab_model/tab_model.h"
43 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
44 #include "chrome/browser/ui/android/window_android_helper.h"
45 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
46 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
47 #include "chrome/browser/ui/search/search_tab_helper.h"
48 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
49 #include "chrome/browser/ui/tab_helpers.h"
50 #include "chrome/common/instant_types.h"
51 #include "chrome/common/render_messages.h"
52 #include "chrome/common/url_constants.h"
53 #include "components/bookmarks/browser/bookmark_model.h"
54 #include "components/bookmarks/browser/bookmark_node.h"
55 #include "components/bookmarks/browser/bookmark_utils.h"
56 #include "components/bookmarks/managed/managed_bookmark_service.h"
57 #include "components/dom_distiller/core/url_utils.h"
58 #include "components/favicon/content/content_favicon_driver.h"
59 #include "components/infobars/core/infobar.h"
60 #include "components/infobars/core/infobar_container.h"
61 #include "components/navigation_interception/intercept_navigation_delegate.h"
62 #include "components/navigation_interception/navigation_params.h"
63 #include "components/offline_pages/offline_page_feature.h"
64 #include "components/offline_pages/offline_page_item.h"
65 #include "components/offline_pages/offline_page_model.h"
66 #include "components/url_formatter/url_fixer.h"
67 #include "content/public/browser/android/compositor.h"
68 #include "content/public/browser/android/content_view_core.h"
69 #include "content/public/browser/browser_thread.h"
70 #include "content/public/browser/interstitial_page.h"
71 #include "content/public/browser/navigation_entry.h"
72 #include "content/public/browser/notification_service.h"
73 #include "content/public/browser/render_frame_host.h"
74 #include "content/public/browser/render_process_host.h"
75 #include "content/public/browser/render_view_host.h"
76 #include "content/public/browser/user_metrics.h"
77 #include "content/public/browser/web_contents.h"
78 #include "content/public/common/top_controls_state.h"
79 #include "jni/Tab_jni.h"
80 #include "net/base/escape.h"
81 #include "skia/ext/image_operations.h"
82 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
83 #include "ui/base/resource/resource_bundle.h"
84 #include "ui/base/window_open_disposition.h"
85 #include "ui/gfx/android/device_display_info.h"
86 #include "ui/gfx/android/java_bitmap.h"
87 #include "ui/gfx/favicon_size.h"
88 #include "ui/gfx/image/image_skia.h"
90 using base::android::AttachCurrentThread
;
91 using base::android::ConvertUTF8ToJavaString
;
92 using base::android::ToJavaByteArray
;
93 using content::BrowserThread
;
94 using content::GlobalRequestID
;
95 using content::NavigationController
;
96 using content::WebContents
;
97 using navigation_interception::InterceptNavigationDelegate
;
98 using navigation_interception::NavigationParams
;
102 const int kImageSearchThumbnailMinSize
= 300 * 300;
103 const int kImageSearchThumbnailMaxWidth
= 600;
104 const int kImageSearchThumbnailMaxHeight
= 600;
106 infobars::InfoBar
* FindHungRendererInfoBar(InfoBarService
* infobar_service
) {
107 DCHECK(infobar_service
);
108 for (size_t i
= 0; i
< infobar_service
->infobar_count(); ++i
) {
109 infobars::InfoBar
* infobar
= infobar_service
->infobar_at(i
);
110 if (infobar
->delegate()->AsHungRendererInfoBarDelegate())
118 TabAndroid
* TabAndroid::FromWebContents(content::WebContents
* web_contents
) {
119 CoreTabHelper
* core_tab_helper
= CoreTabHelper::FromWebContents(web_contents
);
120 if (!core_tab_helper
)
123 CoreTabHelperDelegate
* core_delegate
= core_tab_helper
->delegate();
127 return static_cast<TabAndroid
*>(core_delegate
);
130 TabAndroid
* TabAndroid::GetNativeTab(JNIEnv
* env
, jobject obj
) {
131 return reinterpret_cast<TabAndroid
*>(Java_Tab_getNativePtr(env
, obj
));
134 void TabAndroid::AttachTabHelpers(content::WebContents
* web_contents
) {
135 DCHECK(web_contents
);
137 TabHelpers::AttachTabHelpers(web_contents
);
140 TabAndroid::TabAndroid(JNIEnv
* env
, jobject obj
)
141 : weak_java_tab_(env
, obj
),
142 content_layer_(cc::Layer::Create(content::Compositor::LayerSettings())),
143 tab_content_manager_(NULL
),
144 synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) {
145 Java_Tab_setNativePtr(env
, obj
, reinterpret_cast<intptr_t>(this));
148 TabAndroid::~TabAndroid() {
149 GetContentLayer()->RemoveAllChildren();
150 JNIEnv
* env
= base::android::AttachCurrentThread();
151 Java_Tab_clearNativePtr(env
, weak_java_tab_
.get(env
).obj());
154 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetJavaObject() {
155 JNIEnv
* env
= base::android::AttachCurrentThread();
156 return weak_java_tab_
.get(env
);
159 scoped_refptr
<cc::Layer
> TabAndroid::GetContentLayer() const {
160 return content_layer_
;
163 int TabAndroid::GetAndroidId() const {
164 JNIEnv
* env
= base::android::AttachCurrentThread();
165 return Java_Tab_getId(env
, weak_java_tab_
.get(env
).obj());
168 int TabAndroid::GetSyncId() const {
169 JNIEnv
* env
= base::android::AttachCurrentThread();
170 return Java_Tab_getSyncId(env
, weak_java_tab_
.get(env
).obj());
173 base::string16
TabAndroid::GetTitle() const {
174 JNIEnv
* env
= base::android::AttachCurrentThread();
175 return base::android::ConvertJavaStringToUTF16(
176 Java_Tab_getTitle(env
, weak_java_tab_
.get(env
).obj()));
179 GURL
TabAndroid::GetURL() const {
180 JNIEnv
* env
= base::android::AttachCurrentThread();
181 return GURL(base::android::ConvertJavaStringToUTF8(
182 Java_Tab_getUrl(env
, weak_java_tab_
.get(env
).obj())));
185 bool TabAndroid::LoadIfNeeded() {
186 JNIEnv
* env
= base::android::AttachCurrentThread();
187 return Java_Tab_loadIfNeeded(env
, weak_java_tab_
.get(env
).obj());
190 content::ContentViewCore
* TabAndroid::GetContentViewCore() const {
194 return content::ContentViewCore::FromWebContents(web_contents());
197 Profile
* TabAndroid::GetProfile() const {
201 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
204 browser_sync::SyncedTabDelegate
* TabAndroid::GetSyncedTabDelegate() const {
205 return synced_tab_delegate_
.get();
208 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id
) {
209 session_window_id_
.set_id(window_id
);
214 SessionTabHelper
* session_tab_helper
=
215 SessionTabHelper::FromWebContents(web_contents());
216 session_tab_helper
->SetWindowID(session_window_id_
);
219 void TabAndroid::SetSyncId(int sync_id
) {
220 JNIEnv
* env
= base::android::AttachCurrentThread();
221 Java_Tab_setSyncId(env
, weak_java_tab_
.get(env
).obj(), sync_id
);
224 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams
* params
) {
225 DCHECK(params
->source_contents
== web_contents());
226 DCHECK(params
->target_contents
== NULL
||
227 params
->target_contents
== web_contents());
229 WindowOpenDisposition disposition
= params
->disposition
;
230 const GURL
& url
= params
->url
;
232 if (disposition
== NEW_POPUP
||
233 disposition
== NEW_FOREGROUND_TAB
||
234 disposition
== NEW_BACKGROUND_TAB
||
235 disposition
== NEW_WINDOW
||
236 disposition
== OFF_THE_RECORD
) {
237 JNIEnv
* env
= AttachCurrentThread();
238 ScopedJavaLocalRef
<jobject
> jobj
= weak_java_tab_
.get(env
);
239 ScopedJavaLocalRef
<jstring
> jurl(ConvertUTF8ToJavaString(env
, url
.spec()));
240 ScopedJavaLocalRef
<jstring
> jheaders(
241 ConvertUTF8ToJavaString(env
, params
->extra_headers
));
242 ScopedJavaLocalRef
<jbyteArray
> jpost_data
;
243 if (params
->uses_post
&&
244 params
->browser_initiated_post_data
.get() &&
245 params
->browser_initiated_post_data
.get()->size()) {
246 jpost_data
= ToJavaByteArray(
248 reinterpret_cast<const uint8
*>(
249 params
->browser_initiated_post_data
.get()->front()),
250 params
->browser_initiated_post_data
.get()->size());
252 Java_Tab_openNewTab(env
,
258 params
->created_with_opener
,
259 params
->is_renderer_initiated
);
265 bool TabAndroid::HasPrerenderedUrl(GURL gurl
) {
266 prerender::PrerenderManager
* prerender_manager
= GetPrerenderManager();
267 if (!prerender_manager
)
270 std::vector
<content::WebContents
*> contents
=
271 prerender_manager
->GetAllPrerenderingContents();
272 prerender::PrerenderContents
* prerender_contents
;
273 for (size_t i
= 0; i
< contents
.size(); ++i
) {
274 prerender_contents
= prerender_manager
->
275 GetPrerenderContents(contents
.at(i
));
276 if (prerender_contents
->prerender_url() == gurl
&&
277 prerender_contents
->has_finished_loading()) {
284 void TabAndroid::MakeLoadURLParams(
285 chrome::NavigateParams
* params
,
286 NavigationController::LoadURLParams
* load_url_params
) {
287 load_url_params
->referrer
= params
->referrer
;
288 load_url_params
->frame_tree_node_id
= params
->frame_tree_node_id
;
289 load_url_params
->redirect_chain
= params
->redirect_chain
;
290 load_url_params
->transition_type
= params
->transition
;
291 load_url_params
->extra_headers
= params
->extra_headers
;
292 load_url_params
->should_replace_current_entry
=
293 params
->should_replace_current_entry
;
295 if (params
->transferred_global_request_id
!= GlobalRequestID()) {
296 load_url_params
->transferred_global_request_id
=
297 params
->transferred_global_request_id
;
299 load_url_params
->is_renderer_initiated
= params
->is_renderer_initiated
;
301 // Only allows the browser-initiated navigation to use POST.
302 if (params
->uses_post
&& !params
->is_renderer_initiated
) {
303 load_url_params
->load_type
=
304 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
305 load_url_params
->browser_initiated_post_data
=
306 params
->browser_initiated_post_data
;
310 void TabAndroid::SwapTabContents(content::WebContents
* old_contents
,
311 content::WebContents
* new_contents
,
313 bool did_finish_load
) {
314 JNIEnv
* env
= base::android::AttachCurrentThread();
315 Java_Tab_swapWebContents(
317 weak_java_tab_
.get(env
).obj(),
318 new_contents
->GetJavaWebContents().obj(),
323 void TabAndroid::DefaultSearchProviderChanged(
324 bool google_base_url_domain_changed
) {
325 // TODO(kmadhusu): Move this function definition to a common place and update
326 // BrowserInstantController::DefaultSearchProviderChanged to use the same.
330 InstantService
* instant_service
=
331 InstantServiceFactory::GetForProfile(GetProfile());
332 if (!instant_service
)
335 // Send new search URLs to the renderer.
336 content::RenderProcessHost
* rph
= web_contents()->GetRenderProcessHost();
337 instant_service
->SendSearchURLsToRenderer(rph
);
339 // Reload the contents to ensure that it gets assigned to a non-previledged
341 if (!instant_service
->IsInstantProcess(rph
->GetID()))
343 web_contents()->GetController().Reload(false);
345 // As the reload was not triggered by the user we don't want to close any
346 // infobars. We have to tell the InfoBarService after the reload, otherwise it
347 // would ignore this call when
348 // WebContentsObserver::DidStartNavigationToPendingEntry is invoked.
349 InfoBarService::FromWebContents(web_contents())->set_ignore_next_reload();
352 void TabAndroid::OnWebContentsInstantSupportDisabled(
353 const content::WebContents
* contents
) {
355 if (web_contents() != contents
)
358 JNIEnv
* env
= base::android::AttachCurrentThread();
359 Java_Tab_onWebContentsInstantSupportDisabled(env
,
360 weak_java_tab_
.get(env
).obj());
363 void TabAndroid::Observe(int type
,
364 const content::NotificationSource
& source
,
365 const content::NotificationDetails
& details
) {
366 JNIEnv
* env
= base::android::AttachCurrentThread();
368 case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
: {
369 TabSpecificContentSettings
* settings
=
370 TabSpecificContentSettings::FromWebContents(web_contents());
371 if (!settings
->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS
)) {
372 // TODO(dfalcantara): Create an InfoBarDelegate to keep the
373 // PopupBlockedInfoBar logic native-side instead of straddling the JNI
376 PopupBlockerTabHelper
* popup_blocker_helper
=
377 PopupBlockerTabHelper::FromWebContents(web_contents());
378 if (popup_blocker_helper
)
379 num_popups
= popup_blocker_helper
->GetBlockedPopupsCount();
382 PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups
);
384 settings
->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS
);
388 case content::NOTIFICATION_NAV_ENTRY_CHANGED
:
389 Java_Tab_onNavEntryChanged(env
, weak_java_tab_
.get(env
).obj());
392 NOTREACHED() << "Unexpected notification " << type
;
397 void TabAndroid::OnFaviconAvailable(const gfx::Image
& image
) {
398 SkBitmap favicon
= image
.AsImageSkia().GetRepresentation(1.0f
).sk_bitmap();
402 JNIEnv
*env
= base::android::AttachCurrentThread();
403 Java_Tab_onFaviconAvailable(env
, weak_java_tab_
.get(env
).obj(),
404 gfx::ConvertToJavaBitmap(&favicon
).obj());
407 void TabAndroid::OnFaviconUpdated(favicon::FaviconDriver
* favicon_driver
,
408 bool icon_url_changed
) {
411 void TabAndroid::Destroy(JNIEnv
* env
, jobject obj
) {
415 void TabAndroid::InitWebContents(JNIEnv
* env
,
418 jobject jcontent_view_core
,
419 jobject jweb_contents_delegate
,
420 jobject jcontext_menu_populator
) {
421 content::ContentViewCore
* content_view_core
=
422 content::ContentViewCore::GetNativeContentViewCore(env
,
424 DCHECK(content_view_core
);
425 DCHECK(content_view_core
->GetWebContents());
427 web_contents_
.reset(content_view_core
->GetWebContents());
428 AttachTabHelpers(web_contents_
.get());
430 SetWindowSessionID(session_window_id_
.id());
432 session_tab_id_
.set_id(
433 SessionTabHelper::FromWebContents(web_contents())->session_id().id());
434 ContextMenuHelper::FromWebContents(web_contents())->SetPopulator(
435 jcontext_menu_populator
);
436 WindowAndroidHelper::FromWebContents(web_contents())->
437 SetWindowAndroid(content_view_core
->GetWindowAndroid());
438 CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
439 SearchTabHelper::FromWebContents(web_contents())->set_delegate(this);
440 web_contents_delegate_
.reset(
441 new chrome::android::ChromeWebContentsDelegateAndroid(
442 env
, jweb_contents_delegate
));
443 web_contents_delegate_
->LoadProgressChanged(web_contents(), 0);
444 web_contents()->SetDelegate(web_contents_delegate_
.get());
446 notification_registrar_
.Add(
448 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
,
449 content::Source
<content::WebContents
>(web_contents()));
450 notification_registrar_
.Add(
452 content::NOTIFICATION_NAV_ENTRY_CHANGED
,
453 content::Source
<content::NavigationController
>(
454 &web_contents()->GetController()));
456 favicon::FaviconDriver
* favicon_driver
=
457 favicon::ContentFaviconDriver::FromWebContents(web_contents_
.get());
460 favicon_driver
->AddObserver(this);
462 synced_tab_delegate_
->SetWebContents(web_contents());
464 // Verify that the WebContents this tab represents matches the expected
465 // off the record state.
466 CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito
);
468 InstantService
* instant_service
=
469 InstantServiceFactory::GetForProfile(GetProfile());
471 instant_service
->AddObserver(this);
473 content_layer_
->InsertChild(content_view_core
->GetLayer(), 0);
476 void TabAndroid::DestroyWebContents(JNIEnv
* env
,
478 jboolean delete_native
) {
479 DCHECK(web_contents());
481 content::ContentViewCore
* content_view_core
= GetContentViewCore();
482 if (content_view_core
)
483 content_view_core
->GetLayer()->RemoveFromParent();
485 notification_registrar_
.Remove(
487 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED
,
488 content::Source
<content::WebContents
>(web_contents()));
489 notification_registrar_
.Remove(
491 content::NOTIFICATION_NAV_ENTRY_CHANGED
,
492 content::Source
<content::NavigationController
>(
493 &web_contents()->GetController()));
495 favicon::FaviconDriver
* favicon_driver
=
496 favicon::ContentFaviconDriver::FromWebContents(web_contents_
.get());
499 favicon_driver
->RemoveObserver(this);
501 InstantService
* instant_service
=
502 InstantServiceFactory::GetForProfile(GetProfile());
504 instant_service
->RemoveObserver(this);
506 web_contents()->SetDelegate(NULL
);
509 // Terminate the renderer process if this is the last tab.
510 // If there's no unload listener, FastShutdownForPageCount kills the
511 // renderer process. Otherwise, we go with the slow path where renderer
512 // process shuts down itself when ref count becomes 0.
513 // This helps the render process exit quickly which avoids some issues
514 // during shutdown. See https://codereview.chromium.org/146693011/
515 // and http://crbug.com/338709 for details.
516 content::RenderProcessHost
* process
=
517 web_contents()->GetRenderProcessHost();
519 process
->FastShutdownForPageCount(1);
521 web_contents_
.reset();
522 synced_tab_delegate_
->ResetWebContents();
524 // Release the WebContents so it does not get deleted by the scoped_ptr.
525 ignore_result(web_contents_
.release());
529 base::android::ScopedJavaLocalRef
<jobject
> TabAndroid::GetProfileAndroid(
532 Profile
* profile
= GetProfile();
534 return base::android::ScopedJavaLocalRef
<jobject
>();
535 ProfileAndroid
* profile_android
= ProfileAndroid::FromProfile(profile
);
536 if (!profile_android
)
537 return base::android::ScopedJavaLocalRef
<jobject
>();
539 return profile_android
->GetJavaObject();
542 TabAndroid::TabLoadStatus
TabAndroid::LoadUrl(
546 jstring j_extra_headers
,
547 jbyteArray j_post_data
,
548 jint page_transition
,
549 jstring j_referrer_url
,
550 jint referrer_policy
,
551 jboolean is_renderer_initiated
,
552 jboolean should_replace_current_entry
,
553 jlong intent_received_timestamp
,
554 jboolean has_user_gesture
) {
556 return PAGE_LOAD_FAILED
;
558 GURL
gurl(base::android::ConvertJavaStringToUTF8(env
, url
));
560 return PAGE_LOAD_FAILED
;
562 // If the page was prerendered, use it.
563 // Note in incognito mode, we don't have a PrerenderManager.
565 prerender::PrerenderManager
* prerender_manager
=
566 prerender::PrerenderManagerFactory::GetForProfile(GetProfile());
567 if (prerender_manager
) {
568 bool prefetched_page_loaded
= HasPrerenderedUrl(gurl
);
569 // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
570 chrome::NavigateParams
params(NULL
, web_contents());
571 InstantSearchPrerenderer
* prerenderer
=
572 InstantSearchPrerenderer::GetForProfile(GetProfile());
574 const base::string16
& search_terms
=
575 search::ExtractSearchTermsFromURL(GetProfile(), gurl
);
576 if (!search_terms
.empty() &&
577 prerenderer
->CanCommitQuery(web_contents_
.get(), search_terms
)) {
578 EmbeddedSearchRequestParams
request_params(gurl
);
579 prerenderer
->Commit(search_terms
, request_params
);
581 if (prerenderer
->UsePrerenderedPage(gurl
, ¶ms
))
582 return FULL_PRERENDERED_PAGE_LOAD
;
584 prerenderer
->Cancel();
586 if (prerender_manager
->MaybeUsePrerenderedPage(gurl
, ¶ms
)) {
587 return prefetched_page_loaded
?
588 FULL_PRERENDERED_PAGE_LOAD
: PARTIAL_PRERENDERED_PAGE_LOAD
;
593 url_formatter::FixupURL(gurl
.possibly_invalid_spec(), std::string()));
594 if (!fixed_url
.is_valid())
595 return PAGE_LOAD_FAILED
;
597 if (!HandleNonNavigationAboutURL(fixed_url
)) {
598 // Record UMA "ShowHistory" here. That way it'll pick up both user
599 // typing chrome://history as well as selecting from the drop down menu.
600 if (fixed_url
.spec() == chrome::kChromeUIHistoryURL
) {
601 content::RecordAction(base::UserMetricsAction("ShowHistory"));
604 content::NavigationController::LoadURLParams
load_params(fixed_url
);
605 if (j_extra_headers
) {
606 load_params
.extra_headers
= base::android::ConvertJavaStringToUTF8(
611 load_params
.load_type
=
612 content::NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
613 std::vector
<uint8
> post_data
;
614 base::android::JavaByteArrayToByteVector(env
, j_post_data
, &post_data
);
615 load_params
.browser_initiated_post_data
=
616 base::RefCountedBytes::TakeVector(&post_data
);
618 load_params
.transition_type
=
619 ui::PageTransitionFromInt(page_transition
);
620 if (j_referrer_url
) {
621 load_params
.referrer
= content::Referrer(
622 GURL(base::android::ConvertJavaStringToUTF8(env
, j_referrer_url
)),
623 static_cast<blink::WebReferrerPolicy
>(referrer_policy
));
625 const base::string16 search_terms
=
626 search::ExtractSearchTermsFromURL(GetProfile(), gurl
);
627 SearchTabHelper
* search_tab_helper
=
628 SearchTabHelper::FromWebContents(web_contents_
.get());
629 if (!search_terms
.empty() && search_tab_helper
&&
630 search_tab_helper
->SupportsInstant()) {
631 EmbeddedSearchRequestParams
request_params(gurl
);
632 search_tab_helper
->Submit(search_terms
, request_params
);
633 return DEFAULT_PAGE_LOAD
;
635 load_params
.is_renderer_initiated
= is_renderer_initiated
;
636 load_params
.should_replace_current_entry
= should_replace_current_entry
;
637 load_params
.intent_received_timestamp
= intent_received_timestamp
;
638 load_params
.has_user_gesture
= has_user_gesture
;
639 web_contents()->GetController().LoadURLWithParams(load_params
);
641 return DEFAULT_PAGE_LOAD
;
644 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv
* env
,
648 DCHECK(web_contents());
650 base::string16 title
;
652 title
= base::android::ConvertJavaStringToUTF16(env
, jtitle
);
656 url
= base::android::ConvertJavaStringToUTF8(env
, jurl
);
658 content::NavigationEntry
* entry
=
659 web_contents()->GetController().GetVisibleEntry();
660 if (entry
&& url
== entry
->GetVirtualURL().spec())
661 entry
->SetTitle(title
);
664 bool TabAndroid::Print(JNIEnv
* env
, jobject obj
) {
668 printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
669 printing::PrintViewManagerBasic
* print_view_manager
=
670 printing::PrintViewManagerBasic::FromWebContents(web_contents());
671 if (print_view_manager
== NULL
)
674 print_view_manager
->PrintNow();
678 void TabAndroid::SetPendingPrint() {
679 JNIEnv
* env
= base::android::AttachCurrentThread();
680 Java_Tab_setPendingPrint(env
, weak_java_tab_
.get(env
).obj());
683 ScopedJavaLocalRef
<jobject
> TabAndroid::GetFavicon(JNIEnv
* env
,
686 ScopedJavaLocalRef
<jobject
> bitmap
;
687 favicon::FaviconDriver
* favicon_driver
=
688 favicon::ContentFaviconDriver::FromWebContents(web_contents_
.get());
693 // Always return the default favicon in Android.
694 SkBitmap favicon
= favicon_driver
->GetFavicon().AsBitmap();
695 if (!favicon
.empty()) {
696 gfx::DeviceDisplayInfo device_info
;
697 const float device_scale_factor
= device_info
.GetDIPScale();
698 int target_size_dip
= device_scale_factor
* gfx::kFaviconSize
;
699 if (favicon
.width() != target_size_dip
||
700 favicon
.height() != target_size_dip
) {
702 skia::ImageOperations::Resize(favicon
,
703 skia::ImageOperations::RESIZE_BEST
,
708 bitmap
= gfx::ConvertToJavaBitmap(&favicon
);
713 prerender::PrerenderManager
* TabAndroid::GetPrerenderManager() const {
714 Profile
* profile
= GetProfile();
717 return prerender::PrerenderManagerFactory::GetForProfile(profile
);
721 void TabAndroid::CreateHistoricalTabFromContents(WebContents
* web_contents
) {
722 DCHECK(web_contents
);
724 TabRestoreService
* service
=
725 TabRestoreServiceFactory::GetForProfile(
726 Profile::FromBrowserContext(web_contents
->GetBrowserContext()));
730 // Exclude internal pages from being marked as recent when they are closed.
731 const GURL
& tab_url
= web_contents
->GetURL();
732 if (tab_url
.SchemeIs(content::kChromeUIScheme
) ||
733 tab_url
.SchemeIs(chrome::kChromeNativeScheme
) ||
734 tab_url
.SchemeIs(url::kAboutScheme
)) {
738 // TODO(jcivelli): is the index important?
739 service
->CreateHistoricalTab(web_contents
, -1);
742 void TabAndroid::CreateHistoricalTab(JNIEnv
* env
, jobject obj
) {
743 TabAndroid::CreateHistoricalTabFromContents(web_contents());
746 void TabAndroid::UpdateTopControlsState(JNIEnv
* env
,
751 content::TopControlsState constraints_state
=
752 static_cast<content::TopControlsState
>(constraints
);
753 content::TopControlsState current_state
=
754 static_cast<content::TopControlsState
>(current
);
755 WebContents
* sender
= web_contents();
756 sender
->Send(new ChromeViewMsg_UpdateTopControlsState(
757 sender
->GetRoutingID(), constraints_state
, current_state
, animate
));
759 if (sender
->ShowingInterstitialPage()) {
760 content::RenderViewHost
* interstitial_view_host
=
761 sender
->GetInterstitialPage()->GetMainFrame()->GetRenderViewHost();
762 interstitial_view_host
->Send(new ChromeViewMsg_UpdateTopControlsState(
763 interstitial_view_host
->GetRoutingID(), constraints_state
,
764 current_state
, animate
));
768 void TabAndroid::LoadOriginalImage(JNIEnv
* env
, jobject obj
) {
769 content::RenderFrameHost
* render_frame_host
=
770 web_contents()->GetFocusedFrame();
771 render_frame_host
->Send(new ChromeViewMsg_RequestReloadImageForContextNode(
772 render_frame_host
->GetRoutingID()));
775 void TabAndroid::SearchByImageInNewTabAsync(JNIEnv
* env
, jobject obj
) {
776 content::RenderFrameHost
* render_frame_host
=
777 web_contents()->GetMainFrame();
778 render_frame_host
->Send(
779 new ChromeViewMsg_RequestThumbnailForContextNode(
780 render_frame_host
->GetRoutingID(),
781 kImageSearchThumbnailMinSize
,
782 gfx::Size(kImageSearchThumbnailMaxWidth
,
783 kImageSearchThumbnailMaxHeight
)));
786 jlong
TabAndroid::GetBookmarkId(JNIEnv
* env
,
788 jboolean only_editable
) {
789 GURL url
= dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
790 web_contents()->GetURL());
791 Profile
* profile
= GetProfile();
793 // If the url points to an offline page, replace it with the original url.
794 const offline_pages::OfflinePageItem
* offline_page
= GetOfflinePage(url
);
796 url
= offline_page
->url
;
798 // Get all the nodes for |url| and sort them by date added.
799 std::vector
<const bookmarks::BookmarkNode
*> nodes
;
800 bookmarks::ManagedBookmarkService
* managed
=
801 ManagedBookmarkServiceFactory::GetForProfile(profile
);
802 bookmarks::BookmarkModel
* model
=
803 BookmarkModelFactory::GetForProfile(profile
);
804 model
->GetNodesByURL(url
, &nodes
);
805 std::sort(nodes
.begin(), nodes
.end(), &bookmarks::MoreRecentlyAdded
);
807 // Return the first node matching the search criteria.
808 for (size_t i
= 0; i
< nodes
.size(); ++i
) {
809 if (only_editable
&& !managed
->CanBeEditedByUser(nodes
[i
]))
811 return nodes
[i
]->id();
817 jboolean
TabAndroid::IsOfflinePage(JNIEnv
* env
, jobject obj
) {
818 GURL url
= dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
819 web_contents()->GetURL());
820 return GetOfflinePage(url
) != nullptr;
823 ScopedJavaLocalRef
<jstring
> TabAndroid::GetOfflinePageOriginalUrl(JNIEnv
* env
,
825 GURL url
= dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
826 web_contents()->GetURL());
827 const offline_pages::OfflinePageItem
* offline_page
= GetOfflinePage(url
);
828 if (offline_page
== nullptr)
829 return ScopedJavaLocalRef
<jstring
>();
831 return ScopedJavaLocalRef
<jstring
>(
832 ConvertUTF8ToJavaString(env
, offline_page
->url
.spec()));
835 const offline_pages::OfflinePageItem
* TabAndroid::GetOfflinePage(
836 const GURL
& url
) const {
837 if (!offline_pages::IsOfflinePagesEnabled())
840 // Note that we first check if the url likely points to an offline page
841 // before calling GetPageByOfflineURL in order to avoid unnecessary lookup
843 if (!offline_pages::android::OfflinePageBridge::MightBeOfflineURL(url
))
846 offline_pages::OfflinePageModel
* offline_page_model
=
847 offline_pages::OfflinePageModelFactory::GetForBrowserContext(
849 return offline_page_model
->GetPageByOfflineURL(url
);
852 bool TabAndroid::HasPrerenderedUrl(JNIEnv
* env
, jobject obj
, jstring url
) {
853 GURL
gurl(base::android::ConvertJavaStringToUTF8(env
, url
));
854 return HasPrerenderedUrl(gurl
);
857 void TabAndroid::OnRendererUnresponsive(JNIEnv
* env
, jobject obj
) {
858 InfoBarService
* infobar_service
=
859 InfoBarService::FromWebContents(web_contents());
860 DCHECK(!FindHungRendererInfoBar(infobar_service
));
861 HungRendererInfoBarDelegate::Create(infobar_service
,
862 web_contents()->GetRenderProcessHost());
865 void TabAndroid::OnRendererResponsive(JNIEnv
* env
, jobject obj
) {
866 InfoBarService
* infobar_service
=
867 InfoBarService::FromWebContents(web_contents());
868 infobars::InfoBar
* hung_renderer_infobar
=
869 FindHungRendererInfoBar(infobar_service
);
870 if (!hung_renderer_infobar
)
873 hung_renderer_infobar
->delegate()
874 ->AsHungRendererInfoBarDelegate()
875 ->OnRendererResponsive();
876 infobar_service
->RemoveInfoBar(hung_renderer_infobar
);
881 class ChromeInterceptNavigationDelegate
: public InterceptNavigationDelegate
{
883 ChromeInterceptNavigationDelegate(JNIEnv
* env
, jobject jdelegate
)
884 : InterceptNavigationDelegate(env
, jdelegate
) {}
886 bool ShouldIgnoreNavigation(
887 const NavigationParams
& navigation_params
) override
{
888 NavigationParams
chrome_navigation_params(navigation_params
);
889 chrome_navigation_params
.url() =
890 GURL(net::EscapeExternalHandlerValue(navigation_params
.url().spec()));
891 return InterceptNavigationDelegate::ShouldIgnoreNavigation(
892 chrome_navigation_params
);
898 void TabAndroid::SetInterceptNavigationDelegate(JNIEnv
* env
, jobject obj
,
900 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
901 InterceptNavigationDelegate::Associate(
903 make_scoped_ptr(new ChromeInterceptNavigationDelegate(env
, delegate
)));
906 void TabAndroid::AttachToTabContentManager(JNIEnv
* env
,
908 jobject jtab_content_manager
) {
909 chrome::android::TabContentManager
* tab_content_manager
=
910 chrome::android::TabContentManager::FromJavaObject(jtab_content_manager
);
911 if (tab_content_manager
== tab_content_manager_
)
914 if (tab_content_manager_
)
915 tab_content_manager_
->DetachLiveLayer(GetAndroidId(), GetContentLayer());
916 tab_content_manager_
= tab_content_manager
;
917 if (tab_content_manager_
)
918 tab_content_manager_
->AttachLiveLayer(GetAndroidId(), GetContentLayer());
921 void TabAndroid::AttachOverlayContentViewCore(JNIEnv
* env
,
923 jobject jcontent_view_core
,
925 content::ContentViewCore
* content_view_core
=
926 content::ContentViewCore::GetNativeContentViewCore(env
,
928 DCHECK(content_view_core
);
930 content_view_core
->GetLayer()->SetHideLayerAndSubtree(!visible
);
931 content_layer_
->AddChild(content_view_core
->GetLayer());
934 void TabAndroid::DetachOverlayContentViewCore(JNIEnv
* env
,
936 jobject jcontent_view_core
) {
937 content::ContentViewCore
* content_view_core
=
938 content::ContentViewCore::GetNativeContentViewCore(env
,
940 DCHECK(content_view_core
);
942 if (content_view_core
->GetLayer()->parent() == content_layer_
)
943 content_view_core
->GetLayer()->RemoveFromParent();
946 static void Init(JNIEnv
* env
, const JavaParamRef
<jobject
>& obj
) {
947 TRACE_EVENT0("native", "TabAndroid::Init");
948 // This will automatically bind to the Java object and pass ownership there.
949 new TabAndroid(env
, obj
);
953 bool TabAndroid::RegisterTabAndroid(JNIEnv
* env
) {
954 return RegisterNativesImpl(env
);
957 static void RecordStartupToCommitUma(JNIEnv
* env
,
958 const JavaParamRef
<jclass
>& jcaller
) {
959 // Currently it takes about 2000ms to commit a navigation if the measurement
960 // begins very early in the browser start. How many buckets (b) are needed to
961 // explore the _typical_ values with granularity 100ms and a maximum duration
963 // s^{n+1} / s^{n} = 2100 / 2000
966 // b = ln(60000) / ln(1.05) ~= 225
967 UMA_HISTOGRAM_CUSTOM_TIMES("Startup.FirstCommitNavigationTime",
968 base::Time::Now() - chrome::android::GetMainEntryPointTime(),
969 base::TimeDelta::FromMilliseconds(1),
970 base::TimeDelta::FromMinutes(1),