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: 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 } 34 virtual ~RequestDispatcher() {} 35 36 protected: 37 // Subclass must call this when it's done with the request. 38 void Completed() { 39 if (dispatcher_host_) 40 dispatcher_host_->outstanding_requests_.Remove(request_id_); 41 } 42 43 QuotaDispatcherHost* dispatcher_host() const { 44 return dispatcher_host_.get(); 45 } 46 quota::QuotaManager* quota_manager() const { 47 return dispatcher_host_ ? dispatcher_host_->quota_manager_ : NULL; 48 } 49 QuotaPermissionContext* permission_context() const { 50 return dispatcher_host_ ? 51 dispatcher_host_->permission_context_.get() : NULL; 52 } 53 int render_process_id() const { return render_process_id_; } 54 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: 65 QueryUsageAndQuotaDispatcher( 66 base::WeakPtr<QuotaDispatcherHost> dispatcher_host, 67 int request_id) 68 : RequestDispatcher(dispatcher_host, request_id), 69 weak_factory_(this) {} 70 virtual ~QueryUsageAndQuotaDispatcher() {} 71 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: 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 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) {} 115 virtual ~RequestQuotaDispatcher() {} 116 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: 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 167 void DidGetTemporaryUsageAndQuota(QuotaStatusCode status, 168 int64 usage_unused, 169 int64 quota) { 170 DidFinish(status, std::min(requested_quota_, quota)); 171 } 172 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 188 void DidSetHostQuota(QuotaStatusCode status, int64 new_quota) { 189 DidFinish(status, new_quota); 190 } 191 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 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 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 238 QuotaDispatcherHost::~QuotaDispatcherHost() {} 239 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 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