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