// Copyright (c) 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 "android_webview/native/aw_quota_manager_bridge_impl.h" #include <set> #include "android_webview/browser/aw_browser_context.h" #include "android_webview/browser/aw_content_browser_client.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/synchronization/waitable_event.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_client.h" #include "jni/AwQuotaManagerBridge_jni.h" #include "url/gurl.h" #include "webkit/browser/quota/quota_manager.h" #include "webkit/common/quota/quota_types.h" using base::android::AttachCurrentThread; using content::BrowserThread; using content::StoragePartition; using quota::QuotaClient; using quota::QuotaManager; namespace android_webview { namespace { // This object lives on UI and IO threads. Care need to be taken to make sure // there are no concurrent accesses to instance variables. Also this object // is refcounted in the various callbacks, and is destroyed when all callbacks // are destroyed at the end of DoneOnUIThread. class GetOriginsTask : public base::RefCountedThreadSafe<GetOriginsTask> { public: GetOriginsTask( const AwQuotaManagerBridgeImpl::GetOriginsCallback& callback, QuotaManager* quota_manager); void Run(); private: friend class base::RefCountedThreadSafe<GetOriginsTask>; ~GetOriginsTask(); void OnOriginsObtained(const std::set<GURL>& origins, quota::StorageType type); void OnUsageAndQuotaObtained(const GURL& origin, quota::QuotaStatusCode status_code, int64 usage, int64 quota); void CheckDone(); void DoneOnUIThread(); AwQuotaManagerBridgeImpl::GetOriginsCallback ui_callback_; scoped_refptr<QuotaManager> quota_manager_; std::vector<std::string> origin_; std::vector<int64> usage_; std::vector<int64> quota_; size_t num_callbacks_to_wait_; size_t num_callbacks_received_; DISALLOW_COPY_AND_ASSIGN(GetOriginsTask); }; GetOriginsTask::GetOriginsTask( const AwQuotaManagerBridgeImpl::GetOriginsCallback& callback, QuotaManager* quota_manager) : ui_callback_(callback), quota_manager_(quota_manager) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } GetOriginsTask::~GetOriginsTask() {} void GetOriginsTask::Run() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&QuotaManager::GetOriginsModifiedSince, quota_manager_, quota::kStorageTypeTemporary, base::Time() /* Since beginning of time. */, base::Bind(&GetOriginsTask::OnOriginsObtained, this))); } void GetOriginsTask::OnOriginsObtained( const std::set<GURL>& origins, quota::StorageType type) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); num_callbacks_to_wait_ = origins.size(); num_callbacks_received_ = 0u; for (std::set<GURL>::const_iterator origin = origins.begin(); origin != origins.end(); ++origin) { quota_manager_->GetUsageAndQuota( *origin, type, base::Bind(&GetOriginsTask::OnUsageAndQuotaObtained, this, *origin)); } CheckDone(); } void GetOriginsTask::OnUsageAndQuotaObtained(const GURL& origin, quota::QuotaStatusCode status_code, int64 usage, int64 quota) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (status_code == quota::kQuotaStatusOk) { origin_.push_back(origin.spec()); usage_.push_back(usage); quota_.push_back(quota); } ++num_callbacks_received_; CheckDone(); } void GetOriginsTask::CheckDone() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (num_callbacks_received_ == num_callbacks_to_wait_) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&GetOriginsTask::DoneOnUIThread, this)); } else if (num_callbacks_received_ > num_callbacks_to_wait_) { NOTREACHED(); } } // This method is to avoid copying the 3 vector arguments into a bound callback. void GetOriginsTask::DoneOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ui_callback_.Run(origin_, usage_, quota_); } void RunOnUIThread(const base::Closure& task) { if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { task.Run(); } else { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task); } } } // namespace // static jlong GetDefaultNativeAwQuotaManagerBridge(JNIEnv* env, jclass clazz) { AwBrowserContext* browser_context = AwContentBrowserClient::GetAwBrowserContext(); AwQuotaManagerBridgeImpl* bridge = static_cast<AwQuotaManagerBridgeImpl*>( browser_context->GetQuotaManagerBridge()); DCHECK(bridge); return reinterpret_cast<intptr_t>(bridge); } // static scoped_refptr<AwQuotaManagerBridge> AwQuotaManagerBridgeImpl::Create( AwBrowserContext* browser_context) { return new AwQuotaManagerBridgeImpl(browser_context); } AwQuotaManagerBridgeImpl::AwQuotaManagerBridgeImpl( AwBrowserContext* browser_context) : weak_factory_(this), browser_context_(browser_context) { } AwQuotaManagerBridgeImpl::~AwQuotaManagerBridgeImpl() {} void AwQuotaManagerBridgeImpl::Init(JNIEnv* env, jobject object) { java_ref_ = JavaObjectWeakGlobalRef(env, object); } StoragePartition* AwQuotaManagerBridgeImpl::GetStoragePartition() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // AndroidWebview does not use per-site storage partitions. StoragePartition* storage_partition = content::BrowserContext::GetDefaultStoragePartition(browser_context_); DCHECK(storage_partition); return storage_partition; } QuotaManager* AwQuotaManagerBridgeImpl::GetQuotaManager() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); QuotaManager* quota_manager = GetStoragePartition()->GetQuotaManager(); DCHECK(quota_manager); return quota_manager; } void AwQuotaManagerBridgeImpl::DeleteAllData(JNIEnv* env, jobject object) { RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::DeleteAllDataOnUiThread, this)); } void AwQuotaManagerBridgeImpl::DeleteAllDataOnUiThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); GetStoragePartition()->ClearData( // Clear all web storage data except cookies. StoragePartition::REMOVE_DATA_MASK_APPCACHE | StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS | StoragePartition::REMOVE_DATA_MASK_INDEXEDDB | StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE | StoragePartition::REMOVE_DATA_MASK_WEBSQL, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY, GURL(), StoragePartition::OriginMatcherFunction(), base::Time(), base::Time::Max(), base::Bind(&base::DoNothing)); } void AwQuotaManagerBridgeImpl::DeleteOrigin( JNIEnv* env, jobject object, jstring origin) { base::string16 origin_string( base::android::ConvertJavaStringToUTF16(env, origin)); RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::DeleteOriginOnUiThread, this, origin_string)); } void AwQuotaManagerBridgeImpl::DeleteOriginOnUiThread( const base::string16& origin) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); StoragePartition* storage_partition = GetStoragePartition(); storage_partition->ClearDataForOrigin( // All (temporary) QuotaClient types. StoragePartition::REMOVE_DATA_MASK_APPCACHE | StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS | StoragePartition::REMOVE_DATA_MASK_INDEXEDDB | StoragePartition::REMOVE_DATA_MASK_WEBSQL, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY, GURL(origin), storage_partition->GetURLRequestContext()); } void AwQuotaManagerBridgeImpl::GetOrigins( JNIEnv* env, jobject object, jint callback_id) { RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::GetOriginsOnUiThread, this, callback_id)); } void AwQuotaManagerBridgeImpl::GetOriginsOnUiThread(jint callback_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); const GetOriginsCallback ui_callback = base::Bind( &AwQuotaManagerBridgeImpl::GetOriginsCallbackImpl, weak_factory_.GetWeakPtr(), callback_id); (new GetOriginsTask(ui_callback, GetQuotaManager()))->Run(); } void AwQuotaManagerBridgeImpl::GetOriginsCallbackImpl( int jcallback_id, const std::vector<std::string>& origin, const std::vector<int64>& usage, const std::vector<int64>& quota) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) return; Java_AwQuotaManagerBridge_onGetOriginsCallback( env, obj.obj(), jcallback_id, base::android::ToJavaArrayOfStrings(env, origin).obj(), base::android::ToJavaLongArray(env, usage).obj(), base::android::ToJavaLongArray(env, quota).obj()); } namespace { void OnUsageAndQuotaObtained( const AwQuotaManagerBridgeImpl::QuotaUsageCallback& ui_callback, quota::QuotaStatusCode status_code, int64 usage, int64 quota) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (status_code != quota::kQuotaStatusOk) { usage = 0; quota = 0; } BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(ui_callback, usage, quota)); } } // namespace void AwQuotaManagerBridgeImpl::GetUsageAndQuotaForOrigin( JNIEnv* env, jobject object, jstring origin, jint callback_id, bool is_quota) { base::string16 origin_string( base::android::ConvertJavaStringToUTF16(env, origin)); RunOnUIThread(base::Bind( &AwQuotaManagerBridgeImpl::GetUsageAndQuotaForOriginOnUiThread, this, origin_string, callback_id, is_quota)); } void AwQuotaManagerBridgeImpl::GetUsageAndQuotaForOriginOnUiThread( const base::string16& origin, jint callback_id, bool is_quota) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); const QuotaUsageCallback ui_callback = base::Bind( &AwQuotaManagerBridgeImpl::QuotaUsageCallbackImpl, weak_factory_.GetWeakPtr(), callback_id, is_quota); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&QuotaManager::GetUsageAndQuota, GetQuotaManager(), GURL(origin), quota::kStorageTypeTemporary, base::Bind(&OnUsageAndQuotaObtained, ui_callback))); } void AwQuotaManagerBridgeImpl::QuotaUsageCallbackImpl( int jcallback_id, bool is_quota, int64 usage, int64 quota) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) return; Java_AwQuotaManagerBridge_onGetUsageAndQuotaForOriginCallback( env, obj.obj(), jcallback_id, is_quota, usage, quota); } bool RegisterAwQuotaManagerBridge(JNIEnv* env) { return RegisterNativesImpl(env) >= 0; } } // namespace android_webview