1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "android_webview/native/aw_quota_manager_bridge_impl.h"
6
7#include <set>
8
9#include "android_webview/browser/aw_browser_context.h"
10#include "android_webview/browser/aw_content_browser_client.h"
11#include "base/android/jni_array.h"
12#include "base/android/jni_string.h"
13#include "base/synchronization/waitable_event.h"
14#include "content/public/browser/browser_thread.h"
15#include "content/public/browser/storage_partition.h"
16#include "content/public/common/content_client.h"
17#include "jni/AwQuotaManagerBridge_jni.h"
18#include "storage/browser/quota/quota_manager.h"
19#include "storage/common/quota/quota_types.h"
20#include "url/gurl.h"
21
22using base::android::AttachCurrentThread;
23using content::BrowserThread;
24using content::StoragePartition;
25using storage::QuotaClient;
26using storage::QuotaManager;
27
28namespace android_webview {
29
30namespace {
31
32// This object lives on UI and IO threads. Care need to be taken to make sure
33// there are no concurrent accesses to instance variables. Also this object
34// is refcounted in the various callbacks, and is destroyed when all callbacks
35// are destroyed at the end of DoneOnUIThread.
36class GetOriginsTask : public base::RefCountedThreadSafe<GetOriginsTask> {
37 public:
38  GetOriginsTask(
39      const AwQuotaManagerBridgeImpl::GetOriginsCallback& callback,
40      QuotaManager* quota_manager);
41
42  void Run();
43
44 private:
45  friend class base::RefCountedThreadSafe<GetOriginsTask>;
46  ~GetOriginsTask();
47
48  void OnOriginsObtained(const std::set<GURL>& origins,
49                         storage::StorageType type);
50
51  void OnUsageAndQuotaObtained(const GURL& origin,
52                               storage::QuotaStatusCode status_code,
53                               int64 usage,
54                               int64 quota);
55
56  void CheckDone();
57  void DoneOnUIThread();
58
59  AwQuotaManagerBridgeImpl::GetOriginsCallback ui_callback_;
60  scoped_refptr<QuotaManager> quota_manager_;
61
62  std::vector<std::string> origin_;
63  std::vector<int64> usage_;
64  std::vector<int64> quota_;
65
66  size_t num_callbacks_to_wait_;
67  size_t num_callbacks_received_;
68
69  DISALLOW_COPY_AND_ASSIGN(GetOriginsTask);
70};
71
72GetOriginsTask::GetOriginsTask(
73    const AwQuotaManagerBridgeImpl::GetOriginsCallback& callback,
74    QuotaManager* quota_manager)
75    : ui_callback_(callback),
76      quota_manager_(quota_manager) {
77  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
78}
79
80GetOriginsTask::~GetOriginsTask() {}
81
82void GetOriginsTask::Run() {
83  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
84  BrowserThread::PostTask(
85      BrowserThread::IO,
86      FROM_HERE,
87      base::Bind(&QuotaManager::GetOriginsModifiedSince,
88                 quota_manager_,
89                 storage::kStorageTypeTemporary,
90                 base::Time() /* Since beginning of time. */,
91                 base::Bind(&GetOriginsTask::OnOriginsObtained, this)));
92}
93
94void GetOriginsTask::OnOriginsObtained(const std::set<GURL>& origins,
95                                       storage::StorageType type) {
96  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
97  num_callbacks_to_wait_ = origins.size();
98  num_callbacks_received_ = 0u;
99
100  for (std::set<GURL>::const_iterator origin = origins.begin();
101       origin != origins.end();
102       ++origin) {
103    quota_manager_->GetUsageAndQuota(
104        *origin,
105        type,
106        base::Bind(&GetOriginsTask::OnUsageAndQuotaObtained, this, *origin));
107  }
108
109  CheckDone();
110}
111
112void GetOriginsTask::OnUsageAndQuotaObtained(
113    const GURL& origin,
114    storage::QuotaStatusCode status_code,
115    int64 usage,
116    int64 quota) {
117  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
118  if (status_code == storage::kQuotaStatusOk) {
119    origin_.push_back(origin.spec());
120    usage_.push_back(usage);
121    quota_.push_back(quota);
122  }
123
124  ++num_callbacks_received_;
125  CheckDone();
126}
127
128void GetOriginsTask::CheckDone() {
129  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
130  if (num_callbacks_received_ == num_callbacks_to_wait_) {
131    BrowserThread::PostTask(
132        BrowserThread::UI,
133        FROM_HERE,
134        base::Bind(&GetOriginsTask::DoneOnUIThread, this));
135  } else if (num_callbacks_received_ > num_callbacks_to_wait_) {
136    NOTREACHED();
137  }
138}
139
140// This method is to avoid copying the 3 vector arguments into a bound callback.
141void GetOriginsTask::DoneOnUIThread() {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
143  ui_callback_.Run(origin_, usage_, quota_);
144}
145
146void RunOnUIThread(const base::Closure& task) {
147  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
148    task.Run();
149  } else {
150    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
151  }
152}
153
154}  // namespace
155
156
157// static
158jlong GetDefaultNativeAwQuotaManagerBridge(JNIEnv* env, jclass clazz) {
159  AwBrowserContext* browser_context =
160      AwContentBrowserClient::GetAwBrowserContext();
161
162  AwQuotaManagerBridgeImpl* bridge = static_cast<AwQuotaManagerBridgeImpl*>(
163      browser_context->GetQuotaManagerBridge());
164  DCHECK(bridge);
165  return reinterpret_cast<intptr_t>(bridge);
166}
167
168// static
169scoped_refptr<AwQuotaManagerBridge> AwQuotaManagerBridgeImpl::Create(
170    AwBrowserContext* browser_context) {
171  return new AwQuotaManagerBridgeImpl(browser_context);
172}
173
174AwQuotaManagerBridgeImpl::AwQuotaManagerBridgeImpl(
175    AwBrowserContext* browser_context)
176    : browser_context_(browser_context),
177      weak_factory_(this) {
178}
179
180AwQuotaManagerBridgeImpl::~AwQuotaManagerBridgeImpl() {}
181
182void AwQuotaManagerBridgeImpl::Init(JNIEnv* env, jobject object) {
183  java_ref_ = JavaObjectWeakGlobalRef(env, object);
184}
185
186StoragePartition* AwQuotaManagerBridgeImpl::GetStoragePartition() const {
187  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
188
189  // AndroidWebview does not use per-site storage partitions.
190  StoragePartition* storage_partition =
191      content::BrowserContext::GetDefaultStoragePartition(browser_context_);
192  DCHECK(storage_partition);
193  return storage_partition;
194}
195
196QuotaManager* AwQuotaManagerBridgeImpl::GetQuotaManager() const {
197  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
198
199  QuotaManager* quota_manager = GetStoragePartition()->GetQuotaManager();
200  DCHECK(quota_manager);
201  return quota_manager;
202}
203
204void AwQuotaManagerBridgeImpl::DeleteAllData(JNIEnv* env, jobject object) {
205  RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::DeleteAllDataOnUiThread,
206                           this));
207}
208
209void AwQuotaManagerBridgeImpl::DeleteAllDataOnUiThread() {
210  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211  GetStoragePartition()->ClearData(
212      // Clear all web storage data except cookies.
213      StoragePartition::REMOVE_DATA_MASK_APPCACHE |
214          StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
215          StoragePartition::REMOVE_DATA_MASK_INDEXEDDB |
216          StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE |
217          StoragePartition::REMOVE_DATA_MASK_WEBSQL,
218      StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY,
219      GURL(), StoragePartition::OriginMatcherFunction(),
220      base::Time(), base::Time::Max(), base::Bind(&base::DoNothing));
221}
222
223void AwQuotaManagerBridgeImpl::DeleteOrigin(
224    JNIEnv* env, jobject object, jstring origin) {
225  base::string16 origin_string(
226      base::android::ConvertJavaStringToUTF16(env, origin));
227  RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::DeleteOriginOnUiThread,
228                           this,
229                           origin_string));
230}
231
232void AwQuotaManagerBridgeImpl::DeleteOriginOnUiThread(
233    const base::string16& origin) {
234  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
235  StoragePartition* storage_partition = GetStoragePartition();
236  storage_partition->ClearDataForOrigin(
237      // All (temporary) QuotaClient types.
238      StoragePartition::REMOVE_DATA_MASK_APPCACHE |
239          StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS |
240          StoragePartition::REMOVE_DATA_MASK_INDEXEDDB |
241          StoragePartition::REMOVE_DATA_MASK_WEBSQL,
242      StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY,
243      GURL(origin),
244      storage_partition->GetURLRequestContext(),
245      base::Bind(&base::DoNothing));
246}
247
248void AwQuotaManagerBridgeImpl::GetOrigins(
249    JNIEnv* env, jobject object, jint callback_id) {
250  RunOnUIThread(base::Bind(&AwQuotaManagerBridgeImpl::GetOriginsOnUiThread,
251                           this,
252                           callback_id));
253}
254
255void AwQuotaManagerBridgeImpl::GetOriginsOnUiThread(jint callback_id) {
256  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
257
258  const GetOriginsCallback ui_callback = base::Bind(
259      &AwQuotaManagerBridgeImpl::GetOriginsCallbackImpl,
260      weak_factory_.GetWeakPtr(),
261      callback_id);
262
263  (new GetOriginsTask(ui_callback, GetQuotaManager()))->Run();
264}
265
266void AwQuotaManagerBridgeImpl::GetOriginsCallbackImpl(
267    int jcallback_id,
268    const std::vector<std::string>& origin,
269    const std::vector<int64>& usage,
270    const std::vector<int64>& quota) {
271  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
272  JNIEnv* env = AttachCurrentThread();
273  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
274  if (obj.is_null())
275    return;
276
277  Java_AwQuotaManagerBridge_onGetOriginsCallback(
278      env,
279      obj.obj(),
280      jcallback_id,
281      base::android::ToJavaArrayOfStrings(env, origin).obj(),
282      base::android::ToJavaLongArray(env, usage).obj(),
283      base::android::ToJavaLongArray(env, quota).obj());
284}
285
286namespace {
287
288void OnUsageAndQuotaObtained(
289    const AwQuotaManagerBridgeImpl::QuotaUsageCallback& ui_callback,
290    storage::QuotaStatusCode status_code,
291    int64 usage,
292    int64 quota) {
293  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
294  if (status_code != storage::kQuotaStatusOk) {
295    usage = 0;
296    quota = 0;
297  }
298  BrowserThread::PostTask(
299      BrowserThread::UI,
300      FROM_HERE,
301      base::Bind(ui_callback, usage, quota));
302}
303
304} // namespace
305
306void AwQuotaManagerBridgeImpl::GetUsageAndQuotaForOrigin(
307    JNIEnv* env, jobject object,
308    jstring origin,
309    jint callback_id,
310    bool is_quota) {
311  base::string16 origin_string(
312      base::android::ConvertJavaStringToUTF16(env, origin));
313  RunOnUIThread(base::Bind(
314      &AwQuotaManagerBridgeImpl::GetUsageAndQuotaForOriginOnUiThread,
315      this,
316      origin_string,
317      callback_id,
318      is_quota));
319}
320
321void AwQuotaManagerBridgeImpl::GetUsageAndQuotaForOriginOnUiThread(
322    const base::string16& origin,
323    jint callback_id,
324    bool is_quota) {
325  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
326  const QuotaUsageCallback ui_callback = base::Bind(
327      &AwQuotaManagerBridgeImpl::QuotaUsageCallbackImpl,
328      weak_factory_.GetWeakPtr(),
329      callback_id,
330      is_quota);
331
332  BrowserThread::PostTask(
333      BrowserThread::IO,
334      FROM_HERE,
335      base::Bind(&QuotaManager::GetUsageAndQuota,
336                 GetQuotaManager(),
337                 GURL(origin),
338                 storage::kStorageTypeTemporary,
339                 base::Bind(&OnUsageAndQuotaObtained, ui_callback)));
340}
341
342void AwQuotaManagerBridgeImpl::QuotaUsageCallbackImpl(
343    int jcallback_id, bool is_quota, int64 usage, int64 quota) {
344  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345  JNIEnv* env = AttachCurrentThread();
346  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
347  if (obj.is_null())
348    return;
349
350  Java_AwQuotaManagerBridge_onGetUsageAndQuotaForOriginCallback(
351      env, obj.obj(), jcallback_id, is_quota, usage, quota);
352}
353
354bool RegisterAwQuotaManagerBridge(JNIEnv* env) {
355  return RegisterNativesImpl(env);
356}
357
358}  // namespace android_webview
359