blob: abf0b26b62dbe8f5888324b775eb3e1c85621d2e [file] [log] [blame]
// Copyright (c) 2012 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/renderer_host/quota_dispatcher_host.h"
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "content/common/quota_messages.h"
#include "content/public/browser/quota_permission_context.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_util.h"
#include "webkit/quota/quota_manager.h"
using content::QuotaPermissionContext;
using quota::QuotaClient;
using quota::QuotaManager;
using quota::QuotaStatusCode;
using quota::StorageType;
// 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(QuotaDispatcherHost* dispatcher_host,
int request_id)
: dispatcher_host_(dispatcher_host),
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() {
dispatcher_host_->outstanding_requests_.Remove(request_id_);
}
QuotaDispatcherHost* dispatcher_host() const { return dispatcher_host_; }
quota::QuotaManager* quota_manager() const {
return dispatcher_host_->quota_manager_;
}
QuotaPermissionContext* permission_context() const {
return dispatcher_host_->permission_context_.get();
}
int render_process_id() const { return dispatcher_host_->process_id_; }
int request_id() const { return request_id_; }
private:
QuotaDispatcherHost* dispatcher_host_;
int request_id_;
};
class QuotaDispatcherHost::QueryUsageAndQuotaDispatcher
: public RequestDispatcher {
public:
QueryUsageAndQuotaDispatcher(
QuotaDispatcherHost* dispatcher_host,
int request_id)
: RequestDispatcher(dispatcher_host, request_id),
weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
virtual ~QueryUsageAndQuotaDispatcher() {}
void QueryStorageUsageAndQuota(const GURL& origin, StorageType type) {
quota_manager()->GetUsageAndQuota(
origin, type,
base::Bind(&QueryUsageAndQuotaDispatcher::DidQueryStorageUsageAndQuota,
weak_factory_.GetWeakPtr()));
}
private:
void DidQueryStorageUsageAndQuota(
QuotaStatusCode status, int64 usage, int64 quota) {
DCHECK(dispatcher_host());
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(QuotaDispatcherHost* dispatcher_host,
int request_id,
const GURL& origin,
StorageType type,
int64 requested_quota,
int render_view_id)
: RequestDispatcher(dispatcher_host, request_id),
origin_(origin),
host_(net::GetHostOrSpecFromURL(origin)),
type_(type),
current_quota_(0),
requested_quota_(requested_quota),
render_view_id_(render_view_id),
weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
virtual ~RequestQuotaDispatcher() {}
void Start() {
DCHECK(type_ == quota::kStorageTypeTemporary ||
type_ == quota::kStorageTypePersistent);
if (type_ == quota::kStorageTypePersistent) {
quota_manager()->GetPersistentHostQuota(
host_,
base::Bind(&self_type::DidGetHostQuota,
weak_factory_.GetWeakPtr()));
} else {
quota_manager()->GetUsageAndQuota(
origin_, type_,
base::Bind(&self_type::DidGetTemporaryUsageAndQuota,
weak_factory_.GetWeakPtr()));
}
}
private:
void DidGetHostQuota(QuotaStatusCode status,
const std::string& host,
StorageType type,
int64 quota) {
DCHECK_EQ(type_, type);
DCHECK_EQ(host_, host);
if (status != quota::kQuotaStatusOk) {
DidFinish(status, 0);
return;
}
if (requested_quota_ <= quota) {
// Seems like we can just let it go.
DidFinish(quota::kQuotaStatusOk, requested_quota_);
return;
}
current_quota_ = quota;
// Otherwise we need to consult with the permission context and
// possibly show an infobar.
DCHECK(permission_context());
permission_context()->RequestQuotaPermission(
origin_, type_, requested_quota_, render_process_id(), render_view_id_,
base::Bind(&self_type::DidGetPermissionResponse,
weak_factory_.GetWeakPtr()));
}
void DidGetTemporaryUsageAndQuota(QuotaStatusCode status,
int64 usage_unused,
int64 quota) {
DidFinish(status, std::min(requested_quota_, quota));
}
void DidGetPermissionResponse(
QuotaPermissionContext::QuotaPermissionResponse response) {
if (response != QuotaPermissionContext::QUOTA_PERMISSION_RESPONSE_ALLOW) {
// User didn't allow the new quota. Just returning the current quota.
DidFinish(quota::kQuotaStatusOk, current_quota_);
return;
}
// Now we're allowed to set the new quota.
quota_manager()->SetPersistentHostQuota(
host_, requested_quota_,
base::Bind(&self_type::DidSetHostQuota,
weak_factory_.GetWeakPtr()));
}
void DidSetHostQuota(QuotaStatusCode status,
const std::string& host,
StorageType type,
int64 new_quota) {
DCHECK_EQ(host_, host);
DCHECK_EQ(type_, type);
DidFinish(status, new_quota);
}
void DidFinish(QuotaStatusCode status, int64 granted_quota) {
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(), granted_quota));
}
Completed();
}
const GURL origin_;
const std::string host_;
const StorageType type_;
int64 current_quota_;
const int64 requested_quota_;
const int render_view_id_;
base::WeakPtrFactory<self_type> weak_factory_;
};
QuotaDispatcherHost::QuotaDispatcherHost(
int process_id,
QuotaManager* quota_manager,
QuotaPermissionContext* permission_context)
: process_id_(process_id),
quota_manager_(quota_manager),
permission_context_(permission_context) {
}
QuotaDispatcherHost::~QuotaDispatcherHost() {
}
bool QuotaDispatcherHost::OnMessageReceived(
const IPC::Message& message, bool* message_was_ok) {
*message_was_ok = true;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(QuotaDispatcherHost, message, *message_was_ok)
IPC_MESSAGE_HANDLER(QuotaHostMsg_QueryStorageUsageAndQuota,
OnQueryStorageUsageAndQuota)
IPC_MESSAGE_HANDLER(QuotaHostMsg_RequestStorageQuota,
OnRequestStorageQuota)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX()
return handled;
}
void QuotaDispatcherHost::OnQueryStorageUsageAndQuota(
int request_id,
const GURL& origin,
StorageType type) {
QueryUsageAndQuotaDispatcher* dispatcher = new QueryUsageAndQuotaDispatcher(
this, request_id);
dispatcher->QueryStorageUsageAndQuota(origin, type);
}
void QuotaDispatcherHost::OnRequestStorageQuota(
int render_view_id,
int request_id,
const GURL& origin,
StorageType type,
int64 requested_size) {
if (quota_manager_->IsStorageUnlimited(origin)) {
// If the origin is marked 'unlimited' we always just return ok.
Send(new QuotaMsg_DidGrantStorageQuota(request_id, requested_size));
return;
}
if (type != quota::kStorageTypeTemporary &&
type != quota::kStorageTypePersistent) {
// Unsupported storage types.
Send(new QuotaMsg_DidFail(request_id, quota::kQuotaErrorNotSupported));
return;
}
RequestQuotaDispatcher* dispatcher = new RequestQuotaDispatcher(
this, request_id, origin, type, requested_size, render_view_id);
dispatcher->Start();
}