ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 1 | // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "chrome/browser/extensions/app_data_migrator.h" |
| 6 | |
| 7 | #include "base/files/file_util.h" |
| 8 | #include "base/memory/weak_ptr.h" |
| 9 | #include "chrome/browser/profiles/profile.h" |
| 10 | #include "content/public/browser/browser_context.h" |
| 11 | #include "content/public/browser/browser_thread.h" |
| 12 | #include "content/public/browser/indexed_db_context.h" |
| 13 | #include "content/public/browser/storage_partition.h" |
| 14 | #include "extensions/browser/extension_registry.h" |
| 15 | #include "extensions/common/extension.h" |
| 16 | #include "storage/browser/fileapi/file_system_context.h" |
| 17 | #include "storage/browser/fileapi/sandbox_file_system_backend_delegate.h" |
| 18 | #include "storage/common/fileapi/file_system_types.h" |
| 19 | |
| 20 | using base::WeakPtr; |
| 21 | using content::BrowserContext; |
| 22 | using content::BrowserThread; |
| 23 | using content::IndexedDBContext; |
| 24 | using content::StoragePartition; |
| 25 | using storage::FileSystemContext; |
| 26 | using storage::SandboxFileSystemBackendDelegate; |
| 27 | |
| 28 | namespace { |
| 29 | |
| 30 | void MigrateOnFileSystemThread(FileSystemContext* old_fs_context, |
| 31 | FileSystemContext* fs_context, |
| 32 | const extensions::Extension* extension) { |
| 33 | DCHECK( |
peary2 | be58808 | 2017-05-17 01:59:49 | [diff] [blame] | 34 | old_fs_context->default_file_task_runner()->RunsTasksInCurrentSequence()); |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 35 | |
| 36 | SandboxFileSystemBackendDelegate* old_sandbox_delegate = |
| 37 | old_fs_context->sandbox_delegate(); |
| 38 | SandboxFileSystemBackendDelegate* sandbox_delegate = |
| 39 | fs_context->sandbox_delegate(); |
| 40 | |
| 41 | GURL extension_url = |
| 42 | extensions::Extension::GetBaseURLFromExtensionId(extension->id()); |
| 43 | |
dcheng | c963c714 | 2016-04-08 03:55:22 | [diff] [blame] | 44 | std::unique_ptr<storage::SandboxFileSystemBackendDelegate::OriginEnumerator> |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 45 | enumerator(old_sandbox_delegate->CreateOriginEnumerator()); |
| 46 | |
| 47 | // Find out if there is a file system that needs migration. |
| 48 | GURL origin; |
| 49 | do { |
| 50 | origin = enumerator->Next(); |
| 51 | } while (origin != extension_url && !origin.is_empty()); |
| 52 | |
| 53 | if (!origin.is_empty()) { |
| 54 | // Copy the temporary file system. |
| 55 | if (enumerator->HasFileSystemType(storage::kFileSystemTypeTemporary)) { |
| 56 | old_sandbox_delegate->CopyFileSystem( |
| 57 | extension_url, storage::kFileSystemTypeTemporary, sandbox_delegate); |
| 58 | } |
| 59 | // Copy the persistent file system. |
| 60 | if (enumerator->HasFileSystemType(storage::kFileSystemTypePersistent)) { |
| 61 | old_sandbox_delegate->CopyFileSystem( |
| 62 | extension_url, storage::kFileSystemTypePersistent, sandbox_delegate); |
| 63 | } |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | void MigrateOnIndexedDBThread(IndexedDBContext* old_indexed_db_context, |
| 68 | IndexedDBContext* indexed_db_context, |
| 69 | const extensions::Extension* extension) { |
peary2 | be58808 | 2017-05-17 01:59:49 | [diff] [blame] | 70 | DCHECK(old_indexed_db_context->TaskRunner()->RunsTasksInCurrentSequence()); |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 71 | |
| 72 | GURL extension_url = |
| 73 | extensions::Extension::GetBaseURLFromExtensionId(extension->id()); |
| 74 | |
| 75 | old_indexed_db_context->CopyOriginData(extension_url, indexed_db_context); |
| 76 | } |
| 77 | |
| 78 | void MigrateFileSystem(WeakPtr<extensions::AppDataMigrator> migrator, |
| 79 | StoragePartition* old_partition, |
| 80 | StoragePartition* current_partition, |
| 81 | const extensions::Extension* extension, |
| 82 | const base::Closure& reply) { |
anujk.sharma | f2a0b4e | 2015-04-24 17:53:29 | [diff] [blame] | 83 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 84 | |
| 85 | // Since this method is static and it's being run as a closure task, check to |
| 86 | // make sure the calling object is still around. |
| 87 | if (!migrator.get()) { |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | FileSystemContext* old_fs_context = old_partition->GetFileSystemContext(); |
| 92 | FileSystemContext* fs_context = current_partition->GetFileSystemContext(); |
| 93 | |
| 94 | // Perform the file system migration on the old file system's |
| 95 | // sequenced task runner. This is to ensure it queues after any |
| 96 | // in-flight file system operations. After it completes, it should |
| 97 | // invoke the original callback passed into DoMigrationAndReply. |
| 98 | old_fs_context->default_file_task_runner()->PostTaskAndReply( |
| 99 | FROM_HERE, |
tzik | 8d880ee | 2017-04-20 19:46:24 | [diff] [blame] | 100 | base::BindOnce( |
| 101 | &MigrateOnFileSystemThread, base::RetainedRef(old_fs_context), |
| 102 | base::RetainedRef(fs_context), base::RetainedRef(extension)), |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 103 | reply); |
| 104 | } |
| 105 | |
| 106 | void MigrateLegacyPartition(WeakPtr<extensions::AppDataMigrator> migrator, |
| 107 | StoragePartition* old_partition, |
| 108 | StoragePartition* current_partition, |
| 109 | const extensions::Extension* extension, |
| 110 | const base::Closure& reply) { |
anujk.sharma | f2a0b4e | 2015-04-24 17:53:29 | [diff] [blame] | 111 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 112 | |
| 113 | IndexedDBContext* indexed_db_context = |
| 114 | current_partition->GetIndexedDBContext(); |
| 115 | IndexedDBContext* old_indexed_db_context = |
| 116 | old_partition->GetIndexedDBContext(); |
| 117 | |
| 118 | // Create a closure for the file system migration. This is the next step in |
| 119 | // the migration flow after the IndexedDB migration. |
| 120 | base::Closure migrate_fs = |
| 121 | base::Bind(&MigrateFileSystem, migrator, old_partition, current_partition, |
vmpstr | a34d1132 | 2016-03-21 20:28:47 | [diff] [blame] | 122 | base::RetainedRef(extension), reply); |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 123 | |
| 124 | // Perform the IndexedDB migration on the old context's sequenced task |
| 125 | // runner. After completion, it should call MigrateFileSystem. |
| 126 | old_indexed_db_context->TaskRunner()->PostTaskAndReply( |
vmpstr | a34d1132 | 2016-03-21 20:28:47 | [diff] [blame] | 127 | FROM_HERE, |
tzik | 8d880ee | 2017-04-20 19:46:24 | [diff] [blame] | 128 | base::BindOnce( |
vmpstr | a34d1132 | 2016-03-21 20:28:47 | [diff] [blame] | 129 | &MigrateOnIndexedDBThread, base::RetainedRef(old_indexed_db_context), |
| 130 | base::RetainedRef(indexed_db_context), base::RetainedRef(extension)), |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 131 | migrate_fs); |
| 132 | } |
| 133 | |
| 134 | } // namespace |
| 135 | |
| 136 | namespace extensions { |
| 137 | |
| 138 | AppDataMigrator::AppDataMigrator(Profile* profile, ExtensionRegistry* registry) |
| 139 | : profile_(profile), registry_(registry), weak_factory_(this) { |
| 140 | } |
| 141 | |
| 142 | AppDataMigrator::~AppDataMigrator() { |
| 143 | } |
| 144 | |
| 145 | bool AppDataMigrator::NeedsMigration(const Extension* old, |
| 146 | const Extension* extension) { |
| 147 | return old && old->is_legacy_packaged_app() && extension->is_platform_app(); |
| 148 | } |
| 149 | |
| 150 | void AppDataMigrator::DoMigrationAndReply(const Extension* old, |
| 151 | const Extension* extension, |
| 152 | const base::Closure& reply) { |
anujk.sharma | f2a0b4e | 2015-04-24 17:53:29 | [diff] [blame] | 153 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
ryanackley | 48bedbd | 2015-01-27 23:12:14 | [diff] [blame] | 154 | DCHECK(NeedsMigration(old, extension)); |
| 155 | |
| 156 | // This should retrieve the general storage partition. |
| 157 | content::StoragePartition* old_partition = |
| 158 | BrowserContext::GetStoragePartitionForSite( |
| 159 | profile_, Extension::GetBaseURLFromExtensionId(extension->id())); |
| 160 | |
| 161 | // Enable the new extension so we can access its storage partition. |
| 162 | bool old_was_disabled = registry_->AddEnabled(extension); |
| 163 | |
| 164 | // This should create a new isolated partition for the new version of the |
| 165 | // extension. |
| 166 | StoragePartition* new_partition = BrowserContext::GetStoragePartitionForSite( |
| 167 | profile_, Extension::GetBaseURLFromExtensionId(extension->id())); |
| 168 | |
| 169 | // Now, restore the enabled/disabled state of the new and old extensions. |
| 170 | if (old_was_disabled) |
| 171 | registry_->RemoveEnabled(extension->id()); |
| 172 | else |
| 173 | registry_->AddEnabled(old); |
| 174 | |
| 175 | MigrateLegacyPartition(weak_factory_.GetWeakPtr(), old_partition, |
| 176 | new_partition, extension, reply); |
| 177 | } |
| 178 | |
| 179 | } // namespace extensions |