1// Copyright 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 "content/browser/quota_dispatcher_host.h"
6
7#include "base/bind.h"
8#include "base/memory/weak_ptr.h"
9#include "base/numerics/safe_conversions.h"
10#include "content/common/quota_messages.h"
11#include "content/public/browser/quota_permission_context.h"
12#include "net/base/net_util.h"
13#include "storage/browser/quota/quota_manager.h"
14#include "url/gurl.h"
15
16using storage::QuotaClient;
17using storage::QuotaManager;
18using storage::QuotaStatusCode;
19using storage::StorageType;
20
21namespace content {
22
23// Created one per request to carry the request's request_id around.
24// Dispatches requests from renderer/worker to the QuotaManager and
25// sends back the response to the renderer/worker.
26class QuotaDispatcherHost::RequestDispatcher {
27 public:
28  RequestDispatcher(base::WeakPtr<QuotaDispatcherHost> dispatcher_host,
29                    int request_id)
30      : dispatcher_host_(dispatcher_host),
31        render_process_id_(dispatcher_host->process_id_),
32        request_id_(request_id) {
33    dispatcher_host_->outstanding_requests_.AddWithID(this, request_id_);
34  }
35  virtual ~RequestDispatcher() {}
36
37 protected:
38  // Subclass must call this when it's done with the request.
39  void Completed() {
40    if (dispatcher_host_)
41      dispatcher_host_->outstanding_requests_.Remove(request_id_);
42  }
43
44  QuotaDispatcherHost* dispatcher_host() const {
45    return dispatcher_host_.get();
46  }
47  storage::QuotaManager* quota_manager() const {
48    return dispatcher_host_ ? dispatcher_host_->quota_manager_ : NULL;
49  }
50  QuotaPermissionContext* permission_context() const {
51    return dispatcher_host_ ?
52        dispatcher_host_->permission_context_.get() : NULL;
53  }
54  int render_process_id() const { return render_process_id_; }
55  int request_id() const { return request_id_; }
56
57 private:
58  base::WeakPtr<QuotaDispatcherHost> dispatcher_host_;
59  int render_process_id_;
60  int request_id_;
61};
62
63class QuotaDispatcherHost::QueryUsageAndQuotaDispatcher
64    : public RequestDispatcher {
65 public:
66  QueryUsageAndQuotaDispatcher(
67      base::WeakPtr<QuotaDispatcherHost> dispatcher_host,
68      int request_id)
69      : RequestDispatcher(dispatcher_host, request_id),
70        weak_factory_(this) {}
71  virtual ~QueryUsageAndQuotaDispatcher() {}
72
73  void QueryStorageUsageAndQuota(const GURL& origin, StorageType type) {
74    quota_manager()->GetUsageAndQuotaForWebApps(
75        origin, type,
76        base::Bind(&QueryUsageAndQuotaDispatcher::DidQueryStorageUsageAndQuota,
77                   weak_factory_.GetWeakPtr()));
78  }
79
80 private:
81  void DidQueryStorageUsageAndQuota(
82      QuotaStatusCode status, int64 usage, int64 quota) {
83    if (!dispatcher_host())
84      return;
85    if (status != storage::kQuotaStatusOk) {
86      dispatcher_host()->Send(new QuotaMsg_DidFail(request_id(), status));
87    } else {
88      dispatcher_host()->Send(new QuotaMsg_DidQueryStorageUsageAndQuota(
89          request_id(), usage, quota));
90    }
91    Completed();
92  }
93
94  base::WeakPtrFactory<QueryUsageAndQuotaDispatcher> weak_factory_;
95};
96
97class QuotaDispatcherHost::RequestQuotaDispatcher
98    : public RequestDispatcher {
99 public:
100  typedef RequestQuotaDispatcher self_type;
101
102  RequestQuotaDispatcher(base::WeakPtr<QuotaDispatcherHost> dispatcher_host,
103                         const StorageQuotaParams& params)
104      : RequestDispatcher(dispatcher_host, params.request_id),
105        params_(params),
106        current_usage_(0),
107        current_quota_(0),
108        requested_quota_(0),
109        weak_factory_(this) {
110    // Convert the requested size from uint64 to int64 since the quota backend
111    // requires int64 values.
112    // TODO(nhiroki): The backend should accept uint64 values.
113    requested_quota_ = base::saturated_cast<int64>(params_.requested_size);
114  }
115  virtual ~RequestQuotaDispatcher() {}
116
117  void Start() {
118    DCHECK(dispatcher_host());
119
120    DCHECK(params_.storage_type == storage::kStorageTypeTemporary ||
121           params_.storage_type == storage::kStorageTypePersistent ||
122           params_.storage_type == storage::kStorageTypeSyncable);
123    if (params_.storage_type == storage::kStorageTypePersistent) {
124      quota_manager()->GetUsageAndQuotaForWebApps(
125          params_.origin_url, params_.storage_type,
126          base::Bind(&self_type::DidGetPersistentUsageAndQuota,
127                     weak_factory_.GetWeakPtr()));
128    } else {
129      quota_manager()->GetUsageAndQuotaForWebApps(
130          params_.origin_url, params_.storage_type,
131          base::Bind(&self_type::DidGetTemporaryUsageAndQuota,
132                     weak_factory_.GetWeakPtr()));
133    }
134  }
135
136 private:
137  void DidGetPersistentUsageAndQuota(QuotaStatusCode status,
138                                     int64 usage,
139                                     int64 quota) {
140    if (!dispatcher_host())
141      return;
142    if (status != storage::kQuotaStatusOk) {
143      DidFinish(status, 0, 0);
144      return;
145    }
146
147    if (quota_manager()->IsStorageUnlimited(params_.origin_url,
148                                            params_.storage_type) ||
149        requested_quota_ <= quota) {
150      // Seems like we can just let it go.
151      DidFinish(storage::kQuotaStatusOk, usage, params_.requested_size);
152      return;
153    }
154    current_usage_ = usage;
155    current_quota_ = quota;
156
157    // Otherwise we need to consult with the permission context and
158    // possibly show a prompt.
159    DCHECK(permission_context());
160    permission_context()->RequestQuotaPermission(params_, render_process_id(),
161        base::Bind(&self_type::DidGetPermissionResponse,
162                   weak_factory_.GetWeakPtr()));
163  }
164
165  void DidGetTemporaryUsageAndQuota(QuotaStatusCode status,
166                                    int64 usage,
167                                    int64 quota) {
168    DidFinish(status, usage, std::min(requested_quota_, quota));
169  }
170
171  void DidGetPermissionResponse(
172      QuotaPermissionContext::QuotaPermissionResponse response) {
173    if (!dispatcher_host())
174      return;
175    if (response != QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW) {
176      // User didn't allow the new quota.  Just returning the current quota.
177      DidFinish(storage::kQuotaStatusOk, current_usage_, current_quota_);
178      return;
179    }
180    // Now we're allowed to set the new quota.
181    quota_manager()->SetPersistentHostQuota(
182        net::GetHostOrSpecFromURL(params_.origin_url), params_.requested_size,
183        base::Bind(&self_type::DidSetHostQuota, weak_factory_.GetWeakPtr()));
184  }
185
186  void DidSetHostQuota(QuotaStatusCode status, int64 new_quota) {
187    DidFinish(status, current_usage_, new_quota);
188  }
189
190  void DidFinish(QuotaStatusCode status,
191                 int64 usage,
192                 int64 granted_quota) {
193    if (!dispatcher_host())
194      return;
195    DCHECK(dispatcher_host());
196    if (status != storage::kQuotaStatusOk) {
197      dispatcher_host()->Send(new QuotaMsg_DidFail(request_id(), status));
198    } else {
199      dispatcher_host()->Send(new QuotaMsg_DidGrantStorageQuota(
200          request_id(), usage, granted_quota));
201    }
202    Completed();
203  }
204
205  StorageQuotaParams params_;
206  int64 current_usage_;
207  int64 current_quota_;
208  int64 requested_quota_;
209  base::WeakPtrFactory<self_type> weak_factory_;
210};
211
212QuotaDispatcherHost::QuotaDispatcherHost(
213    int process_id,
214    QuotaManager* quota_manager,
215    QuotaPermissionContext* permission_context)
216    : BrowserMessageFilter(QuotaMsgStart),
217      process_id_(process_id),
218      quota_manager_(quota_manager),
219      permission_context_(permission_context),
220      weak_factory_(this) {
221}
222
223bool QuotaDispatcherHost::OnMessageReceived(const IPC::Message& message) {
224  bool handled = true;
225  IPC_BEGIN_MESSAGE_MAP(QuotaDispatcherHost, message)
226    IPC_MESSAGE_HANDLER(QuotaHostMsg_QueryStorageUsageAndQuota,
227                        OnQueryStorageUsageAndQuota)
228    IPC_MESSAGE_HANDLER(QuotaHostMsg_RequestStorageQuota,
229                        OnRequestStorageQuota)
230    IPC_MESSAGE_UNHANDLED(handled = false)
231  IPC_END_MESSAGE_MAP()
232  return handled;
233}
234
235QuotaDispatcherHost::~QuotaDispatcherHost() {}
236
237void QuotaDispatcherHost::OnQueryStorageUsageAndQuota(
238    int request_id,
239    const GURL& origin,
240    StorageType type) {
241  QueryUsageAndQuotaDispatcher* dispatcher = new QueryUsageAndQuotaDispatcher(
242      weak_factory_.GetWeakPtr(), request_id);
243  dispatcher->QueryStorageUsageAndQuota(origin, type);
244}
245
246void QuotaDispatcherHost::OnRequestStorageQuota(
247    const StorageQuotaParams& params) {
248  if (params.storage_type != storage::kStorageTypeTemporary &&
249      params.storage_type != storage::kStorageTypePersistent) {
250    // Unsupported storage types.
251    Send(new QuotaMsg_DidFail(params.request_id,
252                              storage::kQuotaErrorNotSupported));
253    return;
254  }
255
256  RequestQuotaDispatcher* dispatcher =
257      new RequestQuotaDispatcher(weak_factory_.GetWeakPtr(),
258                                 params);
259  dispatcher->Start();
260}
261
262}  // namespace content
263