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 "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