| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/quota_dispatcher_host.h" |
| |
| #include "base/bind.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "content/common/quota_messages.h" |
| #include "content/public/browser/quota_permission_context.h" |
| #include "net/base/net_util.h" |
| #include "url/gurl.h" |
| #include "webkit/browser/quota/quota_manager.h" |
| |
| using quota::QuotaClient; |
| using quota::QuotaManager; |
| using quota::QuotaStatusCode; |
| using quota::StorageType; |
| |
| namespace content { |
| |
| // Created one per request to carry the request's request_id around. |
| // Dispatches requests from renderer/worker to the QuotaManager and |
| // sends back the response to the renderer/worker. |
| class QuotaDispatcherHost::RequestDispatcher { |
| public: |
| RequestDispatcher(base::WeakPtr<QuotaDispatcherHost> dispatcher_host, |
| int request_id) |
| : dispatcher_host_(dispatcher_host), |
| render_process_id_(dispatcher_host->process_id_), |
| request_id_(request_id) { |
| dispatcher_host_->outstanding_requests_.AddWithID(this, request_id_); |
| } |
| virtual ~RequestDispatcher() {} |
| |
| protected: |
| // Subclass must call this when it's done with the request. |
| void Completed() { |
| if (dispatcher_host_) |
| dispatcher_host_->outstanding_requests_.Remove(request_id_); |
| } |
| |
| QuotaDispatcherHost* dispatcher_host() const { |
| return dispatcher_host_.get(); |
| } |
| quota::QuotaManager* quota_manager() const { |
| return dispatcher_host_ ? dispatcher_host_->quota_manager_ : NULL; |
| } |
| QuotaPermissionContext* permission_context() const { |
| return dispatcher_host_ ? |
| dispatcher_host_->permission_context_.get() : NULL; |
| } |
| int render_process_id() const { return render_process_id_; } |
| int request_id() const { return request_id_; } |
| |
| private: |
| base::WeakPtr<QuotaDispatcherHost> dispatcher_host_; |
| int render_process_id_; |
| int request_id_; |
| }; |
| |
| class QuotaDispatcherHost::QueryUsageAndQuotaDispatcher |
| : public RequestDispatcher { |
| public: |
| QueryUsageAndQuotaDispatcher( |
| base::WeakPtr<QuotaDispatcherHost> dispatcher_host, |
| int request_id) |
| : RequestDispatcher(dispatcher_host, request_id), |
| weak_factory_(this) {} |
| virtual ~QueryUsageAndQuotaDispatcher() {} |
| |
| void QueryStorageUsageAndQuota(const GURL& origin, StorageType type) { |
| quota_manager()->GetUsageAndQuotaForWebApps( |
| origin, type, |
| base::Bind(&QueryUsageAndQuotaDispatcher::DidQueryStorageUsageAndQuota, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| private: |
| void DidQueryStorageUsageAndQuota( |
| QuotaStatusCode status, int64 usage, int64 quota) { |
| if (!dispatcher_host()) |
| return; |
| if (status != quota::kQuotaStatusOk) { |
| dispatcher_host()->Send(new QuotaMsg_DidFail(request_id(), status)); |
| } else { |
| dispatcher_host()->Send(new QuotaMsg_DidQueryStorageUsageAndQuota( |
| request_id(), usage, quota)); |
| } |
| Completed(); |
| } |
| |
| base::WeakPtrFactory<QueryUsageAndQuotaDispatcher> weak_factory_; |
| }; |
| |
| class QuotaDispatcherHost::RequestQuotaDispatcher |
| : public RequestDispatcher { |
| public: |
| typedef RequestQuotaDispatcher self_type; |
| |
| RequestQuotaDispatcher(base::WeakPtr<QuotaDispatcherHost> dispatcher_host, |
| const StorageQuotaParams& params) |
| : RequestDispatcher(dispatcher_host, params.request_id), |
| params_(params), |
| current_usage_(0), |
| current_quota_(0), |
| requested_quota_(0), |
| weak_factory_(this) { |
| // Convert the requested size from uint64 to int64 since the quota backend |
| // requires int64 values. |
| // TODO(nhiroki): The backend should accept uint64 values. |
| requested_quota_ = base::saturated_cast<int64>(params_.requested_size); |
| } |
| virtual ~RequestQuotaDispatcher() {} |
| |
| void Start() { |
| DCHECK(dispatcher_host()); |
| |
| DCHECK(params_.storage_type == quota::kStorageTypeTemporary || |
| params_.storage_type == quota::kStorageTypePersistent || |
| params_.storage_type == quota::kStorageTypeSyncable); |
| if (params_.storage_type == quota::kStorageTypePersistent) { |
| quota_manager()->GetUsageAndQuotaForWebApps( |
| params_.origin_url, params_.storage_type, |
| base::Bind(&self_type::DidGetPersistentUsageAndQuota, |
| weak_factory_.GetWeakPtr())); |
| } else { |
| quota_manager()->GetUsageAndQuotaForWebApps( |
| params_.origin_url, params_.storage_type, |
| base::Bind(&self_type::DidGetTemporaryUsageAndQuota, |
| weak_factory_.GetWeakPtr())); |
| } |
| } |
| |
| private: |
| void DidGetPersistentUsageAndQuota(QuotaStatusCode status, |
| int64 usage, |
| int64 quota) { |
| if (!dispatcher_host()) |
| return; |
| if (status != quota::kQuotaStatusOk) { |
| DidFinish(status, 0, 0); |
| return; |
| } |
| |
| if (quota_manager()->IsStorageUnlimited(params_.origin_url, |
| params_.storage_type) || |
| requested_quota_ <= quota) { |
| // Seems like we can just let it go. |
| DidFinish(quota::kQuotaStatusOk, usage, params_.requested_size); |
| return; |
| } |
| current_usage_ = usage; |
| current_quota_ = quota; |
| |
| // Otherwise we need to consult with the permission context and |
| // possibly show a prompt. |
| DCHECK(permission_context()); |
| permission_context()->RequestQuotaPermission(params_, render_process_id(), |
| base::Bind(&self_type::DidGetPermissionResponse, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void DidGetTemporaryUsageAndQuota(QuotaStatusCode status, |
| int64 usage, |
| int64 quota) { |
| DidFinish(status, usage, std::min(requested_quota_, quota)); |
| } |
| |
| void DidGetPermissionResponse( |
| QuotaPermissionContext::QuotaPermissionResponse response) { |
| if (!dispatcher_host()) |
| return; |
| if (response != QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW) { |
| // User didn't allow the new quota. Just returning the current quota. |
| DidFinish(quota::kQuotaStatusOk, current_usage_, current_quota_); |
| return; |
| } |
| // Now we're allowed to set the new quota. |
| quota_manager()->SetPersistentHostQuota( |
| net::GetHostOrSpecFromURL(params_.origin_url), params_.requested_size, |
| base::Bind(&self_type::DidSetHostQuota, weak_factory_.GetWeakPtr())); |
| } |
| |
| void DidSetHostQuota(QuotaStatusCode status, int64 new_quota) { |
| DidFinish(status, current_usage_, new_quota); |
| } |
| |
| void DidFinish(QuotaStatusCode status, |
| int64 usage, |
| int64 granted_quota) { |
| if (!dispatcher_host()) |
| return; |
| DCHECK(dispatcher_host()); |
| if (status != quota::kQuotaStatusOk) { |
| dispatcher_host()->Send(new QuotaMsg_DidFail(request_id(), status)); |
| } else { |
| dispatcher_host()->Send(new QuotaMsg_DidGrantStorageQuota( |
| request_id(), usage, granted_quota)); |
| } |
| Completed(); |
| } |
| |
| StorageQuotaParams params_; |
| int64 current_usage_; |
| int64 current_quota_; |
| int64 requested_quota_; |
| base::WeakPtrFactory<self_type> weak_factory_; |
| }; |
| |
| QuotaDispatcherHost::QuotaDispatcherHost( |
| int process_id, |
| QuotaManager* quota_manager, |
| QuotaPermissionContext* permission_context) |
| : BrowserMessageFilter(QuotaMsgStart), |
| process_id_(process_id), |
| quota_manager_(quota_manager), |
| permission_context_(permission_context), |
| weak_factory_(this) { |
| } |
| |
| bool QuotaDispatcherHost::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(QuotaDispatcherHost, message) |
| IPC_MESSAGE_HANDLER(QuotaHostMsg_QueryStorageUsageAndQuota, |
| OnQueryStorageUsageAndQuota) |
| IPC_MESSAGE_HANDLER(QuotaHostMsg_RequestStorageQuota, |
| OnRequestStorageQuota) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| QuotaDispatcherHost::~QuotaDispatcherHost() {} |
| |
| void QuotaDispatcherHost::OnQueryStorageUsageAndQuota( |
| int request_id, |
| const GURL& origin, |
| StorageType type) { |
| QueryUsageAndQuotaDispatcher* dispatcher = new QueryUsageAndQuotaDispatcher( |
| weak_factory_.GetWeakPtr(), request_id); |
| dispatcher->QueryStorageUsageAndQuota(origin, type); |
| } |
| |
| void QuotaDispatcherHost::OnRequestStorageQuota( |
| const StorageQuotaParams& params) { |
| if (params.storage_type != quota::kStorageTypeTemporary && |
| params.storage_type != quota::kStorageTypePersistent) { |
| // Unsupported storage types. |
| Send(new QuotaMsg_DidFail(params.request_id, |
| quota::kQuotaErrorNotSupported)); |
| return; |
| } |
| |
| RequestQuotaDispatcher* dispatcher = |
| new RequestQuotaDispatcher(weak_factory_.GetWeakPtr(), |
| params); |
| dispatcher->Start(); |
| } |
| |
| } // namespace content |