blob: 528586062d6d63ea536f8d326738b26f4fd791ba [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "modules/webdatabase/DatabaseTracker.h"
#include <memory>
#include "base/location.h"
#include "core/dom/Document.h"
#include "core/dom/ExecutionContext.h"
#include "modules/webdatabase/Database.h"
#include "modules/webdatabase/DatabaseClient.h"
#include "modules/webdatabase/DatabaseContext.h"
#include "modules/webdatabase/QuotaTracker.h"
#include "modules/webdatabase/sqlite/SQLiteFileSystem.h"
#include "platform/CrossThreadFunctional.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/weborigin/SecurityOriginHash.h"
#include "platform/wtf/Assertions.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/StdLibExtras.h"
#include "public/platform/Platform.h"
#include "public/platform/TaskType.h"
#include "public/platform/WebDatabaseObserver.h"
#include "public/platform/WebSecurityOrigin.h"
namespace blink {
static void DatabaseClosed(Database* database) {
if (Platform::Current()->DatabaseObserver()) {
Platform::Current()->DatabaseObserver()->DatabaseClosed(
WebSecurityOrigin(database->GetSecurityOrigin()),
database->StringIdentifier());
}
}
DatabaseTracker& DatabaseTracker::Tracker() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(DatabaseTracker, tracker, ());
return tracker;
}
DatabaseTracker::DatabaseTracker() {
SQLiteFileSystem::InitializeSQLite();
}
bool DatabaseTracker::CanEstablishDatabase(DatabaseContext* database_context,
const String& name,
const String& display_name,
unsigned estimated_size,
DatabaseError& error) {
ExecutionContext* execution_context = database_context->GetExecutionContext();
bool success = DatabaseClient::From(execution_context)
->AllowDatabase(execution_context, name, display_name,
estimated_size);
if (!success)
error = DatabaseError::kGenericSecurityError;
return success;
}
String DatabaseTracker::FullPathForDatabase(const SecurityOrigin* origin,
const String& name,
bool) {
return String(Platform::Current()->DatabaseCreateOriginIdentifier(
WebSecurityOrigin(origin))) +
"/" + name + "#";
}
void DatabaseTracker::AddOpenDatabase(Database* database) {
MutexLocker open_database_map_lock(open_database_map_guard_);
if (!open_database_map_)
open_database_map_ = std::make_unique<DatabaseOriginMap>();
String origin_string = database->GetSecurityOrigin()->ToRawString();
DatabaseNameMap* name_map = open_database_map_->at(origin_string);
if (!name_map) {
name_map = new DatabaseNameMap();
open_database_map_->Set(origin_string, name_map);
}
String name(database->StringIdentifier());
DatabaseSet* database_set = name_map->at(name);
if (!database_set) {
database_set = new DatabaseSet();
name_map->Set(name, database_set);
}
database_set->insert(database);
}
void DatabaseTracker::RemoveOpenDatabase(Database* database) {
{
MutexLocker open_database_map_lock(open_database_map_guard_);
String origin_string = database->GetSecurityOrigin()->ToRawString();
DCHECK(open_database_map_);
DatabaseNameMap* name_map = open_database_map_->at(origin_string);
if (!name_map)
return;
String name(database->StringIdentifier());
DatabaseSet* database_set = name_map->at(name);
if (!database_set)
return;
DatabaseSet::iterator found = database_set->find(database);
if (found == database_set->end())
return;
database_set->erase(found);
if (database_set->IsEmpty()) {
name_map->erase(name);
delete database_set;
if (name_map->IsEmpty()) {
open_database_map_->erase(origin_string);
delete name_map;
}
}
}
DatabaseClosed(database);
}
void DatabaseTracker::PrepareToOpenDatabase(Database* database) {
DCHECK(
database->GetDatabaseContext()->GetExecutionContext()->IsContextThread());
if (Platform::Current()->DatabaseObserver()) {
// This is an asynchronous call to the browser to open the database,
// however we can't actually use the database until we revieve an RPC back
// that advises is of the actual size of the database, so there is a race
// condition where the database is in an unusable state. To assist, we
// will record the size of the database straight away so we can use it
// immediately, and the real size will eventually be updated by the RPC from
// the browser.
Platform::Current()->DatabaseObserver()->DatabaseOpened(
WebSecurityOrigin(database->GetSecurityOrigin()),
database->StringIdentifier(), database->DisplayName(),
database->EstimatedSize());
// We write a temporary size of 0 to the QuotaTracker - we will be updated
// with the correct size via RPC asynchronously.
QuotaTracker::Instance().UpdateDatabaseSize(
database->GetSecurityOrigin(), database->StringIdentifier(), 0);
}
}
void DatabaseTracker::FailedToOpenDatabase(Database* database) {
DatabaseClosed(database);
}
unsigned long long DatabaseTracker::GetMaxSizeForDatabase(
const Database* database) {
unsigned long long space_available = 0;
unsigned long long database_size = 0;
QuotaTracker::Instance().GetDatabaseSizeAndSpaceAvailableToOrigin(
database->GetSecurityOrigin(), database->StringIdentifier(),
&database_size, &space_available);
return database_size + space_available;
}
void DatabaseTracker::CloseDatabasesImmediately(const SecurityOrigin* origin,
const String& name) {
String origin_string = origin->ToRawString();
MutexLocker open_database_map_lock(open_database_map_guard_);
if (!open_database_map_)
return;
DatabaseNameMap* name_map = open_database_map_->at(origin_string);
if (!name_map)
return;
DatabaseSet* database_set = name_map->at(name);
if (!database_set)
return;
// We have to call closeImmediately() on the context thread.
for (DatabaseSet::iterator it = database_set->begin();
it != database_set->end(); ++it) {
PostCrossThreadTask(
*(*it)->GetDatabaseTaskRunner(), FROM_HERE,
CrossThreadBind(&DatabaseTracker::CloseOneDatabaseImmediately,
CrossThreadUnretained(this), origin_string, name, *it));
}
}
void DatabaseTracker::ForEachOpenDatabaseInPage(Page* page,
DatabaseCallback callback) {
MutexLocker open_database_map_lock(open_database_map_guard_);
if (!open_database_map_)
return;
for (auto& origin_map : *open_database_map_) {
for (auto& name_database_set : *origin_map.value) {
for (Database* database : *name_database_set.value) {
ExecutionContext* context = database->GetExecutionContext();
DCHECK(context->IsDocument());
if (ToDocument(context)->GetFrame()->GetPage() == page)
callback.Run(database);
}
}
}
}
void DatabaseTracker::CloseOneDatabaseImmediately(const String& origin_string,
const String& name,
Database* database) {
// First we have to confirm the 'database' is still in our collection.
{
MutexLocker open_database_map_lock(open_database_map_guard_);
if (!open_database_map_)
return;
DatabaseNameMap* name_map = open_database_map_->at(origin_string);
if (!name_map)
return;
DatabaseSet* database_set = name_map->at(name);
if (!database_set)
return;
DatabaseSet::iterator found = database_set->find(database);
if (found == database_set->end())
return;
}
// And we have to call closeImmediately() without our collection lock being
// held.
database->CloseImmediately();
}
} // namespace blink