blob: 8e5b19842a0f7438e95645076c116dbc4f44fc7f [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/indexed_db/indexed_db_dispatcher_host.h"
#include <stddef.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/guid.h"
#include "base/memory/ptr_util.h"
#include "base/process/process.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/browser/bad_message.h"
#include "content/browser/indexed_db/indexed_db_callbacks.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_database_callbacks.h"
#include "content/browser/indexed_db/indexed_db_pending_connection.h"
#include "content/browser/indexed_db/indexed_db_value.h"
#include "content/browser/renderer_host/render_message_filter.h"
#include "content/common/indexed_db/indexed_db_messages.h"
#include "content/common/indexed_db/indexed_db_metadata.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/database/database_util.h"
#include "storage/common/database/database_identifier.h"
#include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
#include "url/origin.h"
namespace content {
namespace {
const char kInvalidOrigin[] = "Origin is invalid";
bool IsValidOrigin(const url::Origin& origin) {
return !origin.unique();
}
} // namespace
IndexedDBDispatcherHost::IndexedDBDispatcherHost(
int ipc_process_id,
net::URLRequestContextGetter* request_context_getter,
IndexedDBContextImpl* indexed_db_context,
ChromeBlobStorageContext* blob_storage_context)
: BrowserMessageFilter(IndexedDBMsgStart),
BrowserAssociatedInterface(this, this),
request_context_getter_(request_context_getter),
indexed_db_context_(indexed_db_context),
blob_storage_context_(blob_storage_context),
ipc_process_id_(ipc_process_id) {
DCHECK(indexed_db_context_.get());
}
IndexedDBDispatcherHost::~IndexedDBDispatcherHost() {
// TODO(alecflett): uncomment these when we find the source of these leaks.
// DCHECK(transaction_size_map_.empty());
// DCHECK(transaction_origin_map_.empty());
}
void IndexedDBDispatcherHost::OnChannelClosing() {
bool success = indexed_db_context_->TaskRunner()->PostTask(
FROM_HERE,
base::Bind(&IndexedDBDispatcherHost::ResetDispatcherHosts, this));
if (!success)
ResetDispatcherHosts();
}
void IndexedDBDispatcherHost::OnDestruct() const {
// The last reference to the dispatcher may be a posted task, which would
// be destructed on the IndexedDB thread. Without this override, that would
// take the dispatcher with it. Since the dispatcher may be keeping the
// IndexedDBContext alive, it might be destructed to on its own thread,
// which is not supported. Ensure destruction runs on the IO thread instead.
BrowserThread::DeleteOnIOThread::Destruct(this);
}
void IndexedDBDispatcherHost::ResetDispatcherHosts() {
// It is important that the various *_dispatcher_host_ members are reset
// on the IndexedDB thread, since there might be incoming messages on that
// thread, and we must not reset the dispatcher hosts until after those
// messages are processed.
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
// Prevent any pending connections from being processed.
is_open_ = false;
}
bool IndexedDBDispatcherHost::OnMessageReceived(const IPC::Message& message) {
return false;
}
std::string IndexedDBDispatcherHost::HoldBlobData(
const IndexedDBBlobInfo& blob_info) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::string uuid = blob_info.uuid();
storage::BlobStorageContext* context = blob_storage_context_->context();
std::unique_ptr<storage::BlobDataHandle> blob_data_handle;
if (uuid.empty()) {
uuid = base::GenerateGUID();
storage::BlobDataBuilder blob_data_builder(uuid);
blob_data_builder.set_content_type(base::UTF16ToUTF8(blob_info.type()));
blob_data_builder.AppendFile(blob_info.file_path(), 0, blob_info.size(),
blob_info.last_modified());
blob_data_handle = context->AddFinishedBlob(&blob_data_builder);
} else {
auto iter = blob_data_handle_map_.find(uuid);
if (iter != blob_data_handle_map_.end()) {
iter->second.second += 1;
return uuid;
}
blob_data_handle = context->GetBlobDataFromUUID(uuid);
}
DCHECK(!base::ContainsKey(blob_data_handle_map_, uuid));
blob_data_handle_map_[uuid] = std::make_pair(std::move(blob_data_handle), 1);
return uuid;
}
void IndexedDBDispatcherHost::DropBlobData(const std::string& uuid) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
const auto& iter = blob_data_handle_map_.find(uuid);
if (iter == blob_data_handle_map_.end()) {
DLOG(FATAL) << "Failed to find blob UUID in map:" << uuid;
return;
}
DCHECK_GE(iter->second.second, 1);
if (iter->second.second == 1)
blob_data_handle_map_.erase(iter);
else
--iter->second.second;
}
bool IndexedDBDispatcherHost::IsOpen() const {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
return is_open_;
}
void IndexedDBDispatcherHost::GetDatabaseNames(
::indexed_db::mojom::CallbacksAssociatedPtrInfo callbacks_info,
const url::Origin& origin) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsValidOrigin(origin)) {
mojo::ReportBadMessage(kInvalidOrigin);
return;
}
scoped_refptr<IndexedDBCallbacks> callbacks(
new IndexedDBCallbacks(this, origin, std::move(callbacks_info)));
indexed_db_context_->TaskRunner()->PostTask(
FROM_HERE,
base::Bind(&IndexedDBDispatcherHost::GetDatabaseNamesOnIDBThread, this,
base::Passed(&callbacks), origin));
}
void IndexedDBDispatcherHost::Open(
::indexed_db::mojom::CallbacksAssociatedPtrInfo callbacks_info,
::indexed_db::mojom::DatabaseCallbacksAssociatedPtrInfo
database_callbacks_info,
const url::Origin& origin,
const base::string16& name,
int64_t version,
int64_t transaction_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsValidOrigin(origin)) {
mojo::ReportBadMessage(kInvalidOrigin);
return;
}
scoped_refptr<IndexedDBCallbacks> callbacks(
new IndexedDBCallbacks(this, origin, std::move(callbacks_info)));
scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks(
new IndexedDBDatabaseCallbacks(this, std::move(database_callbacks_info)));
indexed_db_context_->TaskRunner()->PostTask(
FROM_HERE,
base::Bind(&IndexedDBDispatcherHost::OpenOnIDBThread, this,
base::Passed(&callbacks), base::Passed(&database_callbacks),
origin, name, version, transaction_id));
}
void IndexedDBDispatcherHost::DeleteDatabase(
::indexed_db::mojom::CallbacksAssociatedPtrInfo callbacks_info,
const url::Origin& origin,
const base::string16& name) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsValidOrigin(origin)) {
mojo::ReportBadMessage(kInvalidOrigin);
return;
}
scoped_refptr<IndexedDBCallbacks> callbacks(
new IndexedDBCallbacks(this, origin, std::move(callbacks_info)));
indexed_db_context_->TaskRunner()->PostTask(
FROM_HERE, base::Bind(&IndexedDBDispatcherHost::DeleteDatabaseOnIDBThread,
this, base::Passed(&callbacks), origin, name));
}
void IndexedDBDispatcherHost::GetDatabaseNamesOnIDBThread(
scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
base::FilePath indexed_db_path = indexed_db_context_->data_path();
context()->GetIDBFactory()->GetDatabaseNames(
callbacks, origin, indexed_db_path, request_context_getter_);
}
void IndexedDBDispatcherHost::OpenOnIDBThread(
scoped_refptr<IndexedDBCallbacks> callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
const url::Origin& origin,
const base::string16& name,
int64_t version,
int64_t transaction_id) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
base::TimeTicks begin_time = base::TimeTicks::Now();
base::FilePath indexed_db_path = indexed_db_context_->data_path();
// TODO(dgrogan): Don't let a non-existing database be opened (and therefore
// created) if this origin is already over quota.
callbacks->SetConnectionOpenStartTime(begin_time);
std::unique_ptr<IndexedDBPendingConnection> connection =
base::MakeUnique<IndexedDBPendingConnection>(
callbacks, database_callbacks, ipc_process_id_, transaction_id,
version);
DCHECK(request_context_getter_);
context()->GetIDBFactory()->Open(name, std::move(connection),
request_context_getter_, origin,
indexed_db_path);
}
void IndexedDBDispatcherHost::DeleteDatabaseOnIDBThread(
scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin,
const base::string16& name) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksOnCurrentThread());
base::FilePath indexed_db_path = indexed_db_context_->data_path();
DCHECK(request_context_getter_);
context()->GetIDBFactory()->DeleteDatabase(
name, request_context_getter_, callbacks, origin, indexed_db_path);
}
} // namespace content