// 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 #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 "storage/browser/quota/quota_manager.h" #include "storage/common/quota/quota_types.h" #include "url/gurl.h" using base::android::AttachCurrentThread; using content::BrowserThread; using content::StoragePartition; using storage::QuotaClient; using storage::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 { public: GetOriginsTask( const AwQuotaManagerBridgeImpl::GetOriginsCallback& callback, QuotaManager* quota_manager); void Run(); private: friend class base::RefCountedThreadSafe; ~GetOriginsTask(); void OnOriginsObtained(const std::set& origins, storage::StorageType type); void OnUsageAndQuotaObtained(const GURL& origin, storage::QuotaStatusCode status_code, int64_t usage, int64_t quota); void CheckDone(); void DoneOnUIThread(); AwQuotaManagerBridgeImpl::GetOriginsCallback ui_callback_; scoped_refptr quota_manager_; std::vector origin_; std::vector usage_; std::vector 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_CURRENTLY_ON(BrowserThread::UI); } GetOriginsTask::~GetOriginsTask() {} void GetOriginsTask::Run() { DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&QuotaManager::GetOriginsModifiedSince, quota_manager_, storage::kStorageTypeTemporary, base::Time() /* Since beginning of time. */, base::Bind(&GetOriginsTask::OnOriginsObtained, this))); } void GetOriginsTask::OnOriginsObtained(const std::set& origins, storage::StorageType type) { DCHECK_CURRENTLY_ON(BrowserThread::IO); num_callbacks_to_wait_ = origins.size(); num_callbacks_received_ = 0u; for (std::set::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, storage::QuotaStatusCode status_code, int64_t usage, int64_t quota) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (status_code == storage::kQuotaStatusOk) { origin_.push_back(origin.spec()); usage_.push_back(usage); quota_.push_back(quota); } ++num_callbacks_received_; CheckDone(); } void GetOriginsTask::CheckDone() { DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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, const JavaParamRef& clazz) { AwBrowserContext* browser_context = AwContentBrowserClient::GetAwBrowserContext(); AwQuotaManagerBridgeImpl* bridge = static_cast( browser_context->GetQuotaManagerBridge()); DCHECK(bridge); return reinterpret_cast(bridge); } // static scoped_refptr AwQuotaManagerBridgeImpl::Create( AwBrowserContext* browser_context) { return new AwQuotaManagerBridgeImpl(browser_context); } AwQuotaManagerBridgeImpl::AwQuotaManagerBridgeImpl( AwBrowserContext* browser_context) : browser_context_(browser_context), weak_factory_(this) { } AwQuotaManagerBridgeImpl::~AwQuotaManagerBridgeImpl() {} void AwQuotaManagerBridgeImpl::Init(JNIEnv* env, const JavaParamRef& object) { java_ref_ = JavaObjectWeakGlobalRef(env, object); } StoragePartition* AwQuotaManagerBridgeImpl::GetStoragePartition() const { DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(BrowserThread::UI); QuotaManager* quota_manager = GetStoragePartition()->GetQuotaManager(); DCHECK(quota_manager); return quota_manager; } void AwQuotaManagerBridgeImpl::DeleteAllData( JNIEnv* env, const JavaParamRef& object) { RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::DeleteAllDataOnUiThread, this)); } void AwQuotaManagerBridgeImpl::DeleteAllDataOnUiThread() { DCHECK_CURRENTLY_ON(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, const JavaParamRef& object, const JavaParamRef& 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_CURRENTLY_ON(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(), base::Bind(&base::DoNothing)); } void AwQuotaManagerBridgeImpl::GetOrigins(JNIEnv* env, const JavaParamRef& object, jint callback_id) { RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::GetOriginsOnUiThread, this, callback_id)); } void AwQuotaManagerBridgeImpl::GetOriginsOnUiThread(jint callback_id) { DCHECK_CURRENTLY_ON(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& origin, const std::vector& usage, const std::vector& quota) { DCHECK_CURRENTLY_ON(BrowserThread::UI); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef 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, storage::QuotaStatusCode status_code, int64_t usage, int64_t quota) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (status_code != storage::kQuotaStatusOk) { usage = 0; quota = 0; } BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(ui_callback, usage, quota)); } } // namespace void AwQuotaManagerBridgeImpl::GetUsageAndQuotaForOrigin( JNIEnv* env, const JavaParamRef& object, const JavaParamRef& 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_CURRENTLY_ON(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), storage::kStorageTypeTemporary, base::Bind(&OnUsageAndQuotaObtained, ui_callback))); } void AwQuotaManagerBridgeImpl::QuotaUsageCallbackImpl(int jcallback_id, bool is_quota, int64_t usage, int64_t quota) { DCHECK_CURRENTLY_ON(BrowserThread::UI); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef 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); } } // namespace android_webview