Commit b3e916f5 authored by jsbell@chromium.org's avatar jsbell@chromium.org

Handling LevelDB errors encountered after open.

The previous IndexedDB implementation only checked for (and reported)
corrupted LevelDB's during open. If they are determined to be corrupted
during use then the database was simply closed. The page would be unaware of
this, and would likely reopen the database at a later time (which would work)
only to fail again during a read/write operation. There was no way for the
page to get out of that corrupted state - the user would have to delete all
origin data.

This change will record the fact that the backing store was corrupted, and
during open this would be read and, if present, result in the same dataLoss
state being reported to the page in the upgradeneeded event.

Also split IndexedDBBackingStore's REPORT_ERROR macro in two:
REPORT_ERROR_UNTESTED (which calls NOTREACHED) and REPORT_ERROR. REPORT_ERROR
is used for errors which are encountered during tests. This was done so that
runtime errors could still be caught during a debug build run.

BUG=322707
R=isherman@chromium.org, jochen@chromium.org, jsbell@chromium.org

Review URL: https://codereview.chromium.org/197333009

Patch from Christopher Mumford <cmumford@chromium.org>.

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260147 0039d316-1c4b-4281-b951-d872f2087c98
parent e746403e
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
namespace content { namespace content {
class IndexedDBDatabaseError;
class LevelDBComparator; class LevelDBComparator;
class LevelDBDatabase; class LevelDBDatabase;
struct IndexedDBValue; struct IndexedDBValue;
...@@ -78,6 +79,7 @@ class CONTENT_EXPORT IndexedDBBackingStore ...@@ -78,6 +79,7 @@ class CONTENT_EXPORT IndexedDBBackingStore
const GURL& origin_url, const GURL& origin_url,
LevelDBFactory* factory); LevelDBFactory* factory);
virtual void Compact();
virtual std::vector<base::string16> GetDatabaseNames(); virtual std::vector<base::string16> GetDatabaseNames();
virtual leveldb::Status GetIDBDatabaseMetaData( virtual leveldb::Status GetIDBDatabaseMetaData(
const base::string16& name, const base::string16& name,
...@@ -94,6 +96,12 @@ class CONTENT_EXPORT IndexedDBBackingStore ...@@ -94,6 +96,12 @@ class CONTENT_EXPORT IndexedDBBackingStore
int64 int_version); int64 int_version);
virtual leveldb::Status DeleteDatabase(const base::string16& name); virtual leveldb::Status DeleteDatabase(const base::string16& name);
// Assumes caller has already closed the backing store.
static leveldb::Status DestroyBackingStore(const base::FilePath& path_base,
const GURL& origin_url);
static bool RecordCorruptionInfo(const base::FilePath& path_base,
const GURL& origin_url,
const std::string& message);
leveldb::Status GetObjectStores( leveldb::Status GetObjectStores(
int64 database_id, int64 database_id,
IndexedDBDatabaseMetadata::ObjectStoreMap* map) WARN_UNUSED_RESULT; IndexedDBDatabaseMetadata::ObjectStoreMap* map) WARN_UNUSED_RESULT;
...@@ -325,6 +333,9 @@ class CONTENT_EXPORT IndexedDBBackingStore ...@@ -325,6 +333,9 @@ class CONTENT_EXPORT IndexedDBBackingStore
const GURL& origin_url, const GURL& origin_url,
scoped_ptr<LevelDBDatabase> db, scoped_ptr<LevelDBDatabase> db,
scoped_ptr<LevelDBComparator> comparator); scoped_ptr<LevelDBComparator> comparator);
static bool ReadCorruptionInfo(const base::FilePath& path_base,
const GURL& origin_url,
std::string& message);
leveldb::Status FindKeyInIndex( leveldb::Status FindKeyInIndex(
IndexedDBBackingStore::Transaction* transaction, IndexedDBBackingStore::Transaction* transaction,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/file_util.h" #include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
...@@ -24,6 +25,10 @@ ...@@ -24,6 +25,10 @@
#include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h" #include "content/shell/browser/shell.h"
#include "net/base/net_errors.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "webkit/browser/database/database_util.h" #include "webkit/browser/database/database_util.h"
#include "webkit/browser/quota/quota_manager.h" #include "webkit/browser/quota/quota_manager.h"
...@@ -356,6 +361,134 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) { ...@@ -356,6 +361,134 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) {
SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html")); SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html"));
} }
namespace {
static void CompactIndexedDBBackingStore(
scoped_refptr<IndexedDBContextImpl> context,
const GURL& origin_url) {
IndexedDBFactory* factory = context->GetIDBFactory();
std::pair<IndexedDBFactory::OriginDBMapIterator,
IndexedDBFactory::OriginDBMapIterator> range =
factory->GetOpenDatabasesForOrigin(origin_url);
if (range.first == range.second) // If no open db's for this origin
return;
// Compact the first db's backing store since all the db's are in the same
// backing store.
IndexedDBDatabase* db = range.first->second;
IndexedDBBackingStore* backing_store = db->backing_store();
backing_store->Compact();
}
static void CorruptIndexedDBDatabase(
IndexedDBContextImpl* context,
const GURL& origin_url,
base::WaitableEvent* signal_when_finished) {
CompactIndexedDBBackingStore(context, origin_url);
int numFiles = 0;
int numErrors = 0;
base::FilePath idb_data_path = context->GetFilePath(origin_url);
const bool recursive = false;
base::FileEnumerator enumerator(
idb_data_path, recursive, base::FileEnumerator::FILES);
for (base::FilePath idb_file = enumerator.Next(); !idb_file.empty();
idb_file = enumerator.Next()) {
int64 size(0);
GetFileSize(idb_file, &size);
if (idb_file.Extension() == FILE_PATH_LITERAL(".ldb")) {
numFiles++;
base::ScopedFILE f(base::OpenFile(idb_file, "w"));
if (f) {
char zero(0);
if (size != (int64)fwrite(&zero, sizeof(zero), size, f.get()))
numErrors++;
} else {
numErrors++;
}
}
}
VLOG(0) << "There were " << numFiles << " in " << idb_data_path.value()
<< " with " << numErrors << " errors";
signal_when_finished->Signal();
}
const std::string s_corrupt_db_test_prefix = "/corrupt/test/";
static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler(
IndexedDBContextImpl* context,
const GURL& origin_url,
const std::string& path,
const net::test_server::HttpRequest& request) {
std::string request_path;
if (path.find(s_corrupt_db_test_prefix) != std::string::npos)
request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size());
else
return scoped_ptr<net::test_server::HttpResponse>();
// Remove the query string if present.
std::string request_query;
size_t query_pos = request_path.find('?');
if (query_pos != std::string::npos) {
request_query = request_path.substr(query_pos + 1);
request_path = request_path.substr(0, query_pos);
}
if (request_path == "corruptdb" && !request_query.empty()) {
VLOG(0) << "Requested to corrupt IndexedDB: " << request_query;
base::WaitableEvent signal_when_finished(false, false);
context->TaskRunner()->PostTask(FROM_HERE,
base::Bind(&CorruptIndexedDBDatabase,
base::ConstRef(context),
origin_url,
&signal_when_finished));
signal_when_finished.Wait();
scoped_ptr<net::test_server::BasicHttpResponse> http_response(
new net::test_server::BasicHttpResponse);
http_response->set_code(net::HTTP_OK);
return http_response.PassAs<net::test_server::HttpResponse>();
}
// A request for a test resource
base::FilePath resourcePath =
content::GetTestFilePath("indexeddb", request_path.c_str());
scoped_ptr<net::test_server::BasicHttpResponse> http_response(
new net::test_server::BasicHttpResponse);
http_response->set_code(net::HTTP_OK);
std::string file_contents;
if (!base::ReadFileToString(resourcePath, &file_contents))
return scoped_ptr<net::test_server::HttpResponse>();
http_response->set_content(file_contents);
return http_response.PassAs<net::test_server::HttpResponse>();
}
} // namespace
IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CorruptedOpenDatabase) {
ASSERT_TRUE(embedded_test_server()->Started() ||
embedded_test_server()->InitializeAndWaitUntilReady());
const GURL& origin_url = embedded_test_server()->base_url();
embedded_test_server()->RegisterRequestHandler(
base::Bind(&CorruptDBRequestHandler,
base::ConstRef(GetContext()),
origin_url,
s_corrupt_db_test_prefix));
std::string test_file =
s_corrupt_db_test_prefix + "corrupted_open_db_detection.html";
SimpleTest(embedded_test_server()->GetURL(test_file));
test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html";
SimpleTest(embedded_test_server()->GetURL(test_file));
}
IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DeleteCompactsBackingStore) { IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DeleteCompactsBackingStore) {
const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html"); const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html");
SimpleTest(GURL(test_url.spec() + "#fill")); SimpleTest(GURL(test_url.spec() + "#fill"));
...@@ -449,6 +582,7 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) { ...@@ -449,6 +582,7 @@ IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) {
base::string16 expected_title16(ASCIIToUTF16("connection closed")); base::string16 expected_title16(ASCIIToUTF16("connection closed"));
TitleWatcher title_watcher(shell()->web_contents(), expected_title16); TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
title_watcher.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error"));
EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "content/browser/indexed_db/indexed_db_connection.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_cursor.h" #include "content/browser/indexed_db/indexed_db_cursor.h"
#include "content/browser/indexed_db/indexed_db_factory.h" #include "content/browser/indexed_db/indexed_db_factory.h"
#include "content/browser/indexed_db/indexed_db_index_writer.h" #include "content/browser/indexed_db/indexed_db_index_writer.h"
...@@ -294,17 +295,22 @@ void IndexedDBDatabase::CreateObjectStoreOperation( ...@@ -294,17 +295,22 @@ void IndexedDBDatabase::CreateObjectStoreOperation(
const IndexedDBObjectStoreMetadata& object_store_metadata, const IndexedDBObjectStoreMetadata& object_store_metadata,
IndexedDBTransaction* transaction) { IndexedDBTransaction* transaction) {
IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation"); IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
if (!backing_store_->CreateObjectStore( leveldb::Status s =
transaction->BackingStoreTransaction(), backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
transaction->database()->id(), transaction->database()->id(),
object_store_metadata.id, object_store_metadata.id,
object_store_metadata.name, object_store_metadata.name,
object_store_metadata.key_path, object_store_metadata.key_path,
object_store_metadata.auto_increment).ok()) { object_store_metadata.auto_increment);
transaction->Abort(IndexedDBDatabaseError( if (!s.ok()) {
IndexedDBDatabaseError error(
blink::WebIDBDatabaseExceptionUnknownError, blink::WebIDBDatabaseExceptionUnknownError,
ASCIIToUTF16("Internal error creating object store '") + ASCIIToUTF16("Internal error creating object store '") +
object_store_metadata.name + ASCIIToUTF16("'."))); object_store_metadata.name + ASCIIToUTF16("'."));
transaction->Abort(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
} }
...@@ -436,8 +442,12 @@ void IndexedDBDatabase::DeleteIndexOperation( ...@@ -436,8 +442,12 @@ void IndexedDBDatabase::DeleteIndexOperation(
base::string16 error_string = base::string16 error_string =
ASCIIToUTF16("Internal error deleting index '") + ASCIIToUTF16("Internal error deleting index '") +
index_metadata.name + ASCIIToUTF16("'."); index_metadata.name + ASCIIToUTF16("'.");
transaction->Abort(IndexedDBDatabaseError( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
blink::WebIDBDatabaseExceptionUnknownError, error_string)); error_string);
transaction->Abort(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
} }
} }
...@@ -569,9 +579,13 @@ void IndexedDBDatabase::GetOperation( ...@@ -569,9 +579,13 @@ void IndexedDBDatabase::GetOperation(
*key, *key,
&value); &value);
if (!s.ok()) { if (!s.ok()) {
callbacks->OnError( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, "Internal error in GetRecord.");
"Internal error in GetRecord.")); callbacks->OnError(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
...@@ -599,9 +613,12 @@ void IndexedDBDatabase::GetOperation( ...@@ -599,9 +613,12 @@ void IndexedDBDatabase::GetOperation(
*key, *key,
&primary_key); &primary_key);
if (!s.ok()) { if (!s.ok()) {
callbacks->OnError( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, "Internal error in GetPrimaryKeyViaIndex.");
"Internal error in GetPrimaryKeyViaIndex.")); callbacks->OnError(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
if (!primary_key) { if (!primary_key) {
...@@ -622,9 +639,12 @@ void IndexedDBDatabase::GetOperation( ...@@ -622,9 +639,12 @@ void IndexedDBDatabase::GetOperation(
*primary_key, *primary_key,
&value); &value);
if (!s.ok()) { if (!s.ok()) {
callbacks->OnError( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, "Internal error in GetRecord.");
"Internal error in GetRecord.")); callbacks->OnError(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
...@@ -761,9 +781,12 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params, ...@@ -761,9 +781,12 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
&record_identifier, &record_identifier,
&found); &found);
if (!s.ok()) { if (!s.ok()) {
params->callbacks->OnError( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, "Internal error checking key existence.");
"Internal error checking key existence.")); params->callbacks->OnError(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
if (found) { if (found) {
...@@ -809,9 +832,13 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params, ...@@ -809,9 +832,13 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
params->value, params->value,
&record_identifier); &record_identifier);
if (!s.ok()) { if (!s.ok()) {
params->callbacks->OnError(IndexedDBDatabaseError( IndexedDBDatabaseError error(
blink::WebIDBDatabaseExceptionUnknownError, blink::WebIDBDatabaseExceptionUnknownError,
"Internal error: backing store error performing put/add.")); "Internal error: backing store error performing put/add.");
params->callbacks->OnError(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
...@@ -834,9 +861,12 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params, ...@@ -834,9 +861,12 @@ void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
*key, *key,
!key_was_generated); !key_was_generated);
if (!s.ok()) { if (!s.ok()) {
params->callbacks->OnError( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, "Internal error updating key generator.");
"Internal error updating key generator.")); params->callbacks->OnError(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
} }
...@@ -866,9 +896,12 @@ void IndexedDBDatabase::SetIndexKeys(int64 transaction_id, ...@@ -866,9 +896,12 @@ void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
&record_identifier, &record_identifier,
&found); &found);
if (!s.ok()) { if (!s.ok()) {
transaction->Abort( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, "Internal error setting index keys.");
"Internal error setting index keys.")); transaction->Abort(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
return; return;
} }
if (!found) { if (!found) {
...@@ -1206,8 +1239,12 @@ void IndexedDBDatabase::DeleteObjectStoreOperation( ...@@ -1206,8 +1239,12 @@ void IndexedDBDatabase::DeleteObjectStoreOperation(
base::string16 error_string = base::string16 error_string =
ASCIIToUTF16("Internal error deleting object store '") + ASCIIToUTF16("Internal error deleting object store '") +
object_store_metadata.name + ASCIIToUTF16("'."); object_store_metadata.name + ASCIIToUTF16("'.");
transaction->Abort(IndexedDBDatabaseError( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
blink::WebIDBDatabaseExceptionUnknownError, error_string)); error_string);
transaction->Abort(error);
if (s.IsCorruption())
factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
error);
} }
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "content/browser/indexed_db/indexed_db_backing_store.h" #include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h" #include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_database_error.h"
#include "content/browser/indexed_db/indexed_db_tracing.h" #include "content/browser/indexed_db/indexed_db_tracing.h"
#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h" #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
...@@ -234,6 +235,24 @@ void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) { ...@@ -234,6 +235,24 @@ void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) {
IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE); IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE);
} }
void IndexedDBFactory::HandleBackingStoreCorruption(
const GURL& origin_url,
const IndexedDBDatabaseError& error) {
// Make a copy of origin_url as this is likely a reference to a member of a
// backing store which this function will be deleting.
GURL saved_origin_url(origin_url);
DCHECK(context_);
base::FilePath path_base = context_->data_path();
IndexedDBBackingStore::RecordCorruptionInfo(
path_base, saved_origin_url, base::UTF16ToUTF8(error.message()));
HandleBackingStoreFailure(saved_origin_url);
// Note: DestroyBackingStore only deletes LevelDB files, leaving all others,
// so our corruption info file will remain.
if (!IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url)
.ok())
DLOG(ERROR) << "Unable to delete backing store";
}
bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url, bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url,
const base::string16& name) const { const base::string16& name) const {
......
...@@ -50,12 +50,12 @@ class CONTENT_EXPORT IndexedDBFactory ...@@ -50,12 +50,12 @@ class CONTENT_EXPORT IndexedDBFactory
const base::FilePath& data_directory); const base::FilePath& data_directory);
void HandleBackingStoreFailure(const GURL& origin_url); void HandleBackingStoreFailure(const GURL& origin_url);
void HandleBackingStoreCorruption(const GURL& origin_url,
const IndexedDBDatabaseError& error);
std::pair<OriginDBMapIterator, OriginDBMapIterator> GetOpenDatabasesForOrigin( std::pair<OriginDBMapIterator, OriginDBMapIterator> GetOpenDatabasesForOrigin(
const GURL& origin_url) const; const GURL& origin_url) const;
// Called by IndexedDBContext after all connections are closed, to
// ensure the backing store closed immediately.
void ForceClose(const GURL& origin_url); void ForceClose(const GURL& origin_url);
// Called by the IndexedDBContext destructor so the factory can do cleanup. // Called by the IndexedDBContext destructor so the factory can do cleanup.
......
...@@ -259,10 +259,9 @@ void IndexedDBTransaction::Commit() { ...@@ -259,10 +259,9 @@ void IndexedDBTransaction::Commit() {
while (!abort_task_stack_.empty()) while (!abort_task_stack_.empty())
abort_task_stack_.pop().Run(NULL); abort_task_stack_.pop().Run(NULL);
callbacks_->OnAbort( IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
id_, "Internal error committing transaction.");
IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, callbacks_->OnAbort(id_, error);
"Internal error committing transaction."));
database_->TransactionFinished(this, false); database_->TransactionFinished(this, false);
database_->TransactionCommitFailed(); database_->TransactionCommitFailed();
} }
......
...@@ -459,4 +459,6 @@ void LevelDBDatabase::Compact(const base::StringPiece& start, ...@@ -459,4 +459,6 @@ void LevelDBDatabase::Compact(const base::StringPiece& start,
db_->CompactRange(&start_slice, &stop_slice); db_->CompactRange(&start_slice, &stop_slice);
} }
void LevelDBDatabase::CompactAll() { db_->CompactRange(NULL, NULL); }
} // namespace content } // namespace content
...@@ -87,6 +87,7 @@ class CONTENT_EXPORT LevelDBDatabase { ...@@ -87,6 +87,7 @@ class CONTENT_EXPORT LevelDBDatabase {
scoped_ptr<LevelDBIterator> CreateIterator(const LevelDBSnapshot* = 0); scoped_ptr<LevelDBIterator> CreateIterator(const LevelDBSnapshot* = 0);
const LevelDBComparator* Comparator() const; const LevelDBComparator* Comparator() const;
void Compact(const base::StringPiece& start, const base::StringPiece& stop); void Compact(const base::StringPiece& start, const base::StringPiece& stop);
void CompactAll();
protected: protected:
LevelDBDatabase(); LevelDBDatabase();
......
...@@ -147,3 +147,9 @@ function indexedDBTest(upgradeCallback, optionalOpenCallback) { ...@@ -147,3 +147,9 @@ function indexedDBTest(upgradeCallback, optionalOpenCallback) {
openRequest.onsuccess = optionalOpenCallback; openRequest.onsuccess = optionalOpenCallback;
}; };
} }
if (typeof String.prototype.startsWith !== 'function') {
String.prototype.startsWith = function (str) {
return this.indexOf(str) === 0;
};
}
<!DOCTYPE html>
<html>
<!--
Copyright 2014 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.
-->
<head>
<title>IDB test that db's corrupted while open are properly handled Part 1 / 2</title>
<script type="text/javascript" src="common.js"></script>
<script>
function test() {
indexedDBTest(upgradeCallback, openCallback);
}
var numObjectsWrittenToDb = 500;
var numTransactions = 0;
var numTransactionErrors = 0;
var numTransactionAborts = 0;
var numKeys = 0;
var transaction;
var request;
var db;
var objectStore;
function upgradeCallback() {
db = event.target.result;
deleteAllObjectStores(db);
objectStore = db.createObjectStore('storeName', { autoIncrement : true });
var i;
var len = 80;
var data = Array(len);
for (i = 0; i < len; ++i) {
data[i] = i;
}
for (i = 0; i < numObjectsWrittenToDb; ++i) {
var key = 'key-' + i;
request = objectStore.add(data, key);
request.onerror = unexpectedErrorCallback;
request.onsuccess = upgradeTransactionComplete;
}
}
function upgradeTransactionComplete() {
++numTransactions;
if (numTransactions === numObjectsWrittenToDb) {
debug("All transactions written");
}
}
function transactionError(event) {
if (event.target.error) {
numTransactionErrors += 1;
} else {
fail("Transaction onerror had no error");
}
}
function transactionAbort() {
if (event.target.error) {
numTransactionAborts += 1;
} else {
fail("Transaction onabort had no error");
}
}
function requestError(event) {
if (!event.target.error) {
fail("get request had no/invalid error");
}
}
function databaseClosed(event) {
shouldBe("numTransactionErrors", "1");
shouldBe("numTransactionAborts", "1");
done("Closed as expected");
}
function getData() {
transaction = db.transaction('storeName');
db.onclose = databaseClosed;
transaction.onabort = transactionAbort;
transaction.onerror = transactionError;
request.oncomplete = unexpectedCompleteCallback;
store = transaction.objectStore('storeName');
request = store.get('key-0');
request.onsuccess = unexpectedSuccessCallback;
request.onerror = requestError;
}
function openCallback() {
var xmlhttp = new window.XMLHttpRequest();
xmlhttp.open("GET", "/corrupt/test/corruptdb?storeName", false /*sync*/);
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === 4) {
if (xmlhttp.status === 200) {
// The database is now corrupt.
getData();
}
}
};
xmlhttp.send();
}
</script>
</head>
<body onLoad="test()">
<div id="status">Starting...</div>
</body>
</html>
<!DOCTYPE html>
<html>
<!--
Copyright 2014 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.
-->
<head>
<title>IDB test that db's corrupted while open are properly handled Part 2 / 2</title>
<script type="text/javascript" src="common.js"></script>
<script>
function test() {
dbname = "corrupted_open_db_detection.html";
request = webkitIndexedDB.open(dbname);
request.onupgradeneeded = upgradeNeeded;
request.onsuccess = onSuccess;
request.onerror = unexpectedErrorCallback;
request.onblocked = unexpectedBlockedCallback;
}
var gotUpgradeNeeded = false;
function upgradeNeeded(evt) {
event = evt;
shouldBe("event.dataLoss", "'total'");
shouldBeTrue('event.dataLossMessage.startsWith("IndexedDB (database was corrupt): ")');
gotUpgradeNeeded = true;
}
function onSuccess(event) {
db = event.target.result;
debug("The pre-existing leveldb has an objectStore in 'database-basics',");
debug("ensure that it was blown away");
shouldBe("db.objectStoreNames.length", "0");
debug("We should have gotten an upgradeneeded event because the new empty");
debug("database doesn't have a version.");
shouldBeTrue("gotUpgradeNeeded");
done();
}
</script>
</head>
<body onLoad="test()">
<div id="status">Starting...</div>
</body>
</html>
...@@ -34020,6 +34020,9 @@ other types of suffix sets. ...@@ -34020,6 +34020,9 @@ other types of suffix sets.
An open attempt failed with an I/O error that doesn't necessitate a recovery An open attempt failed with an I/O error that doesn't necessitate a recovery
attempt. attempt.
</int> </int>
<int value="14" label="OpenAttemptPriorCorruption">
The corrupted open database was deleted.
</int>
</enum> </enum>
<enum name="ImporterType" type="int"> <enum name="ImporterType" type="int">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment