// 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()); scoped_ptr 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 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, make_scoped_refptr(old_fs_context), make_scoped_refptr(fs_context), make_scoped_refptr(extension)), reply); } void MigrateLegacyPartition(WeakPtr 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, make_scoped_refptr(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, make_scoped_refptr(old_indexed_db_context), make_scoped_refptr(indexed_db_context), make_scoped_refptr(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