Home | History | Annotate | Download | only in browser
      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