| Index: components/web_restrictions/content_resolver_web_restrictions_provider.cc
|
| diff --git a/components/web_restrictions/content_resolver_web_restrictions_provider.cc b/components/web_restrictions/content_resolver_web_restrictions_provider.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8ebb2928fa605b75fd31a78d704cc41b1624d72f
|
| --- /dev/null
|
| +++ b/components/web_restrictions/content_resolver_web_restrictions_provider.cc
|
| @@ -0,0 +1,304 @@
|
| +// Copyright 2015 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 "components/web_restrictions/content_resolver_web_restrictions_provider.h"
|
| +
|
| +#include "base/android/jni_string.h"
|
| +#include "base/android/scoped_java_ref.h"
|
| +#include "base/bind.h"
|
| +#include "base/location.h"
|
| +#include "base/thread_task_runner_handle.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "jni/ContentResolverWebRestrictionsProvider_jni.h"
|
| +
|
| +namespace web_restrictions {
|
| +
|
| +namespace {
|
| +
|
| +const size_t kMaxCacheSize = 100;
|
| +
|
| +void AsyncShouldProceed(const GURL& url,
|
| + jlong callback,
|
| + const base::android::JavaRef<jobject>& java_provider) {
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + Java_ContentResolverWebRestrictionsProvider_shouldProceed(
|
| + env, java_provider.obj(), callback,
|
| + base::android::ConvertUTF8ToJavaString(env, url.spec()).obj());
|
| +}
|
| +
|
| +void AsyncRequestPermission(
|
| + const GURL& url,
|
| + jlong callback,
|
| + const base::android::JavaRef<jobject>& java_provider) {
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + Java_ContentResolverWebRestrictionsProvider_requestPermission(
|
| + env, java_provider.obj(), callback,
|
| + base::android::ConvertUTF8ToJavaString(env, url.spec()).obj());
|
| +}
|
| +
|
| +bool AsyncCheckSupportsRequest(
|
| + const base::android::JavaRef<jobject>& java_provider) {
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + return Java_ContentResolverWebRestrictionsProvider_supportsRequest(
|
| + env, java_provider.obj());
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// A wrapper to the callback class to facilitate getting a callback from java
|
| +// into C++. Objects of this class delete itself only when they are called back
|
| +// so we must ensure that happens even for error cases.
|
| +class SelfDeletingCallback {
|
| + public:
|
| + SelfDeletingCallback(
|
| + const GURL& url,
|
| + const base::Callback<void(bool)>& callback,
|
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
|
| + const std::string& provider_authority,
|
| + ContentResolverWebRestrictionsProvider* provider);
|
| + void RequestSuccess(bool request_success);
|
| + void ShouldProceed(bool should_proceed, std::string error_page);
|
| +
|
| + private:
|
| + // Only the callback can delete itself. We must ensure it is indeed
|
| + // called back.
|
| + ~SelfDeletingCallback() {}
|
| +
|
| + GURL url_;
|
| + base::Callback<void(bool)> callback_;
|
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
| + std::string provider_authority_;
|
| + ContentResolverWebRestrictionsProvider* provider_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SelfDeletingCallback);
|
| +};
|
| +
|
| +SelfDeletingCallback::SelfDeletingCallback(
|
| + const GURL& url,
|
| + const base::Callback<void(bool)>& callback,
|
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
|
| + const std::string& provider_authority,
|
| + ContentResolverWebRestrictionsProvider* provider)
|
| + : url_(url),
|
| + callback_(callback),
|
| + task_runner_(task_runner),
|
| + provider_authority_(provider_authority),
|
| + provider_(provider) {}
|
| +
|
| +void SelfDeletingCallback::ShouldProceed(bool should_proceed,
|
| + std::string error_page) {
|
| + task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ContentResolverWebRestrictionsProvider::UpdateCache,
|
| + base::Unretained(provider_), provider_authority_, url_,
|
| + should_proceed, error_page));
|
| + task_runner_->PostTask(FROM_HERE, base::Bind(callback_, should_proceed));
|
| + delete this;
|
| +}
|
| +
|
| +void SelfDeletingCallback::RequestSuccess(bool request_success) {
|
| + task_runner_->PostTask(FROM_HERE, base::Bind(callback_, request_success));
|
| + delete this;
|
| +}
|
| +
|
| +// static
|
| +bool ContentResolverWebRestrictionsProvider::Register(JNIEnv* env) {
|
| + return RegisterNativesImpl(env);
|
| +}
|
| +
|
| +ContentResolverWebRestrictionsProvider::ContentResolverWebRestrictionsProvider()
|
| + : initialized_(false), supports_request_(false) {
|
| + single_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
|
| + base::SequencedWorkerPool* worker_pool =
|
| + content::BrowserThread::GetBlockingPool();
|
| + background_task_runner_ =
|
| + worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
|
| + worker_pool->GetSequenceToken(),
|
| + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
|
| +}
|
| +
|
| +ContentResolverWebRestrictionsProvider::
|
| + ~ContentResolverWebRestrictionsProvider() {
|
| + if (java_provider_.is_null())
|
| + return;
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + Java_ContentResolverWebRestrictionsProvider_onDestroy(env,
|
| + java_provider_.obj());
|
| + java_provider_.Reset();
|
| +}
|
| +
|
| +void ContentResolverWebRestrictionsProvider::SetAuthority(
|
| + const std::string& content_provider_authority) {
|
| + DCHECK(single_thread_task_runner_->BelongsToCurrentThread());
|
| + // Destroy any existing content resolver.
|
| + JNIEnv* env = base::android::AttachCurrentThread();
|
| + if (!java_provider_.is_null()) {
|
| + Java_ContentResolverWebRestrictionsProvider_onDestroy(env,
|
| + java_provider_.obj());
|
| + java_provider_.Reset();
|
| + }
|
| + ClearCache();
|
| + provider_authority_ = content_provider_authority;
|
| +
|
| + // Initialize the content resolver.
|
| + initialized_ = !content_provider_authority.empty();
|
| + if (!initialized_)
|
| + return;
|
| + java_provider_.Reset(Java_ContentResolverWebRestrictionsProvider_create(
|
| + env,
|
| + base::android::ConvertUTF8ToJavaString(env, content_provider_authority)
|
| + .obj(),
|
| + reinterpret_cast<jlong>(this)));
|
| + supports_request_ = false;
|
| + base::PostTaskAndReplyWithResult(
|
| + content::BrowserThread::GetBlockingPool(), FROM_HERE,
|
| + base::Bind(&AsyncCheckSupportsRequest, java_provider_),
|
| + base::Bind(&ContentResolverWebRestrictionsProvider::RequestSupportKnown,
|
| + base::Unretained(this), provider_authority_));
|
| +}
|
| +
|
| +UrlAccess ContentResolverWebRestrictionsProvider::ShouldProceed(
|
| + bool is_main_frame,
|
| + const GURL& url,
|
| + const base::Callback<void(bool)>& callback) {
|
| + DCHECK(single_thread_task_runner_->BelongsToCurrentThread());
|
| + if (!initialized_)
|
| + return ALLOW;
|
| + auto iter = url_access_cache_.find(url);
|
| + if (iter != url_access_cache_.end()) {
|
| + RecordURLAccess(url);
|
| + return iter->second ? ALLOW : DISALLOW;
|
| + }
|
| + SelfDeletingCallback* wrapped_callback = new SelfDeletingCallback(
|
| + url, callback, single_thread_task_runner_, provider_authority_, this);
|
| + background_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&AsyncShouldProceed, url,
|
| + reinterpret_cast<jlong>(wrapped_callback), java_provider_));
|
| + return PENDING;
|
| +}
|
| +
|
| +bool ContentResolverWebRestrictionsProvider::SupportsRequest() const {
|
| + return initialized_ && supports_request_;
|
| +}
|
| +
|
| +bool ContentResolverWebRestrictionsProvider::GetErrorHtml(
|
| + const GURL& url,
|
| + std::string* error_page) const {
|
| + DCHECK(single_thread_task_runner_->BelongsToCurrentThread());
|
| + if (!initialized_)
|
| + return false;
|
| + auto iter = error_page_cache_.find(url);
|
| + if (iter == error_page_cache_.end())
|
| + return false;
|
| + *error_page = iter->second;
|
| + return true;
|
| +}
|
| +
|
| +void ContentResolverWebRestrictionsProvider::RequestPermission(
|
| + const GURL& url,
|
| + const base::Callback<void(bool)>& request_success) {
|
| + if (!initialized_) {
|
| + request_success.Run(false);
|
| + return;
|
| + }
|
| + SelfDeletingCallback* wrapped_callback =
|
| + new SelfDeletingCallback(url, request_success, single_thread_task_runner_,
|
| + provider_authority_, this);
|
| + background_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&AsyncRequestPermission, url,
|
| + reinterpret_cast<jlong>(wrapped_callback), java_provider_));
|
| +}
|
| +
|
| +void ContentResolverWebRestrictionsProvider::OnWebRestrictionsChanged() {
|
| + single_thread_task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&ContentResolverWebRestrictionsProvider::ClearCache,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void ContentResolverWebRestrictionsProvider::RecordURLAccess(const GURL& url) {
|
| + DCHECK(single_thread_task_runner_->BelongsToCurrentThread());
|
| + // Move the URL to the front of the cache.
|
| + recent_urls_.remove(url);
|
| + recent_urls_.push_front(url);
|
| +}
|
| +
|
| +void ContentResolverWebRestrictionsProvider::UpdateCache(
|
| + std::string provider_authority,
|
| + GURL url,
|
| + bool should_proceed,
|
| + std::string error_page) {
|
| + DCHECK(single_thread_task_runner_->BelongsToCurrentThread());
|
| + // If the webrestrictions provider changed when the old one was being queried,
|
| + // do not update the cache for the new provider.
|
| + if (provider_authority != provider_authority_)
|
| + return;
|
| + RecordURLAccess(url);
|
| + if (recent_urls_.size() >= kMaxCacheSize) {
|
| + url_access_cache_.erase(recent_urls_.back());
|
| + error_page_cache_.erase(recent_urls_.back());
|
| + recent_urls_.pop_back();
|
| + }
|
| + url_access_cache_[url] = should_proceed;
|
| + if (!error_page.empty()) {
|
| + error_page_cache_[url] = error_page;
|
| + } else {
|
| + error_page_cache_.erase(url);
|
| + }
|
| +}
|
| +
|
| +void ContentResolverWebRestrictionsProvider::RequestSupportKnown(
|
| + std::string provider_authority,
|
| + bool supports_request) {
|
| + // |supports_request_| is initialized to false.
|
| + DCHECK(!supports_request_);
|
| + DCHECK(single_thread_task_runner_->BelongsToCurrentThread());
|
| + // If the webrestrictions provider changed when the old one was being queried,
|
| + // ignore the result.
|
| + if (provider_authority != provider_authority_)
|
| + return;
|
| + supports_request_ = supports_request;
|
| +}
|
| +
|
| +void ContentResolverWebRestrictionsProvider::ClearCache() {
|
| + DCHECK(single_thread_task_runner_->BelongsToCurrentThread());
|
| + error_page_cache_.clear();
|
| + url_access_cache_.clear();
|
| + recent_urls_.clear();
|
| +}
|
| +
|
| +void ShouldProceed(JNIEnv* env,
|
| + const base::android::JavaParamRef<jclass>& clazz,
|
| + jlong callback_ptr,
|
| + jboolean should_proceed,
|
| + const base::android::JavaParamRef<jstring>& j_error_page) {
|
| + SelfDeletingCallback* callback =
|
| + reinterpret_cast<SelfDeletingCallback*>(callback_ptr);
|
| + std::string error_page;
|
| + if (!j_error_page.is_null())
|
| + error_page = base::android::ConvertJavaStringToUTF8(j_error_page);
|
| + callback->ShouldProceed(should_proceed, error_page);
|
| +}
|
| +
|
| +void RequestSuccess(JNIEnv* env,
|
| + const base::android::JavaParamRef<jclass>& clazz,
|
| + jlong callback_ptr,
|
| + jboolean request_success) {
|
| + SelfDeletingCallback* callback =
|
| + reinterpret_cast<SelfDeletingCallback*>(callback_ptr);
|
| + callback->RequestSuccess(request_success);
|
| +}
|
| +
|
| +void NotifyWebRestrictionsChanged(
|
| + JNIEnv* env,
|
| + const base::android::JavaParamRef<jclass>& clazz,
|
| + jlong provider_ptr) {
|
| + ContentResolverWebRestrictionsProvider* provider =
|
| + reinterpret_cast<ContentResolverWebRestrictionsProvider*>(provider_ptr);
|
| + // TODO(knn): Also reload existing interstitials/error pages.
|
| + provider->OnWebRestrictionsChanged();
|
| +}
|
| +
|
| +} // namespace web_restrictions
|
|
|