summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHiroki Nakagawa <nhiroki@chromium.org>2015-06-29 10:59:38 +0900
committerHiroki Nakagawa <nhiroki@chromium.org>2015-06-29 02:00:31 +0000
commit9f41c103c37353271e239a5c91f316796875dd50 (patch)
tree84fb097e6ee0ff565338cd695db174b2d1233cdf
parentac2b37ff8ca0f3015c0163bc91b456e33b7287f3 (diff)
downloadchromium_src-9f41c103c37353271e239a5c91f316796875dd50.zip
chromium_src-9f41c103c37353271e239a5c91f316796875dd50.tar.gz
chromium_src-9f41c103c37353271e239a5c91f316796875dd50.tar.bz2
[Merge] ServiceWorker: Directly delete database directory instead of leveldb::DestroyDB
When a database directory contains files not related to LevelDB, DestroyDB() does not delete the directory and leaves those files. This non-empty database directory infinitely fails ServiceWorkerDatabase::LazyOpen during a session. (See my comment #24 in the issue for more details). To avoid that, this CL directly deletes the directory instead of DestroyDB. BUG=468926 TEST=content_unittests --gtest_filter=ServiceWorkerResourceStorageDiskTest.DeleteAndStartOver* TBR=falken@chromium.org Review URL: https://codereview.chromium.org/1176203007 Cr-Commit-Position: refs/heads/master@{#335893} (cherry picked from commit 0bd5fd70714ca1ae846be5b657fd3a21f49884c1) Review URL: https://codereview.chromium.org/1214793006. Cr-Commit-Position: refs/branch-heads/2403@{#413} Cr-Branched-From: f54b8097a9c45ed4ad308133d49f05325d6c5070-refs/heads/master@{#330231}
-rw-r--r--content/browser/service_worker/service_worker_context_unittest.cc176
-rw-r--r--content/browser/service_worker/service_worker_database.cc31
-rw-r--r--content/browser/service_worker/service_worker_database.h4
-rw-r--r--content/browser/service_worker/service_worker_storage.cc6
-rw-r--r--content/browser/service_worker/service_worker_storage.h6
-rw-r--r--content/browser/service_worker/service_worker_storage_unittest.cc84
6 files changed, 206 insertions, 101 deletions
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 16a3233..ae52dee 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -520,16 +520,105 @@ TEST_F(ServiceWorkerContextTest, RegisterDuplicateScript) {
EXPECT_EQ(old_registration_id, notifications_[1].registration_id);
}
-TEST_F(ServiceWorkerContextTest, DeleteAndStartOver) {
+TEST_F(ServiceWorkerContextTest, ProviderHostIterator) {
+ const int kRenderProcessId1 = 1;
+ const int kRenderProcessId2 = 2;
+ const GURL kOrigin1 = GURL("http://www.example.com/");
+ const GURL kOrigin2 = GURL("https://www.example.com/");
+ int provider_id = 1;
+
+ // Host1 (provider_id=1): process_id=1, origin1.
+ ServiceWorkerProviderHost* host1(new ServiceWorkerProviderHost(
+ kRenderProcessId1, MSG_ROUTING_NONE, provider_id++,
+ SERVICE_WORKER_PROVIDER_FOR_WINDOW, context()->AsWeakPtr(), nullptr));
+ host1->SetDocumentUrl(kOrigin1);
+
+ // Host2 (provider_id=2): process_id=2, origin2.
+ ServiceWorkerProviderHost* host2(new ServiceWorkerProviderHost(
+ kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
+ SERVICE_WORKER_PROVIDER_FOR_WINDOW, context()->AsWeakPtr(), nullptr));
+ host2->SetDocumentUrl(kOrigin2);
+
+ // Host3 (provider_id=3): process_id=2, origin1.
+ ServiceWorkerProviderHost* host3(new ServiceWorkerProviderHost(
+ kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
+ SERVICE_WORKER_PROVIDER_FOR_WINDOW, context()->AsWeakPtr(), nullptr));
+ host3->SetDocumentUrl(kOrigin1);
+
+ // Host4 (provider_id=4): process_id=2, origin2, for ServiceWorker.
+ ServiceWorkerProviderHost* host4(new ServiceWorkerProviderHost(
+ kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
+ SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, context()->AsWeakPtr(), nullptr));
+ host4->SetDocumentUrl(kOrigin2);
+
+ context()->AddProviderHost(make_scoped_ptr(host1));
+ context()->AddProviderHost(make_scoped_ptr(host2));
+ context()->AddProviderHost(make_scoped_ptr(host3));
+ context()->AddProviderHost(make_scoped_ptr(host4));
+
+ // Iterate over all provider hosts.
+ std::set<ServiceWorkerProviderHost*> results;
+ for (auto it = context()->GetProviderHostIterator(); !it->IsAtEnd();
+ it->Advance()) {
+ results.insert(it->GetProviderHost());
+ }
+ EXPECT_EQ(4u, results.size());
+ EXPECT_TRUE(ContainsKey(results, host1));
+ EXPECT_TRUE(ContainsKey(results, host2));
+ EXPECT_TRUE(ContainsKey(results, host3));
+ EXPECT_TRUE(ContainsKey(results, host4));
+
+ // Iterate over the client provider hosts that belong to kOrigin1.
+ results.clear();
+ for (auto it = context()->GetClientProviderHostIterator(kOrigin1);
+ !it->IsAtEnd(); it->Advance()) {
+ results.insert(it->GetProviderHost());
+ }
+ EXPECT_EQ(2u, results.size());
+ EXPECT_TRUE(ContainsKey(results, host1));
+ EXPECT_TRUE(ContainsKey(results, host3));
+
+ // Iterate over the provider hosts that belong to kOrigin2.
+ // (This should not include host4 as it's not for controllee.)
+ results.clear();
+ for (auto it = context()->GetClientProviderHostIterator(kOrigin2);
+ !it->IsAtEnd(); it->Advance()) {
+ results.insert(it->GetProviderHost());
+ }
+ EXPECT_EQ(1u, results.size());
+ EXPECT_TRUE(ContainsKey(results, host2));
+
+ context()->RemoveProviderHost(kRenderProcessId1, 1);
+ context()->RemoveProviderHost(kRenderProcessId2, 2);
+ context()->RemoveProviderHost(kRenderProcessId2, 3);
+ context()->RemoveProviderHost(kRenderProcessId2, 4);
+}
+
+class ServiceWorkerContextRecoveryTest
+ : public ServiceWorkerContextTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ ServiceWorkerContextRecoveryTest() {}
+ virtual ~ServiceWorkerContextRecoveryTest() {}
+};
+
+INSTANTIATE_TEST_CASE_P(ServiceWorkerContextRecoveryTest,
+ ServiceWorkerContextRecoveryTest,
+ testing::Values(true, false));
+
+TEST_P(ServiceWorkerContextRecoveryTest, DeleteAndStartOver) {
GURL pattern("http://www.example.com/");
GURL script_url("http://www.example.com/service_worker.js");
- // Reinitialize the helper to test on-disk storage.
- base::ScopedTempDir user_data_directory;
- ASSERT_TRUE(user_data_directory.CreateUniqueTempDir());
- helper_.reset(new EmbeddedWorkerTestHelper(user_data_directory.path(),
- render_process_id_));
- helper_->context_wrapper()->AddObserver(this);
+ bool is_storage_on_disk = GetParam();
+ if (is_storage_on_disk) {
+ // Reinitialize the helper to test on-disk storage.
+ base::ScopedTempDir user_data_directory;
+ ASSERT_TRUE(user_data_directory.CreateUniqueTempDir());
+ helper_.reset(new EmbeddedWorkerTestHelper(user_data_directory.path(),
+ render_process_id_));
+ helper_->context_wrapper()->AddObserver(this);
+ }
int64 registration_id = kInvalidServiceWorkerRegistrationId;
bool called = false;
@@ -614,78 +703,5 @@ TEST_F(ServiceWorkerContextTest, DeleteAndStartOver) {
EXPECT_EQ(registration_id, notifications_[2].registration_id);
}
-TEST_F(ServiceWorkerContextTest, ProviderHostIterator) {
- const int kRenderProcessId1 = 1;
- const int kRenderProcessId2 = 2;
- const GURL kOrigin1 = GURL("http://www.example.com/");
- const GURL kOrigin2 = GURL("https://www.example.com/");
- int provider_id = 1;
-
- // Host1 (provider_id=1): process_id=1, origin1.
- ServiceWorkerProviderHost* host1(new ServiceWorkerProviderHost(
- kRenderProcessId1, MSG_ROUTING_NONE, provider_id++,
- SERVICE_WORKER_PROVIDER_FOR_WINDOW, context()->AsWeakPtr(), nullptr));
- host1->SetDocumentUrl(kOrigin1);
-
- // Host2 (provider_id=2): process_id=2, origin2.
- ServiceWorkerProviderHost* host2(new ServiceWorkerProviderHost(
- kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
- SERVICE_WORKER_PROVIDER_FOR_WINDOW, context()->AsWeakPtr(), nullptr));
- host2->SetDocumentUrl(kOrigin2);
-
- // Host3 (provider_id=3): process_id=2, origin1.
- ServiceWorkerProviderHost* host3(new ServiceWorkerProviderHost(
- kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
- SERVICE_WORKER_PROVIDER_FOR_WINDOW, context()->AsWeakPtr(), nullptr));
- host3->SetDocumentUrl(kOrigin1);
-
- // Host4 (provider_id=4): process_id=2, origin2, for ServiceWorker.
- ServiceWorkerProviderHost* host4(new ServiceWorkerProviderHost(
- kRenderProcessId2, MSG_ROUTING_NONE, provider_id++,
- SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, context()->AsWeakPtr(), nullptr));
- host4->SetDocumentUrl(kOrigin2);
-
- context()->AddProviderHost(make_scoped_ptr(host1));
- context()->AddProviderHost(make_scoped_ptr(host2));
- context()->AddProviderHost(make_scoped_ptr(host3));
- context()->AddProviderHost(make_scoped_ptr(host4));
-
- // Iterate over all provider hosts.
- std::set<ServiceWorkerProviderHost*> results;
- for (auto it = context()->GetProviderHostIterator(); !it->IsAtEnd();
- it->Advance()) {
- results.insert(it->GetProviderHost());
- }
- EXPECT_EQ(4u, results.size());
- EXPECT_TRUE(ContainsKey(results, host1));
- EXPECT_TRUE(ContainsKey(results, host2));
- EXPECT_TRUE(ContainsKey(results, host3));
- EXPECT_TRUE(ContainsKey(results, host4));
-
- // Iterate over the client provider hosts that belong to kOrigin1.
- results.clear();
- for (auto it = context()->GetClientProviderHostIterator(kOrigin1);
- !it->IsAtEnd(); it->Advance()) {
- results.insert(it->GetProviderHost());
- }
- EXPECT_EQ(2u, results.size());
- EXPECT_TRUE(ContainsKey(results, host1));
- EXPECT_TRUE(ContainsKey(results, host3));
-
- // Iterate over the provider hosts that belong to kOrigin2.
- // (This should not include host4 as it's not for controllee.)
- results.clear();
- for (auto it = context()->GetClientProviderHostIterator(kOrigin2);
- !it->IsAtEnd(); it->Advance()) {
- results.insert(it->GetProviderHost());
- }
- EXPECT_EQ(1u, results.size());
- EXPECT_TRUE(ContainsKey(results, host2));
-
- context()->RemoveProviderHost(kRenderProcessId1, 1);
- context()->RemoveProviderHost(kRenderProcessId2, 2);
- context()->RemoveProviderHost(kRenderProcessId2, 3);
- context()->RemoveProviderHost(kRenderProcessId2, 4);
-}
} // namespace content
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index c928c38..921bf77 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -970,18 +970,17 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::DestroyDatabase() {
DCHECK(sequence_checker_.CalledOnValidSequencedThread());
Disable(FROM_HERE, STATUS_OK);
- leveldb::Options options;
- if (path_.empty()) {
- if (env_) {
- options.env = env_.get();
- } else {
- // In-memory database not initialized.
- return STATUS_OK;
- }
+ if (IsDatabaseInMemory()) {
+ env_.reset();
+ return STATUS_OK;
}
- Status status =
- LevelDBStatusToStatus(leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
+ // Directly delete the database directory instead of leveldb::DestroyDB()
+ // because the API does not delete the directory if there are unrelated files.
+ // (https://code.google.com/p/chromium/issues/detail?id=468926#c24)
+ Status status = base::DeleteFile(path_, true /* recursive */)
+ ? STATUS_OK
+ : STATUS_ERROR_FAILED;
ServiceWorkerMetrics::RecordDestroyDatabaseResult(status);
return status;
}
@@ -996,13 +995,9 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
if (IsOpen())
return STATUS_OK;
- // When |path_| is empty, open a database in-memory.
- bool use_in_memory_db = path_.empty();
-
if (!create_if_missing) {
// Avoid opening a database if it does not exist at the |path_|.
- if (use_in_memory_db ||
- !base::PathExists(path_) ||
+ if (IsDatabaseInMemory() || !base::PathExists(path_) ||
base::IsDirectoryEmpty(path_)) {
return STATUS_ERROR_NOT_FOUND;
}
@@ -1011,7 +1006,7 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen(
leveldb::Options options;
options.create_if_missing = create_if_missing;
options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
- if (use_in_memory_db) {
+ if (IsDatabaseInMemory()) {
env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
options.env = env_.get();
}
@@ -1463,4 +1458,8 @@ void ServiceWorkerDatabase::HandleWriteResult(
ServiceWorkerMetrics::CountWriteDatabaseResult(status);
}
+bool ServiceWorkerDatabase::IsDatabaseInMemory() const {
+ return path_.empty();
+}
+
} // namespace content
diff --git a/content/browser/service_worker/service_worker_database.h b/content/browser/service_worker/service_worker_database.h
index 13c0ed4..241a8b1 100644
--- a/content/browser/service_worker/service_worker_database.h
+++ b/content/browser/service_worker/service_worker_database.h
@@ -350,7 +350,7 @@ class CONTENT_EXPORT ServiceWorkerDatabase {
const tracked_objects::Location& from_here,
Status status);
- base::FilePath path_;
+ const base::FilePath path_;
scoped_ptr<leveldb::Env> env_;
scoped_ptr<leveldb::DB> db_;
@@ -365,6 +365,8 @@ class CONTENT_EXPORT ServiceWorkerDatabase {
};
State state_;
+ bool IsDatabaseInMemory() const;
+
base::SequenceChecker sequence_checker_;
FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, OpenDatabase);
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index c5c6157..6671523 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -825,13 +825,11 @@ void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
// Delete the database on the database thread.
PostTaskAndReplyWithResult(
- database_task_manager_->GetTaskRunner(),
- FROM_HERE,
+ database_task_manager_->GetTaskRunner(), FROM_HERE,
base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
base::Unretained(database_.get())),
base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
- weak_factory_.GetWeakPtr(),
- callback));
+ weak_factory_.GetWeakPtr(), callback));
}
int64 ServiceWorkerStorage::NewRegistrationId() {
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index 1bfa737..b175d42 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -231,6 +231,12 @@ class CONTENT_EXPORT ServiceWorkerStorage
CleanupOnRestart);
FRIEND_TEST_ALL_PREFIXES(ServiceWorkerResourceStorageDiskTest,
ClearOnExit);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerResourceStorageDiskTest,
+ DeleteAndStartOver);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerResourceStorageDiskTest,
+ DeleteAndStartOver_UnrelatedFileExists);
+ FRIEND_TEST_ALL_PREFIXES(ServiceWorkerResourceStorageDiskTest,
+ DeleteAndStartOver_OpenedFileExists);
struct InitialData {
int64 next_registration_id;
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index aa2ceb5..bdfbe18 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -4,6 +4,7 @@
#include <string>
+#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/run_loop.h"
@@ -35,6 +36,13 @@ namespace {
typedef ServiceWorkerDatabase::RegistrationData RegistrationData;
typedef ServiceWorkerDatabase::ResourceRecord ResourceRecord;
+void StatusAndQuitCallback(ServiceWorkerStatusCode* result,
+ const base::Closure& quit_closure,
+ ServiceWorkerStatusCode status) {
+ *result = status;
+ quit_closure.Run();
+}
+
void StatusCallback(bool* was_called,
ServiceWorkerStatusCode* result,
ServiceWorkerStatusCode status) {
@@ -1241,6 +1249,82 @@ TEST_F(ServiceWorkerResourceStorageDiskTest, CleanupOnRestart) {
EXPECT_TRUE(VerifyBasicResponse(storage(), kNewResourceId, true));
}
+TEST_F(ServiceWorkerResourceStorageDiskTest, DeleteAndStartOver) {
+ EXPECT_FALSE(storage()->IsDisabled());
+ ASSERT_TRUE(base::DirectoryExists(storage()->GetDiskCachePath()));
+ ASSERT_TRUE(base::DirectoryExists(storage()->GetDatabasePath()));
+
+ base::RunLoop run_loop;
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_ABORT;
+ storage()->DeleteAndStartOver(
+ base::Bind(&StatusAndQuitCallback, &status, run_loop.QuitClosure()));
+ run_loop.Run();
+
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_TRUE(storage()->IsDisabled());
+ EXPECT_FALSE(base::DirectoryExists(storage()->GetDiskCachePath()));
+ EXPECT_FALSE(base::DirectoryExists(storage()->GetDatabasePath()));
+}
+
+TEST_F(ServiceWorkerResourceStorageDiskTest,
+ DeleteAndStartOver_UnrelatedFileExists) {
+ EXPECT_FALSE(storage()->IsDisabled());
+ ASSERT_TRUE(base::DirectoryExists(storage()->GetDiskCachePath()));
+ ASSERT_TRUE(base::DirectoryExists(storage()->GetDatabasePath()));
+
+ // Create an unrelated file in the database directory to make sure such a file
+ // does not prevent DeleteAndStartOver.
+ base::FilePath file_path;
+ ASSERT_TRUE(
+ base::CreateTemporaryFileInDir(storage()->GetDatabasePath(), &file_path));
+ ASSERT_TRUE(base::PathExists(file_path));
+
+ base::RunLoop run_loop;
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_ABORT;
+ storage()->DeleteAndStartOver(
+ base::Bind(&StatusAndQuitCallback, &status, run_loop.QuitClosure()));
+ run_loop.Run();
+
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_TRUE(storage()->IsDisabled());
+ EXPECT_FALSE(base::DirectoryExists(storage()->GetDiskCachePath()));
+ EXPECT_FALSE(base::DirectoryExists(storage()->GetDatabasePath()));
+}
+
+TEST_F(ServiceWorkerResourceStorageDiskTest,
+ DeleteAndStartOver_OpenedFileExists) {
+ EXPECT_FALSE(storage()->IsDisabled());
+ ASSERT_TRUE(base::DirectoryExists(storage()->GetDiskCachePath()));
+ ASSERT_TRUE(base::DirectoryExists(storage()->GetDatabasePath()));
+
+ // Create an unrelated opened file in the database directory to make sure such
+ // a file does not prevent DeleteAndStartOver on non-Windows platforms.
+ base::FilePath file_path;
+ base::ScopedFILE file(base::CreateAndOpenTemporaryFileInDir(
+ storage()->GetDatabasePath(), &file_path));
+ ASSERT_TRUE(file);
+ ASSERT_TRUE(base::PathExists(file_path));
+
+ base::RunLoop run_loop;
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_ABORT;
+ storage()->DeleteAndStartOver(
+ base::Bind(&StatusAndQuitCallback, &status, run_loop.QuitClosure()));
+ run_loop.Run();
+
+#if defined(OS_WIN)
+ // On Windows, deleting the directory containing an opened file should fail.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_FAILED, status);
+ EXPECT_TRUE(storage()->IsDisabled());
+ EXPECT_TRUE(base::DirectoryExists(storage()->GetDiskCachePath()));
+ EXPECT_TRUE(base::DirectoryExists(storage()->GetDatabasePath()));
+#else
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_TRUE(storage()->IsDisabled());
+ EXPECT_FALSE(base::DirectoryExists(storage()->GetDiskCachePath()));
+ EXPECT_FALSE(base::DirectoryExists(storage()->GetDatabasePath()));
+#endif
+}
+
TEST_F(ServiceWorkerResourceStorageTest, UpdateRegistration) {
// Promote the worker to active worker and add a controllee.
registration_->SetActiveVersion(registration_->waiting_version());