1 // Copyright 2013 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 "base/lazy_instance.h"
6 #include "chrome/browser/browser_process.h"
7 #include "chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.h"
8 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
9 #include "chrome/browser/extensions/api/image_writer_private/operation.h"
10 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
11 #include "chrome/browser/extensions/api/image_writer_private/write_from_file_operation.h"
12 #include "chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h"
13 #include "chrome/browser/extensions/event_router_forwarder.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_service.h"
18 #include "extensions/browser/event_router.h"
19 #include "extensions/browser/extension_host.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "extensions/browser/notification_types.h"
22
23 namespace image_writer_api = extensions::api::image_writer_private;
24
25 namespace extensions {
26 namespace image_writer {
27
28 using content::BrowserThread;
29
OperationManager(content::BrowserContext * context)30 OperationManager::OperationManager(content::BrowserContext* context)
31 : browser_context_(context),
32 extension_registry_observer_(this),
33 weak_factory_(this) {
34 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
35 Profile* profile = Profile::FromBrowserContext(browser_context_);
36 registrar_.Add(this,
37 extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
38 content::Source<Profile>(profile));
39 registrar_.Add(this,
40 extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
41 content::Source<Profile>(profile));
42 registrar_.Add(this,
43 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED,
44 content::Source<Profile>(profile));
45 }
46
~OperationManager()47 OperationManager::~OperationManager() {
48 }
49
Shutdown()50 void OperationManager::Shutdown() {
51 for (OperationMap::iterator iter = operations_.begin();
52 iter != operations_.end();
53 iter++) {
54 BrowserThread::PostTask(BrowserThread::FILE,
55 FROM_HERE,
56 base::Bind(&Operation::Abort,
57 iter->second));
58 }
59 }
60
StartWriteFromUrl(const ExtensionId & extension_id,GURL url,const std::string & hash,const std::string & device_path,const Operation::StartWriteCallback & callback)61 void OperationManager::StartWriteFromUrl(
62 const ExtensionId& extension_id,
63 GURL url,
64 const std::string& hash,
65 const std::string& device_path,
66 const Operation::StartWriteCallback& callback) {
67 #if defined(OS_CHROMEOS)
68 // Chrome OS can only support a single operation at a time.
69 if (operations_.size() > 0) {
70 #else
71 OperationMap::iterator existing_operation = operations_.find(extension_id);
72
73 if (existing_operation != operations_.end()) {
74 #endif
75 return callback.Run(false, error::kOperationAlreadyInProgress);
76 }
77
78 scoped_refptr<Operation> operation(
79 new WriteFromUrlOperation(weak_factory_.GetWeakPtr(),
80 extension_id,
81 browser_context_->GetRequestContext(),
82 url,
83 hash,
84 device_path));
85 operations_[extension_id] = operation;
86 BrowserThread::PostTask(BrowserThread::FILE,
87 FROM_HERE,
88 base::Bind(&Operation::Start, operation));
89 callback.Run(true, "");
90 }
91
92 void OperationManager::StartWriteFromFile(
93 const ExtensionId& extension_id,
94 const base::FilePath& path,
95 const std::string& device_path,
96 const Operation::StartWriteCallback& callback) {
97 #if defined(OS_CHROMEOS)
98 // Chrome OS can only support a single operation at a time.
99 if (operations_.size() > 0) {
100 #else
101 OperationMap::iterator existing_operation = operations_.find(extension_id);
102
103 if (existing_operation != operations_.end()) {
104 #endif
105 return callback.Run(false, error::kOperationAlreadyInProgress);
106 }
107
108 scoped_refptr<Operation> operation(new WriteFromFileOperation(
109 weak_factory_.GetWeakPtr(), extension_id, path, device_path));
110 operations_[extension_id] = operation;
111 BrowserThread::PostTask(BrowserThread::FILE,
112 FROM_HERE,
113 base::Bind(&Operation::Start, operation));
114 callback.Run(true, "");
115 }
116
117 void OperationManager::CancelWrite(
118 const ExtensionId& extension_id,
119 const Operation::CancelWriteCallback& callback) {
120 Operation* existing_operation = GetOperation(extension_id);
121
122 if (existing_operation == NULL) {
123 callback.Run(false, error::kNoOperationInProgress);
124 } else {
125 BrowserThread::PostTask(BrowserThread::FILE,
126 FROM_HERE,
127 base::Bind(&Operation::Cancel, existing_operation));
128 DeleteOperation(extension_id);
129 callback.Run(true, "");
130 }
131 }
132
133 void OperationManager::DestroyPartitions(
134 const ExtensionId& extension_id,
135 const std::string& device_path,
136 const Operation::StartWriteCallback& callback) {
137 OperationMap::iterator existing_operation = operations_.find(extension_id);
138
139 if (existing_operation != operations_.end()) {
140 return callback.Run(false, error::kOperationAlreadyInProgress);
141 }
142
143 scoped_refptr<Operation> operation(new DestroyPartitionsOperation(
144 weak_factory_.GetWeakPtr(), extension_id, device_path));
145 operations_[extension_id] = operation;
146 BrowserThread::PostTask(BrowserThread::FILE,
147 FROM_HERE,
148 base::Bind(&Operation::Start, operation));
149 callback.Run(true, "");
150 }
151
152 void OperationManager::OnProgress(const ExtensionId& extension_id,
153 image_writer_api::Stage stage,
154 int progress) {
155 DCHECK_CURRENTLY_ON(BrowserThread::UI);
156
157 image_writer_api::ProgressInfo info;
158 info.stage = stage;
159 info.percent_complete = progress;
160
161 scoped_ptr<base::ListValue> args(
162 image_writer_api::OnWriteProgress::Create(info));
163 scoped_ptr<Event> event(new Event(
164 image_writer_api::OnWriteProgress::kEventName, args.Pass()));
165
166 EventRouter::Get(browser_context_)
167 ->DispatchEventToExtension(extension_id, event.Pass());
168 }
169
170 void OperationManager::OnComplete(const ExtensionId& extension_id) {
171 DCHECK_CURRENTLY_ON(BrowserThread::UI);
172
173 scoped_ptr<base::ListValue> args(image_writer_api::OnWriteComplete::Create());
174 scoped_ptr<Event> event(new Event(
175 image_writer_api::OnWriteComplete::kEventName, args.Pass()));
176
177 EventRouter::Get(browser_context_)
178 ->DispatchEventToExtension(extension_id, event.Pass());
179
180 DeleteOperation(extension_id);
181 }
182
183 void OperationManager::OnError(const ExtensionId& extension_id,
184 image_writer_api::Stage stage,
185 int progress,
186 const std::string& error_message) {
187 DCHECK_CURRENTLY_ON(BrowserThread::UI);
188 image_writer_api::ProgressInfo info;
189
190 DLOG(ERROR) << "ImageWriter error: " << error_message;
191
192 info.stage = stage;
193 info.percent_complete = progress;
194
195 scoped_ptr<base::ListValue> args(
196 image_writer_api::OnWriteError::Create(info, error_message));
197 scoped_ptr<Event> event(new Event(
198 image_writer_api::OnWriteError::kEventName, args.Pass()));
199
200 EventRouter::Get(browser_context_)
201 ->DispatchEventToExtension(extension_id, event.Pass());
202
203 DeleteOperation(extension_id);
204 }
205
206 Operation* OperationManager::GetOperation(const ExtensionId& extension_id) {
207 OperationMap::iterator existing_operation = operations_.find(extension_id);
208
209 if (existing_operation == operations_.end())
210 return NULL;
211 return existing_operation->second.get();
212 }
213
214 void OperationManager::DeleteOperation(const ExtensionId& extension_id) {
215 OperationMap::iterator existing_operation = operations_.find(extension_id);
216 if (existing_operation != operations_.end()) {
217 operations_.erase(existing_operation);
218 }
219 }
220
221 void OperationManager::OnExtensionUnloaded(
222 content::BrowserContext* browser_context,
223 const Extension* extension,
224 UnloadedExtensionInfo::Reason reason) {
225 DeleteOperation(extension->id());
226 }
227
228 void OperationManager::Observe(int type,
229 const content::NotificationSource& source,
230 const content::NotificationDetails& details) {
231 switch (type) {
232 case extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: {
233 DeleteOperation(content::Details<const Extension>(details).ptr()->id());
234 break;
235 }
236 case extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
237 DeleteOperation(
238 content::Details<ExtensionHost>(details)->extension()->id());
239 break;
240 }
241 case extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
242 DeleteOperation(
243 content::Details<ExtensionHost>(details)->extension()->id());
244 break;
245 }
246 default: {
247 NOTREACHED();
248 break;
249 }
250 }
251 }
252
253 OperationManager* OperationManager::Get(content::BrowserContext* context) {
254 return BrowserContextKeyedAPIFactory<OperationManager>::Get(context);
255 }
256
257 static base::LazyInstance<BrowserContextKeyedAPIFactory<OperationManager> >
258 g_factory = LAZY_INSTANCE_INITIALIZER;
259
260 BrowserContextKeyedAPIFactory<OperationManager>*
261 OperationManager::GetFactoryInstance() {
262 return g_factory.Pointer();
263 }
264
265
266 } // namespace image_writer
267 } // namespace extensions
268