| // Copyright 2015 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 "chrome/browser/extensions/app_data_migrator.h" |
| |
| #include "base/files/file_util.h" |
| #include "base/memory/weak_ptr.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/indexed_db_context.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/extension.h" |
| #include "storage/browser/fileapi/file_system_context.h" |
| #include "storage/browser/fileapi/sandbox_file_system_backend_delegate.h" |
| #include "storage/common/fileapi/file_system_types.h" |
| |
| using base::WeakPtr; |
| using content::BrowserContext; |
| using content::BrowserThread; |
| using content::IndexedDBContext; |
| using content::StoragePartition; |
| using storage::FileSystemContext; |
| using storage::SandboxFileSystemBackendDelegate; |
| |
| namespace { |
| |
| void MigrateOnFileSystemThread(FileSystemContext* old_fs_context, |
| FileSystemContext* fs_context, |
| const extensions::Extension* extension) { |
| DCHECK( |
| old_fs_context->default_file_task_runner()->RunsTasksOnCurrentThread()); |
| |
| SandboxFileSystemBackendDelegate* old_sandbox_delegate = |
| old_fs_context->sandbox_delegate(); |
| SandboxFileSystemBackendDelegate* sandbox_delegate = |
| fs_context->sandbox_delegate(); |
| |
| GURL extension_url = |
| extensions::Extension::GetBaseURLFromExtensionId(extension->id()); |
| |
| std::unique_ptr<storage::SandboxFileSystemBackendDelegate::OriginEnumerator> |
| enumerator(old_sandbox_delegate->CreateOriginEnumerator()); |
| |
| // Find out if there is a file system that needs migration. |
| GURL origin; |
| do { |
| origin = enumerator->Next(); |
| } while (origin != extension_url && !origin.is_empty()); |
| |
| if (!origin.is_empty()) { |
| // Copy the temporary file system. |
| if (enumerator->HasFileSystemType(storage::kFileSystemTypeTemporary)) { |
| old_sandbox_delegate->CopyFileSystem( |
| extension_url, storage::kFileSystemTypeTemporary, sandbox_delegate); |
| } |
| // Copy the persistent file system. |
| if (enumerator->HasFileSystemType(storage::kFileSystemTypePersistent)) { |
| old_sandbox_delegate->CopyFileSystem( |
| extension_url, storage::kFileSystemTypePersistent, sandbox_delegate); |
| } |
| } |
| } |
| |
| void MigrateOnIndexedDBThread(IndexedDBContext* old_indexed_db_context, |
| IndexedDBContext* indexed_db_context, |
| const extensions::Extension* extension) { |
| DCHECK(old_indexed_db_context->TaskRunner()->RunsTasksOnCurrentThread()); |
| |
| GURL extension_url = |
| extensions::Extension::GetBaseURLFromExtensionId(extension->id()); |
| |
| old_indexed_db_context->CopyOriginData(extension_url, indexed_db_context); |
| } |
| |
| void MigrateFileSystem(WeakPtr<extensions::AppDataMigrator> migrator, |
| StoragePartition* old_partition, |
| StoragePartition* current_partition, |
| const extensions::Extension* extension, |
| const base::Closure& reply) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // Since this method is static and it's being run as a closure task, check to |
| // make sure the calling object is still around. |
| if (!migrator.get()) { |
| return; |
| } |
| |
| FileSystemContext* old_fs_context = old_partition->GetFileSystemContext(); |
| FileSystemContext* fs_context = current_partition->GetFileSystemContext(); |
| |
| // Perform the file system migration on the old file system's |
| // sequenced task runner. This is to ensure it queues after any |
| // in-flight file system operations. After it completes, it should |
| // invoke the original callback passed into DoMigrationAndReply. |
| old_fs_context->default_file_task_runner()->PostTaskAndReply( |
| FROM_HERE, |
| base::Bind(&MigrateOnFileSystemThread, base::RetainedRef(old_fs_context), |
| base::RetainedRef(fs_context), base::RetainedRef(extension)), |
| reply); |
| } |
| |
| void MigrateLegacyPartition(WeakPtr<extensions::AppDataMigrator> migrator, |
| StoragePartition* old_partition, |
| StoragePartition* current_partition, |
| const extensions::Extension* extension, |
| const base::Closure& reply) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| IndexedDBContext* indexed_db_context = |
| current_partition->GetIndexedDBContext(); |
| IndexedDBContext* old_indexed_db_context = |
| old_partition->GetIndexedDBContext(); |
| |
| // Create a closure for the file system migration. This is the next step in |
| // the migration flow after the IndexedDB migration. |
| base::Closure migrate_fs = |
| base::Bind(&MigrateFileSystem, migrator, old_partition, current_partition, |
| base::RetainedRef(extension), reply); |
| |
| // Perform the IndexedDB migration on the old context's sequenced task |
| // runner. After completion, it should call MigrateFileSystem. |
| old_indexed_db_context->TaskRunner()->PostTaskAndReply( |
| FROM_HERE, |
| base::Bind( |
| &MigrateOnIndexedDBThread, base::RetainedRef(old_indexed_db_context), |
| base::RetainedRef(indexed_db_context), base::RetainedRef(extension)), |
| migrate_fs); |
| } |
| |
| } // namespace |
| |
| namespace extensions { |
| |
| AppDataMigrator::AppDataMigrator(Profile* profile, ExtensionRegistry* registry) |
| : profile_(profile), registry_(registry), weak_factory_(this) { |
| } |
| |
| AppDataMigrator::~AppDataMigrator() { |
| } |
| |
| bool AppDataMigrator::NeedsMigration(const Extension* old, |
| const Extension* extension) { |
| return old && old->is_legacy_packaged_app() && extension->is_platform_app(); |
| } |
| |
| void AppDataMigrator::DoMigrationAndReply(const Extension* old, |
| const Extension* extension, |
| const base::Closure& reply) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(NeedsMigration(old, extension)); |
| |
| // This should retrieve the general storage partition. |
| content::StoragePartition* old_partition = |
| BrowserContext::GetStoragePartitionForSite( |
| profile_, Extension::GetBaseURLFromExtensionId(extension->id())); |
| |
| // Enable the new extension so we can access its storage partition. |
| bool old_was_disabled = registry_->AddEnabled(extension); |
| |
| // This should create a new isolated partition for the new version of the |
| // extension. |
| StoragePartition* new_partition = BrowserContext::GetStoragePartitionForSite( |
| profile_, Extension::GetBaseURLFromExtensionId(extension->id())); |
| |
| // Now, restore the enabled/disabled state of the new and old extensions. |
| if (old_was_disabled) |
| registry_->RemoveEnabled(extension->id()); |
| else |
| registry_->AddEnabled(old); |
| |
| MigrateLegacyPartition(weak_factory_.GetWeakPtr(), old_partition, |
| new_partition, extension, reply); |
| } |
| |
| } // namespace extensions |