[Android] Delete local java reference every loop.
[chromium-blink-merge.git] / chrome / browser / android / tab_android.cc
blob773d1f047f9eb4250ad17419dfef609ff0b9519b
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/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/google/google_url_tracker.h"
16 #include "chrome/browser/google/google_util.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/sessions/session_tab_helper.h"
25 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
26 #include "chrome/browser/ui/android/content_settings/popup_blocked_infobar_delegate.h"
27 #include "chrome/browser/ui/android/context_menu_helper.h"
28 #include "chrome/browser/ui/android/infobars/infobar_container_android.h"
29 #include "chrome/browser/ui/android/tab_model/tab_model.h"
30 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
31 #include "chrome/browser/ui/android/window_android_helper.h"
32 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
33 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
34 #include "chrome/browser/ui/tab_helpers.h"
35 #include "chrome/browser/ui/toolbar/toolbar_model_impl.h"
36 #include "chrome/common/net/url_fixer_upper.h"
37 #include "chrome/common/url_constants.h"
38 #include "components/infobars/core/infobar_container.h"
39 #include "content/public/browser/android/content_view_core.h"
40 #include "content/public/browser/navigation_entry.h"
41 #include "content/public/browser/notification_service.h"
42 #include "content/public/browser/user_metrics.h"
43 #include "content/public/browser/web_contents.h"
44 #include "jni/Tab_jni.h"
45 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
47 TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
48 CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents);
49 if (!core_tab_helper)
50 return NULL;
52 CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
53 if (!core_delegate)
54 return NULL;
56 return static_cast<TabAndroid*>(core_delegate);
59 TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
60 return reinterpret_cast<TabAndroid*>(Java_Tab_getNativePtr(env, obj));
63 void TabAndroid::AttachTabHelpers(content::WebContents* web_contents) {
64 DCHECK(web_contents);
66 TabHelpers::AttachTabHelpers(web_contents);
69 TabAndroid::TabAndroid(JNIEnv* env, jobject obj)
70 : weak_java_tab_(env, obj),
71 synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)) {
72 Java_Tab_setNativePtr(env, obj, reinterpret_cast<intptr_t>(this));
75 TabAndroid::~TabAndroid() {
76 JNIEnv* env = base::android::AttachCurrentThread();
77 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
78 if (obj.is_null())
79 return;
81 Java_Tab_clearNativePtr(env, obj.obj());
84 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetJavaObject() {
85 JNIEnv* env = base::android::AttachCurrentThread();
86 return weak_java_tab_.get(env);
89 int TabAndroid::GetAndroidId() const {
90 JNIEnv* env = base::android::AttachCurrentThread();
91 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
92 if (obj.is_null())
93 return -1;
94 return Java_Tab_getId(env, obj.obj());
97 int TabAndroid::GetSyncId() const {
98 JNIEnv* env = base::android::AttachCurrentThread();
99 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
100 if (obj.is_null())
101 return 0;
102 return Java_Tab_getSyncId(env, obj.obj());
105 base::string16 TabAndroid::GetTitle() const {
106 JNIEnv* env = base::android::AttachCurrentThread();
107 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
108 if (obj.is_null())
109 return base::string16();
110 return base::android::ConvertJavaStringToUTF16(
111 Java_Tab_getTitle(env, obj.obj()));
114 GURL TabAndroid::GetURL() const {
115 JNIEnv* env = base::android::AttachCurrentThread();
116 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
117 if (obj.is_null())
118 return GURL::EmptyGURL();
119 return GURL(base::android::ConvertJavaStringToUTF8(
120 Java_Tab_getUrl(env, obj.obj())));
123 bool TabAndroid::LoadIfNeeded() {
124 JNIEnv* env = base::android::AttachCurrentThread();
125 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
126 if (obj.is_null())
127 return false;
128 return Java_Tab_loadIfNeeded(env, obj.obj());
131 content::ContentViewCore* TabAndroid::GetContentViewCore() const {
132 if (!web_contents())
133 return NULL;
135 return content::ContentViewCore::FromWebContents(web_contents());
138 Profile* TabAndroid::GetProfile() const {
139 if (!web_contents())
140 return NULL;
142 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
145 browser_sync::SyncedTabDelegate* TabAndroid::GetSyncedTabDelegate() const {
146 return synced_tab_delegate_.get();
149 void TabAndroid::SetWindowSessionID(SessionID::id_type window_id) {
150 session_window_id_.set_id(window_id);
152 if (!web_contents())
153 return;
155 SessionTabHelper* session_tab_helper =
156 SessionTabHelper::FromWebContents(web_contents());
157 session_tab_helper->SetWindowID(session_window_id_);
160 void TabAndroid::SetSyncId(int sync_id) {
161 JNIEnv* env = base::android::AttachCurrentThread();
162 ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
163 if (obj.is_null())
164 return;
165 Java_Tab_setSyncId(env, obj.obj(), sync_id);
168 void TabAndroid::HandlePopupNavigation(chrome::NavigateParams* params) {
169 NOTIMPLEMENTED();
172 void TabAndroid::OnReceivedHttpAuthRequest(jobject auth_handler,
173 const base::string16& host,
174 const base::string16& realm) {
175 NOTIMPLEMENTED();
178 void TabAndroid::AddShortcutToBookmark(const GURL& url,
179 const base::string16& title,
180 const SkBitmap& skbitmap,
181 int r_value,
182 int g_value,
183 int b_value) {
184 NOTREACHED();
187 void TabAndroid::EditBookmark(int64 node_id,
188 const base::string16& node_title,
189 bool is_folder,
190 bool is_partner_bookmark) {
191 NOTREACHED();
194 void TabAndroid::OnNewTabPageReady() {
195 NOTREACHED();
198 bool TabAndroid::ShouldWelcomePageLinkToTermsOfService() {
199 NOTIMPLEMENTED();
200 return false;
203 bool TabAndroid::HasPrerenderedUrl(GURL gurl) {
204 prerender::PrerenderManager* prerender_manager = GetPrerenderManager();
205 if (!prerender_manager)
206 return false;
208 std::vector<content::WebContents*> contents =
209 prerender_manager->GetAllPrerenderingContents();
210 prerender::PrerenderContents* prerender_contents;
211 for (size_t i = 0; i < contents.size(); ++i) {
212 prerender_contents = prerender_manager->
213 GetPrerenderContents(contents.at(i));
214 if (prerender_contents->prerender_url() == gurl &&
215 prerender_contents->has_finished_loading()) {
216 return true;
219 return false;
222 void TabAndroid::SwapTabContents(content::WebContents* old_contents,
223 content::WebContents* new_contents,
224 bool did_start_load,
225 bool did_finish_load) {
226 JNIEnv* env = base::android::AttachCurrentThread();
228 // We need to notify the native InfobarContainer so infobars can be swapped.
229 InfoBarContainerAndroid* infobar_container =
230 reinterpret_cast<InfoBarContainerAndroid*>(
231 Java_Tab_getNativeInfoBarContainer(
232 env,
233 weak_java_tab_.get(env).obj()));
234 InfoBarService* new_infobar_service =
235 new_contents ? InfoBarService::FromWebContents(new_contents) : NULL;
236 infobar_container->ChangeInfoBarManager(new_infobar_service);
238 Java_Tab_swapWebContents(
239 env,
240 weak_java_tab_.get(env).obj(),
241 reinterpret_cast<intptr_t>(new_contents),
242 did_start_load,
243 did_finish_load);
246 void TabAndroid::Observe(int type,
247 const content::NotificationSource& source,
248 const content::NotificationDetails& details) {
249 JNIEnv* env = base::android::AttachCurrentThread();
250 switch (type) {
251 case chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED: {
252 TabSpecificContentSettings* settings =
253 TabSpecificContentSettings::FromWebContents(web_contents());
254 if (!settings->IsBlockageIndicated(CONTENT_SETTINGS_TYPE_POPUPS)) {
255 // TODO(dfalcantara): Create an InfoBarDelegate to keep the
256 // PopupBlockedInfoBar logic native-side instead of straddling the JNI
257 // boundary.
258 int num_popups = 0;
259 PopupBlockerTabHelper* popup_blocker_helper =
260 PopupBlockerTabHelper::FromWebContents(web_contents());
261 if (popup_blocker_helper)
262 num_popups = popup_blocker_helper->GetBlockedPopupsCount();
264 if (num_popups > 0)
265 PopupBlockedInfoBarDelegate::Create(web_contents(), num_popups);
267 settings->SetBlockageHasBeenIndicated(CONTENT_SETTINGS_TYPE_POPUPS);
269 break;
271 case chrome::NOTIFICATION_FAVICON_UPDATED:
272 Java_Tab_onFaviconUpdated(env, weak_java_tab_.get(env).obj());
273 break;
274 case content::NOTIFICATION_NAV_ENTRY_CHANGED:
275 Java_Tab_onNavEntryChanged(env, weak_java_tab_.get(env).obj());
276 break;
277 default:
278 NOTREACHED() << "Unexpected notification " << type;
279 break;
283 void TabAndroid::Destroy(JNIEnv* env, jobject obj) {
284 delete this;
287 void TabAndroid::InitWebContents(JNIEnv* env,
288 jobject obj,
289 jboolean incognito,
290 jobject jcontent_view_core,
291 jobject jweb_contents_delegate,
292 jobject jcontext_menu_populator) {
293 content::ContentViewCore* content_view_core =
294 content::ContentViewCore::GetNativeContentViewCore(env,
295 jcontent_view_core);
296 DCHECK(content_view_core);
297 DCHECK(content_view_core->GetWebContents());
299 web_contents_.reset(content_view_core->GetWebContents());
300 AttachTabHelpers(web_contents_.get());
302 SetWindowSessionID(session_window_id_.id());
304 session_tab_id_.set_id(
305 SessionTabHelper::FromWebContents(web_contents())->session_id().id());
306 ContextMenuHelper::FromWebContents(web_contents())->SetPopulator(
307 jcontext_menu_populator);
308 WindowAndroidHelper::FromWebContents(web_contents())->
309 SetWindowAndroid(content_view_core->GetWindowAndroid());
310 CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
311 web_contents_delegate_.reset(
312 new chrome::android::ChromeWebContentsDelegateAndroid(
313 env, jweb_contents_delegate));
314 web_contents_delegate_->LoadProgressChanged(web_contents(), 0);
315 web_contents()->SetDelegate(web_contents_delegate_.get());
317 notification_registrar_.Add(
318 this,
319 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
320 content::Source<content::WebContents>(web_contents()));
321 notification_registrar_.Add(
322 this,
323 chrome::NOTIFICATION_FAVICON_UPDATED,
324 content::Source<content::WebContents>(web_contents()));
325 notification_registrar_.Add(
326 this,
327 content::NOTIFICATION_NAV_ENTRY_CHANGED,
328 content::Source<content::NavigationController>(
329 &web_contents()->GetController()));
331 synced_tab_delegate_->SetWebContents(web_contents());
333 // Verify that the WebContents this tab represents matches the expected
334 // off the record state.
335 CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito);
338 void TabAndroid::DestroyWebContents(JNIEnv* env,
339 jobject obj,
340 jboolean delete_native) {
341 DCHECK(web_contents());
343 notification_registrar_.Remove(
344 this,
345 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
346 content::Source<content::WebContents>(web_contents()));
347 notification_registrar_.Remove(
348 this,
349 chrome::NOTIFICATION_FAVICON_UPDATED,
350 content::Source<content::WebContents>(web_contents()));
351 notification_registrar_.Remove(
352 this,
353 content::NOTIFICATION_NAV_ENTRY_CHANGED,
354 content::Source<content::NavigationController>(
355 &web_contents()->GetController()));
357 web_contents()->SetDelegate(NULL);
359 if (delete_native) {
360 web_contents_.reset();
361 synced_tab_delegate_->ResetWebContents();
362 } else {
363 // Release the WebContents so it does not get deleted by the scoped_ptr.
364 ignore_result(web_contents_.release());
368 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetWebContents(
369 JNIEnv* env,
370 jobject obj) {
371 if (!web_contents_.get())
372 return base::android::ScopedJavaLocalRef<jobject>();
373 return web_contents_->GetJavaWebContents();
376 base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetProfileAndroid(
377 JNIEnv* env,
378 jobject obj) {
379 Profile* profile = GetProfile();
380 if (!profile)
381 return base::android::ScopedJavaLocalRef<jobject>();
382 ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile);
383 if (!profile_android)
384 return base::android::ScopedJavaLocalRef<jobject>();
386 return profile_android->GetJavaObject();
389 TabAndroid::TabLoadStatus TabAndroid::LoadUrl(JNIEnv* env,
390 jobject obj,
391 jstring url,
392 jstring j_extra_headers,
393 jbyteArray j_post_data,
394 jint page_transition,
395 jstring j_referrer_url,
396 jint referrer_policy) {
397 content::ContentViewCore* content_view = GetContentViewCore();
398 if (!content_view)
399 return PAGE_LOAD_FAILED;
401 GURL gurl(base::android::ConvertJavaStringToUTF8(env, url));
402 if (gurl.is_empty())
403 return PAGE_LOAD_FAILED;
405 // If the page was prerendered, use it.
406 // Note in incognito mode, we don't have a PrerenderManager.
408 prerender::PrerenderManager* prerender_manager =
409 prerender::PrerenderManagerFactory::GetForProfile(GetProfile());
410 if (prerender_manager) {
411 bool prefetched_page_loaded = HasPrerenderedUrl(gurl);
412 // Getting the load status before MaybeUsePrerenderedPage() b/c it resets.
413 chrome::NavigateParams params(NULL, web_contents());
414 if (prerender_manager->MaybeUsePrerenderedPage(gurl, &params)) {
415 return prefetched_page_loaded ?
416 FULL_PRERENDERED_PAGE_LOAD : PARTIAL_PRERENDERED_PAGE_LOAD;
420 GURL fixed_url(URLFixerUpper::FixupURL(gurl.possibly_invalid_spec(),
421 std::string()));
422 if (!fixed_url.is_valid())
423 return PAGE_LOAD_FAILED;
425 if (!HandleNonNavigationAboutURL(fixed_url)) {
426 // Notify the GoogleURLTracker of searches, it might want to change the
427 // actual Google site used (for instance when in the UK, google.co.uk, when
428 // in the US google.com).
429 // Note that this needs to happen before we initiate the navigation as the
430 // GoogleURLTracker uses the navigation pending notification to trigger the
431 // infobar.
432 if (google_util::IsGoogleSearchUrl(fixed_url) &&
433 (page_transition & content::PAGE_TRANSITION_GENERATED)) {
434 GoogleURLTracker::GoogleURLSearchCommitted(GetProfile());
437 // Record UMA "ShowHistory" here. That way it'll pick up both user
438 // typing chrome://history as well as selecting from the drop down menu.
439 if (fixed_url.spec() == chrome::kChromeUIHistoryURL) {
440 content::RecordAction(base::UserMetricsAction("ShowHistory"));
443 content::NavigationController::LoadURLParams load_params(fixed_url);
444 if (j_extra_headers) {
445 load_params.extra_headers = base::android::ConvertJavaStringToUTF8(
446 env,
447 j_extra_headers);
449 if (j_post_data) {
450 load_params.load_type =
451 content::NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
452 std::vector<uint8> post_data;
453 base::android::JavaByteArrayToByteVector(env, j_post_data, &post_data);
454 load_params.browser_initiated_post_data =
455 base::RefCountedBytes::TakeVector(&post_data);
457 load_params.transition_type =
458 content::PageTransitionFromInt(page_transition);
459 if (j_referrer_url) {
460 load_params.referrer = content::Referrer(
461 GURL(base::android::ConvertJavaStringToUTF8(env, j_referrer_url)),
462 static_cast<blink::WebReferrerPolicy>(referrer_policy));
464 content_view->LoadUrl(load_params);
466 return DEFAULT_PAGE_LOAD;
469 ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel(JNIEnv* env,
470 jobject obj) {
471 return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
474 void TabAndroid::SetActiveNavigationEntryTitleForUrl(JNIEnv* env,
475 jobject obj,
476 jstring jurl,
477 jstring jtitle) {
478 DCHECK(web_contents());
480 base::string16 title;
481 if (jtitle)
482 title = base::android::ConvertJavaStringToUTF16(env, jtitle);
484 std::string url;
485 if (jurl)
486 url = base::android::ConvertJavaStringToUTF8(env, jurl);
488 content::NavigationEntry* entry =
489 web_contents()->GetController().GetVisibleEntry();
490 if (entry && url == entry->GetVirtualURL().spec())
491 entry->SetTitle(title);
494 bool TabAndroid::Print(JNIEnv* env, jobject obj) {
495 if (!web_contents())
496 return false;
498 printing::PrintViewManagerBasic::CreateForWebContents(web_contents());
499 printing::PrintViewManagerBasic* print_view_manager =
500 printing::PrintViewManagerBasic::FromWebContents(web_contents());
501 if (print_view_manager == NULL)
502 return false;
504 print_view_manager->PrintNow();
505 return true;
508 prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
509 Profile* profile = GetProfile();
510 if (!profile)
511 return NULL;
512 return prerender::PrerenderManagerFactory::GetForProfile(profile);
515 static void Init(JNIEnv* env, jobject obj) {
516 TRACE_EVENT0("native", "TabAndroid::Init");
517 // This will automatically bind to the Java object and pass ownership there.
518 new TabAndroid(env, obj);
521 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
522 return RegisterNativesImpl(env);