Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / quota_dispatcher_host.cc
blob2ea8a5ec9b59a89792a8258ea74986703df43626
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.
5 #include "content/browser/quota_dispatcher_host.h"
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"
16 using storage::QuotaClient;
17 using storage::QuotaManager;
18 using storage::QuotaStatusCode;
19 using storage::StorageType;
21 namespace content {
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.
26 class 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_);
35 virtual ~RequestDispatcher() {}
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_);
44 QuotaDispatcherHost* dispatcher_host() const {
45 return dispatcher_host_.get();
47 storage::QuotaManager* quota_manager() const {
48 return dispatcher_host_ ? dispatcher_host_->quota_manager_ : NULL;
50 QuotaPermissionContext* permission_context() const {
51 return dispatcher_host_ ?
52 dispatcher_host_->permission_context_.get() : NULL;
54 int render_process_id() const { return render_process_id_; }
55 int request_id() const { return request_id_; }
57 private:
58 base::WeakPtr<QuotaDispatcherHost> dispatcher_host_;
59 int render_process_id_;
60 int request_id_;
63 class 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 ~QueryUsageAndQuotaDispatcher() override {}
73 void QueryStorageUsageAndQuota(const GURL& origin, StorageType type) {
74 quota_manager()->GetUsageAndQuotaForWebApps(
75 origin, type,
76 base::Bind(&QueryUsageAndQuotaDispatcher::DidQueryStorageUsageAndQuota,
77 weak_factory_.GetWeakPtr()));
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));
91 Completed();
94 base::WeakPtrFactory<QueryUsageAndQuotaDispatcher> weak_factory_;
97 class QuotaDispatcherHost::RequestQuotaDispatcher
98 : public RequestDispatcher {
99 public:
100 typedef RequestQuotaDispatcher self_type;
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);
115 ~RequestQuotaDispatcher() override {}
117 void Start() {
118 DCHECK(dispatcher_host());
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()));
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;
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;
154 current_usage_ = usage;
155 current_quota_ = quota;
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()));
165 void DidGetTemporaryUsageAndQuota(QuotaStatusCode status,
166 int64 usage,
167 int64 quota) {
168 DidFinish(status, usage, std::min(requested_quota_, quota));
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;
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()));
186 void DidSetHostQuota(QuotaStatusCode status, int64 new_quota) {
187 DidFinish(status, current_usage_, new_quota);
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));
202 Completed();
205 StorageQuotaParams params_;
206 int64 current_usage_;
207 int64 current_quota_;
208 int64 requested_quota_;
209 base::WeakPtrFactory<self_type> weak_factory_;
212 QuotaDispatcherHost::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) {
223 bool 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;
235 QuotaDispatcherHost::~QuotaDispatcherHost() {}
237 void 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);
246 void 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;
256 RequestQuotaDispatcher* dispatcher =
257 new RequestQuotaDispatcher(weak_factory_.GetWeakPtr(),
258 params);
259 dispatcher->Start();
262 } // namespace content