| // 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 <stdint.h> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/callback.h" |
| #include "base/containers/flat_map.h" |
| #include "base/strings/string16.h" |
| #include "content/browser/frame_host/navigation_controller_impl.h" |
| #include "content/browser/frame_host/navigation_entry_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/ssl_host_state_delegate.h" |
| #include "content/public/common/resource_request_body_android.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; |
| using base::android::JavaParamRef; |
| using base::android::JavaRef; |
| using base::android::ScopedJavaLocalRef; |
| |
| namespace { |
| |
| const char kMapDataKey[] = "map_data_key"; |
| |
| // static |
| static base::android::ScopedJavaLocalRef<jobject> |
| JNI_NavigationControllerImpl_CreateJavaNavigationEntry( |
| JNIEnv* env, |
| content::NavigationEntry* entry, |
| int index) { |
| DCHECK(entry); |
| |
| // Get the details of the current entry |
| ScopedJavaLocalRef<jstring> j_url( |
| ConvertUTF8ToJavaString(env, entry->GetURL().spec())); |
| ScopedJavaLocalRef<jstring> j_virtual_url( |
| ConvertUTF8ToJavaString(env, entry->GetVirtualURL().spec())); |
| ScopedJavaLocalRef<jstring> j_original_url( |
| ConvertUTF8ToJavaString(env, entry->GetOriginalRequestURL().spec())); |
| ScopedJavaLocalRef<jstring> j_title( |
| ConvertUTF16ToJavaString(env, entry->GetTitle())); |
| ScopedJavaLocalRef<jstring> j_referrer_url( |
| ConvertUTF8ToJavaString(env, entry->GetReferrer().url.spec())); |
| ScopedJavaLocalRef<jobject> j_bitmap; |
| const content::FaviconStatus& status = entry->GetFavicon(); |
| if (status.valid && status.image.ToSkBitmap()->computeByteSize() > 0) |
| j_bitmap = gfx::ConvertToJavaBitmap(status.image.ToSkBitmap()); |
| jlong j_timestamp = entry->GetTimestamp().ToJavaTime(); |
| |
| return content::Java_NavigationControllerImpl_createNavigationEntry( |
| env, index, j_url, j_virtual_url, j_original_url, j_referrer_url, j_title, |
| j_bitmap, entry->GetTransitionType(), j_timestamp); |
| } |
| |
| static void JNI_NavigationControllerImpl_AddNavigationEntryToHistory( |
| JNIEnv* env, |
| const JavaRef<jobject>& history, |
| content::NavigationEntry* entry, |
| int index) { |
| content::Java_NavigationControllerImpl_addToNavigationHistory( |
| env, history, |
| JNI_NavigationControllerImpl_CreateJavaNavigationEntry(env, entry, |
| index)); |
| } |
| |
| class MapData : public base::SupportsUserData::Data { |
| public: |
| MapData() = default; |
| ~MapData() override = default; |
| |
| static MapData* Get(content::NavigationEntry* entry) { |
| MapData* map_data = static_cast<MapData*>(entry->GetUserData(kMapDataKey)); |
| if (map_data) |
| return map_data; |
| auto map_data_ptr = std::make_unique<MapData>(); |
| map_data = map_data_ptr.get(); |
| entry->SetUserData(kMapDataKey, std::move(map_data_ptr)); |
| return map_data; |
| } |
| |
| base::flat_map<std::string, base::string16>& map() { return map_; } |
| |
| // base::SupportsUserData::Data: |
| std::unique_ptr<Data> Clone() override { |
| std::unique_ptr<MapData> clone = std::make_unique<MapData>(); |
| clone->map_ = map_; |
| return clone; |
| } |
| |
| private: |
| base::flat_map<std::string, base::string16> map_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MapData); |
| }; |
| |
| } // namespace |
| |
| namespace content { |
| |
| NavigationControllerAndroid::NavigationControllerAndroid( |
| NavigationControllerImpl* navigation_controller) |
| : navigation_controller_(navigation_controller) { |
| JNIEnv* env = AttachCurrentThread(); |
| obj_.Reset(env, |
| Java_NavigationControllerImpl_create( |
| env, reinterpret_cast<intptr_t>(this)).obj()); |
| } |
| |
| NavigationControllerAndroid::~NavigationControllerAndroid() { |
| Java_NavigationControllerImpl_destroy(AttachCurrentThread(), obj_); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| NavigationControllerAndroid::GetJavaObject() { |
| return base::android::ScopedJavaLocalRef<jobject>(obj_); |
| } |
| |
| jboolean NavigationControllerAndroid::CanGoBack( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return navigation_controller_->CanGoBack(); |
| } |
| |
| jboolean NavigationControllerAndroid::CanGoForward( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return navigation_controller_->CanGoForward(); |
| } |
| |
| jboolean NavigationControllerAndroid::CanGoToOffset( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jint offset) { |
| return navigation_controller_->CanGoToOffset(offset); |
| } |
| |
| void NavigationControllerAndroid::GoBack(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| navigation_controller_->GoBack(); |
| } |
| |
| void NavigationControllerAndroid::GoForward(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| navigation_controller_->GoForward(); |
| } |
| |
| void NavigationControllerAndroid::GoToOffset(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jint offset) { |
| navigation_controller_->GoToOffset(offset); |
| } |
| |
| jboolean NavigationControllerAndroid::IsInitialNavigation( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return navigation_controller_->IsInitialNavigation(); |
| } |
| |
| void NavigationControllerAndroid::LoadIfNecessary( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| navigation_controller_->LoadIfNecessary(); |
| } |
| |
| void NavigationControllerAndroid::ContinuePendingReload( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| navigation_controller_->ContinuePendingReload(); |
| } |
| |
| void NavigationControllerAndroid::Reload(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean check_for_repost) { |
| navigation_controller_->Reload(ReloadType::NORMAL, check_for_repost); |
| } |
| |
| void NavigationControllerAndroid::ReloadBypassingCache( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean check_for_repost) { |
| navigation_controller_->Reload(ReloadType::BYPASSING_CACHE, check_for_repost); |
| } |
| |
| jboolean NavigationControllerAndroid::NeedsReload( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return navigation_controller_->NeedsReload(); |
| } |
| |
| void NavigationControllerAndroid::SetNeedsReload( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| navigation_controller_->SetNeedsReload(); |
| } |
| |
| void NavigationControllerAndroid::CancelPendingReload( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| navigation_controller_->CancelPendingReload(); |
| } |
| |
| void NavigationControllerAndroid::GoToNavigationIndex( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jint index) { |
| navigation_controller_->GoToIndex(index); |
| } |
| |
| void NavigationControllerAndroid::LoadUrl( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jstring>& url, |
| jint load_url_type, |
| jint transition_type, |
| const JavaParamRef<jstring>& j_referrer_url, |
| jint referrer_policy, |
| jint ua_override_option, |
| const JavaParamRef<jstring>& extra_headers, |
| const JavaParamRef<jobject>& j_post_data, |
| const JavaParamRef<jstring>& base_url_for_data_url, |
| const JavaParamRef<jstring>& virtual_url_for_data_url, |
| const JavaParamRef<jstring>& 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<NavigationController::LoadURLType>(load_url_type); |
| params.transition_type = ui::PageTransitionFromInt(transition_type); |
| params.override_user_agent = |
| static_cast<NavigationController::UserAgentOverrideOption>( |
| 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); |
| |
| params.post_data = ExtractResourceRequestBodyFromJavaObject(env, j_post_data); |
| |
| 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<network::mojom::ReferrerPolicy>(referrer_policy)); |
| } |
| |
| navigation_controller_->LoadURLWithParams(params); |
| } |
| |
| void NavigationControllerAndroid::ClearHistory( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& 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<jobject>& obj, |
| const JavaParamRef<jobject>& history) { |
| // Iterate through navigation entries to populate the list |
| int count = navigation_controller_->GetEntryCount(); |
| for (int i = 0; i < count; ++i) { |
| JNI_NavigationControllerImpl_AddNavigationEntryToHistory( |
| env, history, navigation_controller_->GetEntryAtIndex(i), i); |
| } |
| |
| return navigation_controller_->GetCurrentEntryIndex(); |
| } |
| |
| void NavigationControllerAndroid::GetDirectedNavigationHistory( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& 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; |
| |
| JNI_NavigationControllerImpl_AddNavigationEntryToHistory( |
| env, history, navigation_controller_->GetEntryAtIndex(i), i); |
| num_added++; |
| } |
| } |
| |
| void NavigationControllerAndroid::ClearSslPreferences( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| content::SSLHostStateDelegate* delegate = |
| navigation_controller_->GetBrowserContext()->GetSSLHostStateDelegate(); |
| if (delegate) |
| delegate->Clear(base::Callback<bool(const std::string&)>()); |
| } |
| |
| bool NavigationControllerAndroid::GetUseDesktopUserAgent( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| NavigationEntry* entry = navigation_controller_->GetVisibleEntry(); |
| return entry && entry->GetIsOverridingUserAgent(); |
| } |
| |
| void NavigationControllerAndroid::SetUseDesktopUserAgent( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& 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); |
| navigation_controller_->delegate()->UpdateOverridingUserAgent(); |
| |
| // 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_->Reload(ReloadType::ORIGINAL_REQUEST_URL, true); |
| } |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| NavigationControllerAndroid::GetEntryAtIndex(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| int index) { |
| if (index < 0 || index >= navigation_controller_->GetEntryCount()) |
| return base::android::ScopedJavaLocalRef<jobject>(); |
| |
| content::NavigationEntry* entry = |
| navigation_controller_->GetEntryAtIndex(index); |
| return JNI_NavigationControllerImpl_CreateJavaNavigationEntry(env, entry, |
| index); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| NavigationControllerAndroid::GetVisibleEntry(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| content::NavigationEntry* entry = navigation_controller_->GetVisibleEntry(); |
| |
| if (!entry) |
| return base::android::ScopedJavaLocalRef<jobject>(); |
| |
| return JNI_NavigationControllerImpl_CreateJavaNavigationEntry(env, entry, |
| /*index=*/-1); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| NavigationControllerAndroid::GetPendingEntry(JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| content::NavigationEntry* entry = navigation_controller_->GetPendingEntry(); |
| |
| if (!entry) |
| return base::android::ScopedJavaLocalRef<jobject>(); |
| |
| return JNI_NavigationControllerImpl_CreateJavaNavigationEntry( |
| env, entry, navigation_controller_->GetPendingEntryIndex()); |
| } |
| |
| jint NavigationControllerAndroid::GetLastCommittedEntryIndex( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj) { |
| return navigation_controller_->GetLastCommittedEntryIndex(); |
| } |
| |
| jboolean NavigationControllerAndroid::RemoveEntryAtIndex( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jint index) { |
| return navigation_controller_->RemoveEntryAtIndex(index); |
| } |
| |
| ScopedJavaLocalRef<jstring> NavigationControllerAndroid::GetEntryExtraData( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jint index, |
| const JavaParamRef<jstring>& jkey) { |
| if (index < 0 || index >= navigation_controller_->GetEntryCount()) |
| return ScopedJavaLocalRef<jstring>(); |
| |
| std::string key = base::android::ConvertJavaStringToUTF8(env, jkey); |
| MapData* map_data = |
| MapData::Get(navigation_controller_->GetEntryAtIndex(index)); |
| auto iter = map_data->map().find(key); |
| return ConvertUTF16ToJavaString( |
| env, iter == map_data->map().end() ? base::string16() : iter->second); |
| } |
| |
| void NavigationControllerAndroid::SetEntryExtraData( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jint index, |
| const JavaParamRef<jstring>& jkey, |
| const JavaParamRef<jstring>& jvalue) { |
| if (index < 0 || index >= navigation_controller_->GetEntryCount()) |
| return; |
| |
| std::string key = base::android::ConvertJavaStringToUTF8(env, jkey); |
| base::string16 value = base::android::ConvertJavaStringToUTF16(env, jvalue); |
| MapData* map_data = |
| MapData::Get(navigation_controller_->GetEntryAtIndex(index)); |
| map_data->map()[key] = value; |
| } |
| |
| jboolean NavigationControllerAndroid::IsEntryMarkedToBeSkipped( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jint index) { |
| return navigation_controller_->IsEntryMarkedToBeSkipped(index); |
| } |
| |
| } // namespace content |