| // 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 "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<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, | 
 |                          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<QuotaManager> quota_manager_; | 
 |  | 
 |   std::vector<std::string> origin_; | 
 |   std::vector<int64_t> usage_; | 
 |   std::vector<int64_t> 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<GURL>& origins, | 
 |                                        storage::StorageType type) { | 
 |   DCHECK_CURRENTLY_ON(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, | 
 |     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<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) | 
 |     : browser_context_(browser_context), | 
 |       weak_factory_(this) { | 
 | } | 
 |  | 
 | AwQuotaManagerBridgeImpl::~AwQuotaManagerBridgeImpl() {} | 
 |  | 
 | void AwQuotaManagerBridgeImpl::Init(JNIEnv* env, | 
 |                                     const JavaParamRef<jobject>& 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<jobject>& 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<jobject>& object, | 
 |     const JavaParamRef<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_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<jobject>& 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<std::string>& origin, | 
 |     const std::vector<int64_t>& usage, | 
 |     const std::vector<int64_t>& quota) { | 
 |   DCHECK_CURRENTLY_ON(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, | 
 |     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<jobject>& object, | 
 |     const JavaParamRef<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_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<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); | 
 | } | 
 |  | 
 | }  // namespace android_webview |