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..5a898997b19b5db8e2dee0fefd15d84774d2a904 |
--- /dev/null |
+++ b/components/web_restrictions/content_resolver_web_restrictions_provider.cc |
@@ -0,0 +1,290 @@ |
+// 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::TaskRunner>& callback_runner, |
+ ContentResolverWebRestrictionsProvider* provider); |
+ void RequestSuccess(bool request_success); |
+ void ShouldProceed(bool should_proceed, |
+ const base::android::JavaRef<jstring>& 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::TaskRunner> callback_runner_; |
+ ContentResolverWebRestrictionsProvider* provider_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SelfDeletingCallback); |
+}; |
+ |
+SelfDeletingCallback::SelfDeletingCallback( |
+ const GURL& url, |
+ const base::Callback<void(bool)>& callback, |
+ const scoped_refptr<base::TaskRunner>& callback_runner, |
Bernhard Bauer
2016/02/03 16:15:42
If the contract of ContentResolverWebRestrictionsP
knn
2016/02/03 17:15:09
Done.
|
+ ContentResolverWebRestrictionsProvider* provider) |
+ : url_(url), |
+ callback_(callback), |
+ callback_runner_(callback_runner), |
+ provider_(provider) {} |
+ |
+void SelfDeletingCallback::ShouldProceed( |
+ bool should_proceed, |
+ const base::android::JavaRef<jstring>& j_error_page) { |
+ std::string* error_page = nullptr; |
+ if (!j_error_page.is_null()) { |
+ // Will be owned by the callback to which it is passed. |
+ error_page = new std::string(); |
+ *error_page = base::android::ConvertJavaStringToUTF8(j_error_page); |
+ } |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&ContentResolverWebRestrictionsProvider::UpdateCache, |
+ base::Unretained(provider_), url_, should_proceed, |
aberent
2016/02/03 16:22:07
Is |provider_| still guaranteed to still exist? Do
knn
2016/02/03 16:26:07
Yes, it is like a singleton should only be reset w
aberent
2016/02/03 17:56:15
I am confused. It is the lifetime of the C++ Conte
knn
2016/02/03 18:51:13
Yes, what you are saying is correct there are no *
|
+ base::Owned(error_page))); |
Bernhard Bauer
2016/02/03 16:15:42
Could we wrap this in a scoped_ptr to make sure no
knn
2016/02/03 17:15:08
Passing by value now.
|
+ callback_runner_->PostTask(FROM_HERE, base::Bind(callback_, should_proceed)); |
+ delete this; |
+} |
+ |
+void SelfDeletingCallback::RequestSuccess(bool request_success) { |
+ callback_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) { |
+ 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()); |
+} |
+ |
+void ContentResolverWebRestrictionsProvider::SetAuthority( |
+ const std::string& content_provider_authority) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
+ // 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(); |
+ |
+ // 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))); |
+} |
+ |
+UrlAccess ContentResolverWebRestrictionsProvider::ShouldProceed( |
+ bool is_main_frame, |
+ const GURL& url, |
+ const base::Callback<void(bool)>& callback) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
+ if (!initialized_) |
+ return ALLOW; |
+ auto iter = url_access_cache_.find(url); |
+ if (iter != url_access_cache_.end()) { |
+ RecordURLAccess(url); |
+ return iter->second ? ALLOW : DISALLOW; |
+ } |
+ scoped_refptr<base::SingleThreadTaskRunner> callback_runner = |
+ base::ThreadTaskRunnerHandle::Get(); |
+ SelfDeletingCallback* wrapped_callback = |
+ new SelfDeletingCallback(url, callback, callback_runner, 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_CURRENTLY_ON(content::BrowserThread::IO); |
+ 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; |
+ } |
+ scoped_refptr<base::SingleThreadTaskRunner> callback_runner = |
+ base::ThreadTaskRunnerHandle::Get(); |
+ SelfDeletingCallback* wrapped_callback = |
+ new SelfDeletingCallback(url, request_success, callback_runner, this); |
+ background_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&AsyncRequestPermission, url, |
+ reinterpret_cast<jlong>(wrapped_callback), java_provider_)); |
+} |
+ |
+void ContentResolverWebRestrictionsProvider::ClearCache() { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
+ error_page_cache_.clear(); |
+ url_access_cache_.clear(); |
+ recent_urls_.clear(); |
+} |
+ |
+void ContentResolverWebRestrictionsProvider::RecordURLAccess(const GURL& url) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
+ // Move the URL to the front of the cache. |
+ recent_urls_.remove(url); |
+ recent_urls_.push_front(url); |
+} |
+ |
+void ContentResolverWebRestrictionsProvider::UpdateCache( |
+ const GURL& url, |
+ bool should_proceed, |
+ std::string* error_page /* owned by the callback */) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
+ 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) { |
+ error_page_cache_[url] = *error_page; |
Bernhard Bauer
2016/02/03 16:15:42
Hm... We pass this as a pointer to this method to
knn
2016/02/03 16:23:56
I am using pointers mostly to indicate missing val
Bernhard Bauer
2016/02/03 16:26:49
Yeah, I don't think anyone would legitimately want
knn
2016/02/03 17:15:09
Acknowledged.
|
+ } else { |
+ error_page_cache_.erase(url); |
+ } |
+} |
+ |
+void ContentResolverWebRestrictionsProvider::RequestSupportKnown( |
+ bool supports_request) { |
+ // |supports_request_| is initialized to false. |
+ DCHECK(!supports_request_); |
+ supports_request_ = supports_request; |
+} |
+ |
+void ShouldProceed(JNIEnv* env, |
+ const base::android::JavaParamRef<jclass>& clazz, |
+ jlong callback_ptr, |
+ jboolean should_proceed, |
+ const base::android::JavaParamRef<jstring>& error_page) { |
+ SelfDeletingCallback* callback = |
+ reinterpret_cast<SelfDeletingCallback*>(callback_ptr); |
+ 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. |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&ContentResolverWebRestrictionsProvider::ClearCache, |
+ base::Unretained(provider))); |
aberent
2016/02/03 16:22:07
As above; do we know that |provider| hasn't been d
|
+} |
+ |
+} // namespace web_restrictions |