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 "url/gurl.h" 14 #include "webkit/browser/quota/quota_manager.h" 15 16 using quota::QuotaClient; 17 using quota::QuotaManager; 18 using quota::QuotaStatusCode; 19 using quota::StorageType; 20 21 namespace 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. 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_); 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 quota::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 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 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 != quota::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 97 class 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 == quota::kStorageTypeTemporary || 121 params_.storage_type == quota::kStorageTypePersistent || 122 params_.storage_type == quota::kStorageTypeSyncable); 123 if (params_.storage_type == quota::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 != quota::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(quota::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(quota::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 != quota::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 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) { 221 } 222 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; 233 } 234 235 QuotaDispatcherHost::~QuotaDispatcherHost() {} 236 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); 244 } 245 246 void QuotaDispatcherHost::OnRequestStorageQuota( 247 const StorageQuotaParams& params) { 248 if (params.storage_type != quota::kStorageTypeTemporary && 249 params.storage_type != quota::kStorageTypePersistent) { 250 // Unsupported storage types. 251 Send(new QuotaMsg_DidFail(params.request_id, 252 quota::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