// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/frame_host/navigation_controller_android.h" #include #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "content/browser/frame_host/navigation_entry_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/ssl_host_state_delegate.h" #include "jni/NavigationControllerImpl_jni.h" #include "net/base/data_url.h" #include "ui/gfx/android/java_bitmap.h" using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF16; using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF16ToJavaString; using base::android::ConvertUTF8ToJavaString; namespace { // static static base::android::ScopedJavaLocalRef CreateJavaNavigationEntry( JNIEnv* env, content::NavigationEntry* entry, int index) { DCHECK(entry); // Get the details of the current entry ScopedJavaLocalRef j_url( ConvertUTF8ToJavaString(env, entry->GetURL().spec())); ScopedJavaLocalRef j_virtual_url( ConvertUTF8ToJavaString(env, entry->GetVirtualURL().spec())); ScopedJavaLocalRef j_original_url( ConvertUTF8ToJavaString(env, entry->GetOriginalRequestURL().spec())); ScopedJavaLocalRef j_title( ConvertUTF16ToJavaString(env, entry->GetTitle())); ScopedJavaLocalRef j_bitmap; const content::FaviconStatus& status = entry->GetFavicon(); if (status.valid && status.image.ToSkBitmap()->getSize() > 0) j_bitmap = gfx::ConvertToJavaBitmap(status.image.ToSkBitmap()); return content::Java_NavigationControllerImpl_createNavigationEntry( env, index, j_url.obj(), j_virtual_url.obj(), j_original_url.obj(), j_title.obj(), j_bitmap.obj(), entry->GetTransitionType()); } static void AddNavigationEntryToHistory(JNIEnv* env, jobject history, content::NavigationEntry* entry, int index) { content::Java_NavigationControllerImpl_addToNavigationHistory( env, history, CreateJavaNavigationEntry(env, entry, index).obj()); } } // namespace namespace content { // static bool NavigationControllerAndroid::Register(JNIEnv* env) { return RegisterNativesImpl(env); } NavigationControllerAndroid::NavigationControllerAndroid( NavigationController* navigation_controller) : navigation_controller_(navigation_controller) { JNIEnv* env = AttachCurrentThread(); obj_.Reset(env, Java_NavigationControllerImpl_create( env, reinterpret_cast(this)).obj()); } NavigationControllerAndroid::~NavigationControllerAndroid() { Java_NavigationControllerImpl_destroy(AttachCurrentThread(), obj_.obj()); } base::android::ScopedJavaLocalRef NavigationControllerAndroid::GetJavaObject() { return base::android::ScopedJavaLocalRef(obj_); } jboolean NavigationControllerAndroid::CanGoBack( JNIEnv* env, const JavaParamRef& obj) { return navigation_controller_->CanGoBack(); } jboolean NavigationControllerAndroid::CanGoForward( JNIEnv* env, const JavaParamRef& obj) { return navigation_controller_->CanGoForward(); } jboolean NavigationControllerAndroid::CanGoToOffset( JNIEnv* env, const JavaParamRef& obj, jint offset) { return navigation_controller_->CanGoToOffset(offset); } void NavigationControllerAndroid::GoBack(JNIEnv* env, const JavaParamRef& obj) { navigation_controller_->GoBack(); } void NavigationControllerAndroid::GoForward(JNIEnv* env, const JavaParamRef& obj) { navigation_controller_->GoForward(); } void NavigationControllerAndroid::GoToOffset(JNIEnv* env, const JavaParamRef& obj, jint offset) { navigation_controller_->GoToOffset(offset); } jboolean NavigationControllerAndroid::IsInitialNavigation( JNIEnv* env, const JavaParamRef& obj) { return navigation_controller_->IsInitialNavigation(); } void NavigationControllerAndroid::LoadIfNecessary( JNIEnv* env, const JavaParamRef& obj) { navigation_controller_->LoadIfNecessary(); } void NavigationControllerAndroid::ContinuePendingReload( JNIEnv* env, const JavaParamRef& obj) { navigation_controller_->ContinuePendingReload(); } void NavigationControllerAndroid::Reload(JNIEnv* env, const JavaParamRef& obj, jboolean check_for_repost) { navigation_controller_->Reload(check_for_repost); } void NavigationControllerAndroid::ReloadToRefreshContent( JNIEnv* env, jobject obj, jboolean check_for_repost) { navigation_controller_->ReloadToRefreshContent(check_for_repost); } void NavigationControllerAndroid::ReloadIgnoringCache( JNIEnv* env, const JavaParamRef& obj, jboolean check_for_repost) { navigation_controller_->ReloadIgnoringCache(check_for_repost); } void NavigationControllerAndroid::ReloadDisableLoFi( JNIEnv* env, const JavaParamRef& obj, jboolean check_for_repost) { navigation_controller_->ReloadDisableLoFi(check_for_repost); } void NavigationControllerAndroid::RequestRestoreLoad( JNIEnv* env, const JavaParamRef& obj) { navigation_controller_->SetNeedsReload(); } void NavigationControllerAndroid::CancelPendingReload( JNIEnv* env, const JavaParamRef& obj) { navigation_controller_->CancelPendingReload(); } void NavigationControllerAndroid::GoToNavigationIndex( JNIEnv* env, const JavaParamRef& obj, jint index) { navigation_controller_->GoToIndex(index); } void NavigationControllerAndroid::LoadUrl( JNIEnv* env, const JavaParamRef& obj, const JavaParamRef& url, jint load_url_type, jint transition_type, const JavaParamRef& j_referrer_url, jint referrer_policy, jint ua_override_option, const JavaParamRef& extra_headers, const JavaParamRef& post_data, const JavaParamRef& base_url_for_data_url, const JavaParamRef& virtual_url_for_data_url, const JavaParamRef& data_url_as_string, jboolean can_load_local_resources, jboolean is_renderer_initiated, jboolean should_replace_current_entry) { DCHECK(url); NavigationController::LoadURLParams params( GURL(ConvertJavaStringToUTF8(env, url))); params.load_type = static_cast(load_url_type); params.transition_type = ui::PageTransitionFromInt(transition_type); params.override_user_agent = static_cast( ua_override_option); params.can_load_local_resources = can_load_local_resources; params.is_renderer_initiated = is_renderer_initiated; params.should_replace_current_entry = should_replace_current_entry; if (extra_headers) params.extra_headers = ConvertJavaStringToUTF8(env, extra_headers); if (post_data) { std::vector http_body_vector; base::android::JavaByteArrayToByteVector(env, post_data, &http_body_vector); params.browser_initiated_post_data = base::RefCountedBytes::TakeVector(&http_body_vector); } if (base_url_for_data_url) { params.base_url_for_data_url = GURL(ConvertJavaStringToUTF8(env, base_url_for_data_url)); } if (virtual_url_for_data_url) { params.virtual_url_for_data_url = GURL(ConvertJavaStringToUTF8(env, virtual_url_for_data_url)); } if (data_url_as_string) { // Treat |data_url_as_string| as if we were intending to put it into a GURL // field. Note that kMaxURLChars is only enforced when serializing URLs // for IPC. GURL data_url = GURL(ConvertJavaStringToUTF8(env, data_url_as_string)); DCHECK(data_url.SchemeIs(url::kDataScheme)); DCHECK(params.url.SchemeIs(url::kDataScheme)); #if DCHECK_IS_ON() { std::string mime_type, charset, data; DCHECK(net::DataURL::Parse(params.url, &mime_type, &charset, &data)); DCHECK(data.empty()); } #endif std::string s = data_url.spec(); params.data_url_as_string = base::RefCountedString::TakeString(&s); } if (j_referrer_url) { params.referrer = content::Referrer( GURL(ConvertJavaStringToUTF8(env, j_referrer_url)), static_cast(referrer_policy)); } navigation_controller_->LoadURLWithParams(params); } void NavigationControllerAndroid::ClearHistory( JNIEnv* env, const JavaParamRef& obj) { // TODO(creis): Do callers of this need to know if it fails? if (navigation_controller_->CanPruneAllButLastCommitted()) navigation_controller_->PruneAllButLastCommitted(); } jint NavigationControllerAndroid::GetNavigationHistory( JNIEnv* env, const JavaParamRef& obj, const JavaParamRef& history) { // Iterate through navigation entries to populate the list int count = navigation_controller_->GetEntryCount(); for (int i = 0; i < count; ++i) { AddNavigationEntryToHistory( env, history, navigation_controller_->GetEntryAtIndex(i), i); } return navigation_controller_->GetCurrentEntryIndex(); } void NavigationControllerAndroid::GetDirectedNavigationHistory( JNIEnv* env, const JavaParamRef& obj, const JavaParamRef& history, jboolean is_forward, jint max_entries) { // Iterate through navigation entries to populate the list int count = navigation_controller_->GetEntryCount(); int num_added = 0; int increment_value = is_forward ? 1 : -1; for (int i = navigation_controller_->GetCurrentEntryIndex() + increment_value; i >= 0 && i < count; i += increment_value) { if (num_added >= max_entries) break; AddNavigationEntryToHistory( env, history, navigation_controller_->GetEntryAtIndex(i), i); num_added++; } } ScopedJavaLocalRef NavigationControllerAndroid::GetOriginalUrlForVisibleNavigationEntry( JNIEnv* env, const JavaParamRef& obj) { NavigationEntry* entry = navigation_controller_->GetVisibleEntry(); if (entry == NULL) return ScopedJavaLocalRef(env, NULL); return ConvertUTF8ToJavaString(env, entry->GetOriginalRequestURL().spec()); } void NavigationControllerAndroid::ClearSslPreferences( JNIEnv* env, const JavaParamRef& obj) { content::SSLHostStateDelegate* delegate = navigation_controller_->GetBrowserContext()->GetSSLHostStateDelegate(); if (delegate) delegate->Clear(); } bool NavigationControllerAndroid::GetUseDesktopUserAgent( JNIEnv* env, const JavaParamRef& obj) { NavigationEntry* entry = navigation_controller_->GetVisibleEntry(); return entry && entry->GetIsOverridingUserAgent(); } void NavigationControllerAndroid::SetUseDesktopUserAgent( JNIEnv* env, const JavaParamRef& obj, jboolean enabled, jboolean reload_on_state_change) { if (GetUseDesktopUserAgent(env, obj) == enabled) return; // Make sure the navigation entry actually exists. NavigationEntry* entry = navigation_controller_->GetVisibleEntry(); if (!entry) return; // Set the flag in the NavigationEntry. entry->SetIsOverridingUserAgent(enabled); // Send the override to the renderer. if (reload_on_state_change) { // Reloading the page will send the override down as part of the // navigation IPC message. navigation_controller_->ReloadOriginalRequestURL(false); } } base::android::ScopedJavaLocalRef NavigationControllerAndroid::GetEntryAtIndex(JNIEnv* env, const JavaParamRef& obj, int index) { if (index < 0 || index >= navigation_controller_->GetEntryCount()) return base::android::ScopedJavaLocalRef(); content::NavigationEntry* entry = navigation_controller_->GetEntryAtIndex(index); return CreateJavaNavigationEntry(env, entry, index); } base::android::ScopedJavaLocalRef NavigationControllerAndroid::GetPendingEntry(JNIEnv* env, const JavaParamRef& obj) { content::NavigationEntry* entry = navigation_controller_->GetPendingEntry(); if (!entry) return base::android::ScopedJavaLocalRef(); return CreateJavaNavigationEntry( env, entry, navigation_controller_->GetPendingEntryIndex()); } jint NavigationControllerAndroid::GetLastCommittedEntryIndex( JNIEnv* env, const JavaParamRef& obj) { return navigation_controller_->GetLastCommittedEntryIndex(); } jboolean NavigationControllerAndroid::RemoveEntryAtIndex( JNIEnv* env, const JavaParamRef& obj, jint index) { return navigation_controller_->RemoveEntryAtIndex(index); } jboolean NavigationControllerAndroid::CanCopyStateOver( JNIEnv* env, const JavaParamRef& obj) { return navigation_controller_->GetEntryCount() == 0 && !navigation_controller_->GetPendingEntry(); } jboolean NavigationControllerAndroid::CanPruneAllButLastCommitted( JNIEnv* env, const JavaParamRef& obj) { return navigation_controller_->CanPruneAllButLastCommitted(); } void NavigationControllerAndroid::CopyStateFrom( JNIEnv* env, const JavaParamRef& obj, jlong source_navigation_controller_android) { navigation_controller_->CopyStateFrom( *(reinterpret_cast( source_navigation_controller_android)->navigation_controller_)); } void NavigationControllerAndroid::CopyStateFromAndPrune( JNIEnv* env, const JavaParamRef& obj, jlong source_navigation_controller_android, jboolean replace_entry) { navigation_controller_->CopyStateFromAndPrune( reinterpret_cast( source_navigation_controller_android)->navigation_controller_, replace_entry); } } // namespace content