| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h" |
| |
| #include <utility> |
| |
| #include "base/lazy_instance.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h" |
| #include "chrome/browser/extensions/api/image_writer_private/error_constants.h" |
| #include "chrome/browser/extensions/api/image_writer_private/operation.h" |
| #include "chrome/browser/extensions/api/image_writer_private/write_from_file_operation.h" |
| #include "chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h" |
| #include "chrome/browser/extensions/event_router_forwarder.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "extensions/browser/api/extensions_api_client.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/extension_host.h" |
| #include "extensions/common/extension_id.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #include "chrome/browser/ash/file_manager/path_util.h" |
| #endif |
| |
| namespace image_writer_api = extensions::api::image_writer_private; |
| |
| namespace extensions { |
| namespace image_writer { |
| |
| using content::BrowserThread; |
| |
| OperationManager::OperationManager(content::BrowserContext* context) |
| : browser_context_(context) { |
| extension_registry_observation_.Observe( |
| ExtensionRegistry::Get(browser_context_)); |
| process_manager_observation_.Observe(ProcessManager::Get(browser_context_)); |
| } |
| |
| OperationManager::~OperationManager() = default; |
| |
| void OperationManager::Shutdown() { |
| for (auto& id_and_operation : operations_) { |
| scoped_refptr<Operation> operation = id_and_operation.second; |
| operation->PostTask(base::BindOnce(&Operation::Abort, operation)); |
| } |
| } |
| |
| void OperationManager::StartWriteFromUrl( |
| const ExtensionId& extension_id, |
| GURL url, |
| const std::string& hash, |
| const std::string& device_path, |
| Operation::StartWriteCallback callback) { |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Chrome OS can only support a single operation at a time. |
| if (operations_.size() > 0) { |
| #else |
| auto existing_operation = operations_.find(extension_id); |
| |
| if (existing_operation != operations_.end()) { |
| #endif |
| |
| std::move(callback).Run(false, error::kOperationAlreadyInProgress); |
| return; |
| } |
| |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| url_loader_factory_remote; |
| browser_context_->GetDefaultStoragePartition() |
| ->GetURLLoaderFactoryForBrowserProcess() |
| ->Clone(url_loader_factory_remote.InitWithNewPipeAndPassReceiver()); |
| |
| auto operation = base::MakeRefCounted<WriteFromUrlOperation>( |
| weak_factory_.GetWeakPtr(), extension_id, |
| std::move(url_loader_factory_remote), url, hash, device_path, |
| GetAssociatedDownloadFolder()); |
| operations_[extension_id] = operation; |
| operation->PostTask(base::BindOnce(&Operation::Start, operation)); |
| |
| std::move(callback).Run(true, ""); |
| } |
| |
| void OperationManager::StartWriteFromFile( |
| const ExtensionId& extension_id, |
| const base::FilePath& path, |
| const std::string& device_path, |
| Operation::StartWriteCallback callback) { |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Chrome OS can only support a single operation at a time. |
| if (operations_.size() > 0) { |
| #else |
| auto existing_operation = operations_.find(extension_id); |
| |
| if (existing_operation != operations_.end()) { |
| #endif |
| std::move(callback).Run(false, error::kOperationAlreadyInProgress); |
| return; |
| } |
| |
| auto operation = base::MakeRefCounted<WriteFromFileOperation>( |
| weak_factory_.GetWeakPtr(), extension_id, path, device_path, |
| GetAssociatedDownloadFolder()); |
| operations_[extension_id] = operation; |
| operation->PostTask(base::BindOnce(&Operation::Start, operation)); |
| std::move(callback).Run(true, ""); |
| } |
| |
| void OperationManager::CancelWrite(const ExtensionId& extension_id, |
| Operation::CancelWriteCallback callback) { |
| Operation* existing_operation = GetOperation(extension_id); |
| |
| if (existing_operation == nullptr) { |
| std::move(callback).Run(false, error::kNoOperationInProgress); |
| } else { |
| existing_operation->PostTask( |
| base::BindOnce(&Operation::Cancel, existing_operation)); |
| DeleteOperation(extension_id); |
| std::move(callback).Run(true, ""); |
| } |
| } |
| |
| void OperationManager::DestroyPartitions( |
| const ExtensionId& extension_id, |
| const std::string& device_path, |
| Operation::StartWriteCallback callback) { |
| auto existing_operation = operations_.find(extension_id); |
| |
| if (existing_operation != operations_.end()) { |
| std::move(callback).Run(false, error::kOperationAlreadyInProgress); |
| return; |
| } |
| |
| auto operation = base::MakeRefCounted<DestroyPartitionsOperation>( |
| weak_factory_.GetWeakPtr(), extension_id, device_path, |
| GetAssociatedDownloadFolder()); |
| operations_[extension_id] = operation; |
| operation->PostTask(base::BindOnce(&Operation::Start, operation)); |
| std::move(callback).Run(true, ""); |
| } |
| |
| void OperationManager::OnProgress(const ExtensionId& extension_id, |
| image_writer_api::Stage stage, |
| int progress) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| image_writer_api::ProgressInfo info; |
| info.stage = stage; |
| info.percent_complete = progress; |
| |
| auto args(image_writer_api::OnWriteProgress::Create(info)); |
| std::unique_ptr<Event> event(new Event( |
| events::IMAGE_WRITER_PRIVATE_ON_WRITE_PROGRESS, |
| image_writer_api::OnWriteProgress::kEventName, std::move(args))); |
| |
| EventRouter::Get(browser_context_) |
| ->DispatchEventToExtension(extension_id, std::move(event)); |
| } |
| |
| void OperationManager::OnComplete(const ExtensionId& extension_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| auto args(image_writer_api::OnWriteComplete::Create()); |
| std::unique_ptr<Event> event(new Event( |
| events::IMAGE_WRITER_PRIVATE_ON_WRITE_COMPLETE, |
| image_writer_api::OnWriteComplete::kEventName, std::move(args))); |
| |
| EventRouter::Get(browser_context_) |
| ->DispatchEventToExtension(extension_id, std::move(event)); |
| |
| DeleteOperation(extension_id); |
| } |
| |
| void OperationManager::OnError(const ExtensionId& extension_id, |
| image_writer_api::Stage stage, |
| int progress, |
| const std::string& error_message) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| image_writer_api::ProgressInfo info; |
| |
| DLOG(ERROR) << "ImageWriter error: " << error_message; |
| |
| info.stage = stage; |
| info.percent_complete = progress; |
| |
| auto args(image_writer_api::OnWriteError::Create(info, error_message)); |
| std::unique_ptr<Event> event( |
| new Event(events::IMAGE_WRITER_PRIVATE_ON_WRITE_ERROR, |
| image_writer_api::OnWriteError::kEventName, std::move(args))); |
| |
| EventRouter::Get(browser_context_) |
| ->DispatchEventToExtension(extension_id, std::move(event)); |
| |
| DeleteOperation(extension_id); |
| } |
| |
| base::FilePath OperationManager::GetAssociatedDownloadFolder() { |
| #if BUILDFLAG(IS_CHROMEOS) |
| Profile* profile = Profile::FromBrowserContext(browser_context_); |
| return file_manager::util::GetDownloadsFolderForProfile(profile); |
| #else |
| return base::FilePath(); |
| #endif |
| } |
| |
| Operation* OperationManager::GetOperation(const ExtensionId& extension_id) { |
| auto existing_operation = operations_.find(extension_id); |
| |
| if (existing_operation == operations_.end()) |
| return nullptr; |
| return existing_operation->second.get(); |
| } |
| |
| void OperationManager::DeleteOperation(const ExtensionId& extension_id) { |
| auto existing_operation = operations_.find(extension_id); |
| if (existing_operation != operations_.end()) { |
| operations_.erase(existing_operation); |
| } |
| } |
| |
| void OperationManager::OnExtensionUnloaded( |
| content::BrowserContext* browser_context, |
| const Extension* extension, |
| UnloadedExtensionReason reason) { |
| DeleteOperation(extension->id()); |
| } |
| |
| void OperationManager::OnShutdown(ExtensionRegistry* registry) { |
| DCHECK(extension_registry_observation_.IsObservingSource(registry)); |
| extension_registry_observation_.Reset(); |
| } |
| |
| void OperationManager::OnBackgroundHostClose(const ExtensionId& extension_id) { |
| DeleteOperation(extension_id); |
| } |
| |
| void OperationManager::OnProcessManagerShutdown(ProcessManager* manager) { |
| DCHECK(process_manager_observation_.IsObservingSource(manager)); |
| process_manager_observation_.Reset(); |
| } |
| |
| void OperationManager::OnExtensionProcessTerminated( |
| const Extension* extension) { |
| DeleteOperation(extension->id()); |
| } |
| |
| OperationManager* OperationManager::Get(content::BrowserContext* context) { |
| return BrowserContextKeyedAPIFactory<OperationManager>::Get(context); |
| } |
| |
| static base::LazyInstance<BrowserContextKeyedAPIFactory<OperationManager>>:: |
| DestructorAtExit g_operation_manager_factory = LAZY_INSTANCE_INITIALIZER; |
| |
| BrowserContextKeyedAPIFactory<OperationManager>* |
| OperationManager::GetFactoryInstance() { |
| return g_operation_manager_factory.Pointer(); |
| } |
| |
| } // namespace image_writer |
| } // namespace extensions |