blob: a06ecea6dda9f3e9e8d1b5da37ade531ac0e0e93 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/browser/aw_contents_io_thread_client.h"
#include <map>
#include <memory>
#include <optional>
#include <utility>
#include "android_webview/browser/aw_settings.h"
#include "android_webview/browser/network_service/aw_web_resource_intercept_response.h"
#include "android_webview/browser/network_service/aw_web_resource_request.h"
#include "android_webview/common/aw_features.h"
#include "android_webview/common/devtools_instrumentation.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/jni_weak_ref.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/trace_event/base_tracing.h"
#include "components/embedder_support/android/util/features.h"
#include "components/embedder_support/android/util/input_stream.h"
#include "components/embedder_support/android/util/web_resource_response.h"
#include "components/safe_browsing/core/common/features.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/frame_tree_node_id.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "net/base/data_url.h"
#include "services/network/public/cpp/resource_request.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "android_webview/browser_jni_headers/AwContentsBackgroundThreadClient_jni.h"
#include "android_webview/browser_jni_headers/AwContentsIoThreadClient_jni.h"
using base::LazyInstance;
using base::android::AttachCurrentThread;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfStrings;
using content::BrowserThread;
using content::RenderFrameHost;
using content::WebContents;
using std::map;
using std::pair;
using std::string;
namespace android_webview {
namespace {
using RenderFrameHostToWeakGlobalRefType =
map<content::GlobalRenderFrameHostToken, JavaObjectWeakGlobalRef>;
using HostsAndWeakGlobalRefPair =
pair<base::flat_set<raw_ptr<RenderFrameHost, CtnExperimental>>,
JavaObjectWeakGlobalRef>;
// When browser side navigation is enabled, RenderFrameIDs do not have
// valid render process host and render frame ids for frame navigations.
// We need to identify these by using FrameTreeNodeIds. Furthermore, we need
// to keep track of which RenderFrameHosts are associated with each
// FrameTreeNodeId, so we know when the last RenderFrameHost is deleted (and
// therefore the FrameTreeNodeId should be removed).
using FrameTreeNodeToWeakGlobalRefType =
map<content::FrameTreeNodeId, HostsAndWeakGlobalRefPair>;
// RfhToIoThreadClientMap -----------------------------------------------------
class RfhToIoThreadClientMap {
public:
static RfhToIoThreadClientMap* GetInstance();
void Set(const content::GlobalRenderFrameHostToken& rfh_token,
const JavaObjectWeakGlobalRef& client);
std::optional<JavaObjectWeakGlobalRef> Get(
const content::GlobalRenderFrameHostToken& rfh_token);
std::optional<JavaObjectWeakGlobalRef> Get(
content::FrameTreeNodeId frame_tree_node_id);
// Prefer to call these when RenderFrameHost* is available, because they
// update both maps at the same time.
void Set(RenderFrameHost* rfh, const JavaObjectWeakGlobalRef& client);
void Erase(RenderFrameHost* rfh);
void RenderFrameHostChanged(RenderFrameHost* old_rfh,
RenderFrameHost* new_rfh);
private:
base::Lock map_lock_;
// We maintain two maps simultaneously so that we can always get the correct
// JavaObjectWeakGlobalRef, even when only GlobalRenderFrameHostToken or
// FrameTreeNodeId is available.
RenderFrameHostToWeakGlobalRefType rfh_to_weak_global_ref_;
FrameTreeNodeToWeakGlobalRefType frame_tree_node_to_weak_global_ref_;
};
// static
LazyInstance<RfhToIoThreadClientMap>::DestructorAtExit g_instance_ =
LAZY_INSTANCE_INITIALIZER;
// static
RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() {
return g_instance_.Pointer();
}
void RfhToIoThreadClientMap::Set(
const content::GlobalRenderFrameHostToken& rfh_token,
const JavaObjectWeakGlobalRef& client) {
base::AutoLock lock(map_lock_);
rfh_to_weak_global_ref_[rfh_token] = client;
}
std::optional<JavaObjectWeakGlobalRef> RfhToIoThreadClientMap::Get(
const content::GlobalRenderFrameHostToken& rfh_token) {
base::AutoLock lock(map_lock_);
RenderFrameHostToWeakGlobalRefType::iterator iterator =
rfh_to_weak_global_ref_.find(rfh_token);
if (iterator == rfh_to_weak_global_ref_.end()) {
return std::nullopt;
} else {
return iterator->second;
}
}
std::optional<JavaObjectWeakGlobalRef> RfhToIoThreadClientMap::Get(
content::FrameTreeNodeId frame_tree_node_id) {
base::AutoLock lock(map_lock_);
FrameTreeNodeToWeakGlobalRefType::iterator iterator =
frame_tree_node_to_weak_global_ref_.find(frame_tree_node_id);
if (iterator == frame_tree_node_to_weak_global_ref_.end()) {
return std::nullopt;
} else {
return iterator->second.second;
}
}
void RfhToIoThreadClientMap::Set(RenderFrameHost* rfh,
const JavaObjectWeakGlobalRef& client) {
content::FrameTreeNodeId frame_tree_node_id = rfh->GetFrameTreeNodeId();
auto rfh_token = rfh->GetGlobalFrameToken();
base::AutoLock lock(map_lock_);
// If this FrameTreeNodeId already has an associated JavaObjectWeakGlobalRef,
// add this RenderFrameHost to the hosts set (it's harmless to overwrite the
// JavaObjectWeakGlobalRef). Otherwise, operator[] creates a new map entry and
// we add this RenderFrameHost to the hosts set and insert |client| in the
// pair.
HostsAndWeakGlobalRefPair& current_entry =
frame_tree_node_to_weak_global_ref_[frame_tree_node_id];
current_entry.second = client;
current_entry.first.insert(rfh);
// Always add the entry to the HostIdPair map, since entries are 1:1 with
// RenderFrameHosts.
rfh_to_weak_global_ref_[rfh_token] = client;
}
void RfhToIoThreadClientMap::Erase(RenderFrameHost* rfh) {
content::FrameTreeNodeId frame_tree_node_id = rfh->GetFrameTreeNodeId();
auto rfh_token = rfh->GetGlobalFrameToken();
base::AutoLock lock(map_lock_);
HostsAndWeakGlobalRefPair& current_entry =
frame_tree_node_to_weak_global_ref_[frame_tree_node_id];
size_t num_erased = current_entry.first.erase(rfh);
DCHECK_EQ(num_erased, 1u);
// Only remove this entry from the FrameTreeNodeId map if there are no more
// live RenderFrameHosts.
if (current_entry.first.empty()) {
frame_tree_node_to_weak_global_ref_.erase(frame_tree_node_id);
}
// Always safe to remove the entry from the HostIdPair map, since entries are
// 1:1 with RenderFrameHosts.
rfh_to_weak_global_ref_.erase(rfh_token);
}
void RfhToIoThreadClientMap::RenderFrameHostChanged(RenderFrameHost* old_rfh,
RenderFrameHost* new_rfh) {
// Handles FrameTree swap, which occurs only in prerender activation.
if (old_rfh == nullptr) {
return;
}
// Ensured by `WebContentsObserver::RenderFrameHostChanged()`.
CHECK(new_rfh);
// If the swap is for subframes, it's not a prerender activation and therefore
// not a FrameTree swap.
if (old_rfh->GetParentOrOuterDocument() ||
new_rfh->GetParentOrOuterDocument()) {
return;
}
// If this is a prerender activation, `new_rfh` should initially be associated
// with a prerendering FrameTree's root FrameTreeNode before getting
// transferred to the primary FrameTree's root FrameTreeNode during the
// navigation commit. (See also `PrerenderHost::Activate()`.) This means the
// entry for `new_rfh` used in `frame_tree_node_to_weak_global_ref_` will be
// keyed by the prerendering FrameTreeNode id. We want to move that entry to
// the primary root FTN's entry, so that future lookups will be correct.
//
// If `pre_swap_ftn_id` and `post_swap_ftn_id` are the same, it's not a
// FrameTree swap (and therefore not a prerender activation). So, there's no
// need to move entries.
content::FrameTreeNodeId pre_swap_ftn_id;
content::FrameTreeNodeId post_swap_ftn_id = new_rfh->GetFrameTreeNodeId();
CHECK_EQ(post_swap_ftn_id, old_rfh->GetFrameTreeNodeId());
base::AutoLock lock(map_lock_);
for (auto& [frame_tree_node_id, entry] :
frame_tree_node_to_weak_global_ref_) {
if (entry.first.contains(new_rfh)) {
pre_swap_ftn_id = frame_tree_node_id;
break;
}
}
CHECK(pre_swap_ftn_id);
if (pre_swap_ftn_id == post_swap_ftn_id) {
return;
}
// Otherwise, it is a prerender activation and `new_rfh` is now attached to
// frame tree node with `post_swap_ftn_id`. So, we move entry with key
// `(pre_swap_ftn_id, new_rfh)` to `(post_swap_ftn_id, new_rfh)`.
HostsAndWeakGlobalRefPair& pre_swap_entry =
frame_tree_node_to_weak_global_ref_[pre_swap_ftn_id];
HostsAndWeakGlobalRefPair& post_swap_entry =
frame_tree_node_to_weak_global_ref_[post_swap_ftn_id];
// Note that `post_swap_entry.second == pre_swap_entry.second` must hold
// because `old_rfh` and `new_rfh` belongs to the same WebContents. It's nice
// to `CHECK_EQ`, but we can't because `JavaObjectWeakGlobalRef` doesn't
// provide comparison.
//
// Note that we don't modify `rfh_to_weak_global_ref_` in this function, as we
// are only handling the moving of the entries of
// `frame_tree_node_to_weak_global_ref_` that is affected by the FrameTreeNode
// change here. Addition/deletion of RenderFrameHosts is handled by
// `WebContentsObserver::RenderFrameCreated()` and
// `WebContentsObserver::RenderFrameDeleted()` calling `Set()` and `Erase()`
// respectively.
size_t num_erased = pre_swap_entry.first.erase(new_rfh);
CHECK_EQ(num_erased, 1u);
post_swap_entry.first.insert(new_rfh);
CHECK(pre_swap_entry.first.empty());
frame_tree_node_to_weak_global_ref_.erase(pre_swap_ftn_id);
}
// WebContentsToIoThreadClientMap ---------------------------------------------
class WebContentsToIoThreadClientMap {
public:
static WebContentsToIoThreadClientMap* GetInstance() {
static base::NoDestructor<WebContentsToIoThreadClientMap> instance;
return instance.get();
}
void Set(WebContentsKey key, const JavaObjectWeakGlobalRef& client) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::AutoLock lock(map_lock_);
web_contents_to_weak_global_ref_[key] = client;
}
std::optional<JavaObjectWeakGlobalRef> Get(WebContentsKey key) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::AutoLock lock(map_lock_);
auto iterator = web_contents_to_weak_global_ref_.find(key);
if (iterator == web_contents_to_weak_global_ref_.end()) {
return std::nullopt;
} else {
return iterator->second;
}
}
void Erase(WebContentsKey key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::AutoLock lock(map_lock_);
web_contents_to_weak_global_ref_.erase(key);
}
private:
base::Lock map_lock_;
map<WebContentsKey, JavaObjectWeakGlobalRef> web_contents_to_weak_global_ref_;
};
// ClientMapEntryUpdater ------------------------------------------------------
class ClientMapEntryUpdater : public content::WebContentsObserver {
public:
ClientMapEntryUpdater(JNIEnv* env,
WebContents* web_contents,
const jni_zero::JavaRef<jobject>& jdelegate);
void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
void RenderFrameHostChanged(RenderFrameHost* old_rfh,
RenderFrameHost* new_rfh) override;
void WebContentsDestroyed() override;
private:
JavaObjectWeakGlobalRef jdelegate_;
};
ClientMapEntryUpdater::ClientMapEntryUpdater(
JNIEnv* env,
WebContents* web_contents,
const jni_zero::JavaRef<jobject>& jdelegate)
: content::WebContentsObserver(web_contents), jdelegate_(env, jdelegate) {
DCHECK(web_contents);
DCHECK(jdelegate);
if (web_contents->GetPrimaryMainFrame())
RenderFrameCreated(web_contents->GetPrimaryMainFrame());
WebContentsToIoThreadClientMap::GetInstance()->Set(
GetWebContentsKey(*web_contents), jdelegate_);
}
void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost* rfh) {
RfhToIoThreadClientMap::GetInstance()->Set(rfh, jdelegate_);
}
void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) {
RfhToIoThreadClientMap::GetInstance()->Erase(rfh);
}
void ClientMapEntryUpdater::RenderFrameHostChanged(RenderFrameHost* old_rfh,
RenderFrameHost* new_rfh) {
RfhToIoThreadClientMap::GetInstance()->RenderFrameHostChanged(old_rfh,
new_rfh);
}
void ClientMapEntryUpdater::WebContentsDestroyed() {
WebContentsToIoThreadClientMap::GetInstance()->Erase(
GetWebContentsKey(*web_contents()));
delete this;
}
} // namespace
WebContentsKey GetWebContentsKey(content::WebContents& web_contents) {
return safe_browsing::GetWebContentsKey(&web_contents);
}
// AwContentsIoThreadClient -----------------------------------------------
// static
// Wrap an optional |JavaObjectWeakGlobalRef| to a Java
// |AwContentsIoThreadClient| in a native |AwContentsIoThreadClient| by getting
// a scoped local reference. This will return |nullptr| if either the optional
// is empty or the weak reference has already expired.
std::unique_ptr<AwContentsIoThreadClient> WrapOptionalWeakRef(
std::optional<JavaObjectWeakGlobalRef> opt_delegate_weak_ref) {
if (opt_delegate_weak_ref) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> java_delegate = opt_delegate_weak_ref->get(env);
if (java_delegate) {
return std::make_unique<AwContentsIoThreadClient>(java_delegate);
}
}
return nullptr;
}
// static
std::unique_ptr<AwContentsIoThreadClient> AwContentsIoThreadClient::FromToken(
const content::GlobalRenderFrameHostToken& global_frame_token) {
return WrapOptionalWeakRef(
RfhToIoThreadClientMap::GetInstance()->Get(global_frame_token));
}
std::unique_ptr<AwContentsIoThreadClient> AwContentsIoThreadClient::FromID(
content::FrameTreeNodeId frame_tree_node_id) {
return WrapOptionalWeakRef(
RfhToIoThreadClientMap::GetInstance()->Get(frame_tree_node_id));
}
// static
std::unique_ptr<AwContentsIoThreadClient> AwContentsIoThreadClient::FromKey(
WebContentsKey key) {
return WrapOptionalWeakRef(
WebContentsToIoThreadClientMap::GetInstance()->Get(key));
}
// static
void AwContentsIoThreadClient::SubFrameCreated(
int child_id,
const blink::LocalFrameToken& parent_frame_token,
const blink::LocalFrameToken& child_frame_token) {
RfhToIoThreadClientMap* map = RfhToIoThreadClientMap::GetInstance();
std::optional<JavaObjectWeakGlobalRef> opt_delegate_weak_ref = map->Get(
content::GlobalRenderFrameHostToken(child_id, parent_frame_token));
if (opt_delegate_weak_ref) {
map->Set(content::GlobalRenderFrameHostToken(child_id, child_frame_token),
opt_delegate_weak_ref.value());
} else {
// It is possible to not find a mapping for the parent rfh_id if the WebView
// is in the process of being destroyed, and the mapping has already been
// erased.
LOG(WARNING) << "No IoThreadClient associated with parent RenderFrameHost.";
}
}
// static
void AwContentsIoThreadClient::Associate(WebContents* web_contents,
const JavaRef<jobject>& jclient) {
JNIEnv* env = AttachCurrentThread();
// The ClientMapEntryUpdater lifespan is tied to the WebContents.
new ClientMapEntryUpdater(env, web_contents, jclient);
}
AwContentsIoThreadClient::AwContentsIoThreadClient(const JavaRef<jobject>& obj)
: java_object_(obj) {
DCHECK(java_object_);
}
AwContentsIoThreadClient::~AwContentsIoThreadClient() = default;
AwContentsIoThreadClient::CacheMode AwContentsIoThreadClient::GetCacheMode()
const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
return static_cast<AwContentsIoThreadClient::CacheMode>(
Java_AwContentsIoThreadClient_getCacheMode(env, java_object_));
}
using CallbackMap =
std::map<jint,
AwContentsIoThreadClient::ShouldInterceptRequestResponseCallback>;
namespace {
CallbackMap* GetCallbacks() {
static CallbackMap instance;
return &instance;
}
jint GetNextRequestId() {
static jint ctr = 1;
CHECK(ctr < std::numeric_limits<int32_t>::max());
return ctr++;
}
base::Lock* GetLock() {
static base::Lock instance;
return &instance;
}
std::optional<AwContentsIoThreadClient::ShouldInterceptRequestResponseCallback>
LookupAndRemove(jint request_id) {
base::AutoLock lock(*GetLock());
CallbackMap* callbacks = GetCallbacks();
auto callback_iter = callbacks->find(request_id);
if (callback_iter != callbacks->end()) {
AwContentsIoThreadClient::ShouldInterceptRequestResponseCallback callback =
std::move(callback_iter->second);
DCHECK(callback);
callbacks->erase(callback_iter);
return callback;
}
return std::nullopt;
}
jint AddCallback(
AwContentsIoThreadClient::ShouldInterceptRequestResponseCallback callback) {
DCHECK(callback);
base::AutoLock lock(*GetLock());
jint request_id = GetNextRequestId();
GetCallbacks()->insert(std::make_pair(request_id, std::move(callback)));
return request_id;
}
AwContentsIoThreadClient::InterceptResponseData NoInterceptRequest() {
return AwContentsIoThreadClient::InterceptResponseData();
}
void StartShouldInterceptRequest(
AwWebResourceRequest request,
AwContentsIoThreadClient::ShouldInterceptRequestResponseCallback callback,
JavaObjectWeakGlobalRef ref) {
// Historically this method was called `RunShouldInterceptRequest` and so we
// keep this here to preserve trace comparisons across different milestones
// and versions.
TRACE_EVENT0("android_webview", "RunShouldInterceptRequest");
JNIEnv* env = AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> obj = ref.get(env);
if (!obj) {
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), NoInterceptRequest()));
return;
}
jint request_id = AddCallback(std::move(callback));
AwWebResourceRequest::AwJavaWebResourceRequest java_web_resource_request;
AwWebResourceRequest::ConvertToJava(env, request, &java_web_resource_request);
devtools_instrumentation::ScopedEmbedderCallbackTask embedder_callback(
"shouldInterceptRequest");
Java_AwContentsBackgroundThreadClient_shouldInterceptRequestFromNative(
env, obj, java_web_resource_request.jurl, request.is_outermost_main_frame,
request.has_user_gesture, java_web_resource_request.jmethod,
java_web_resource_request.jheader_names,
java_web_resource_request.jheader_values, request_id);
}
} // namespace
// static
// Returns status to indicate whether callback was successfully
// fetched from the map.
jboolean JNI_AwContentsIoThreadClient_FinishShouldInterceptRequest(
JNIEnv*,
jint request_id,
const base::android::JavaParamRef<jobject>& java_ref) {
JNIEnv* env = base::android::AttachCurrentThread();
std::optional<
AwContentsIoThreadClient::ShouldInterceptRequestResponseCallback>
callback = LookupAndRemove(request_id);
if (!callback) {
return false;
}
DCHECK(java_ref)
<< "shouldInterceptRequest from Java should return non-null value";
auto web_resource_intercept_response =
std::make_unique<AwWebResourceInterceptResponse>(java_ref);
bool has_response = web_resource_intercept_response->HasResponse(env);
UMA_HISTOGRAM_BOOLEAN(
"Android.WebView.ShouldInterceptRequest.IsRequestIntercepted",
has_response);
AwContentsIoThreadClient::InterceptResponseData response_data;
// Grab the input stream now as an optimization to avoid thread hopping later.
if (base::FeatureList::IsEnabled(
embedder_support::features::kInputStreamOptimizations) &&
has_response) {
auto response = web_resource_intercept_response->GetResponse(env);
if (response->HasInputStream(env)) {
// Only transfer the input stream if it exists since
// GetInputStream() can only be called once, even for null input
// streams.
response_data.input_stream = response->GetInputStream(env);
}
}
response_data.response = std::move(web_resource_intercept_response);
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(*callback), std::move(response_data)));
return true;
}
AwContentsIoThreadClient::InterceptResponseData::InterceptResponseData() =
default;
AwContentsIoThreadClient::InterceptResponseData::~InterceptResponseData() =
default;
AwContentsIoThreadClient::InterceptResponseData::InterceptResponseData(
InterceptResponseData&& other) = default;
AwContentsIoThreadClient::InterceptResponseData&
AwContentsIoThreadClient::InterceptResponseData::operator=(
InterceptResponseData&& other) = default;
void AwContentsIoThreadClient::ShouldInterceptRequestAsync(
AwWebResourceRequest request,
ShouldInterceptRequestResponseCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
if (!bg_thread_client_object_) {
bg_thread_client_object_.Reset(
Java_AwContentsIoThreadClient_getBackgroundThreadClient(env,
java_object_));
}
if (bg_thread_client_object_) {
sequenced_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&StartShouldInterceptRequest, std::move(request),
std::move(callback),
JavaObjectWeakGlobalRef(env, bg_thread_client_object_)));
} else {
// We are already on the IOThread. Just call the callback directly here.
std::move(callback).Run(NoInterceptRequest());
}
}
bool AwContentsIoThreadClient::ShouldBlockContentUrls() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
return Java_AwContentsIoThreadClient_shouldBlockContentUrls(env,
java_object_);
}
bool AwContentsIoThreadClient::ShouldBlockFileUrls() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
return Java_AwContentsIoThreadClient_shouldBlockFileUrls(env, java_object_);
}
bool AwContentsIoThreadClient::ShouldBlockSpecialFileUrls() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
return Java_AwContentsIoThreadClient_shouldBlockSpecialFileUrls(env,
java_object_);
}
bool AwContentsIoThreadClient::ShouldAcceptCookies() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
return Java_AwContentsIoThreadClient_shouldAcceptCookies(env, java_object_);
}
bool AwContentsIoThreadClient::ShouldAcceptThirdPartyCookies() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
env, java_object_);
}
bool AwContentsIoThreadClient::GetSafeBrowsingEnabled() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
JNIEnv* env = AttachCurrentThread();
return Java_AwContentsIoThreadClient_getSafeBrowsingEnabled(env,
java_object_);
}
bool AwContentsIoThreadClient::ShouldBlockNetworkLoads() const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
JNIEnv* env = AttachCurrentThread();
return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(env,
java_object_);
}
} // namespace android_webview