1 // Copyright (c) 2012 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/chromeos/extensions/file_manager/event_router.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_change_registrar.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "base/values.h"
15 #include "chrome/browser/app_mode/app_mode_utils.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
18 #include "chrome/browser/chromeos/drive/file_system_interface.h"
19 #include "chrome/browser/chromeos/drive/file_system_util.h"
20 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
21 #include "chrome/browser/chromeos/file_manager/app_id.h"
22 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
23 #include "chrome/browser/chromeos/file_manager/open_util.h"
24 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
25 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
26 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
27 #include "chrome/browser/drive/drive_service_interface.h"
28 #include "chrome/browser/extensions/extension_service.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/common/pref_names.h"
32 #include "chromeos/login/login_state.h"
33 #include "chromeos/network/network_handler.h"
34 #include "chromeos/network/network_state_handler.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/notification_source.h"
37 #include "content/public/browser/render_process_host.h"
38 #include "extensions/browser/event_router.h"
39 #include "extensions/browser/extension_host.h"
40 #include "extensions/browser/extension_prefs.h"
41 #include "extensions/browser/extension_system.h"
42 #include "webkit/common/fileapi/file_system_types.h"
43 #include "webkit/common/fileapi/file_system_util.h"
44
45 using chromeos::disks::DiskMountManager;
46 using chromeos::NetworkHandler;
47 using content::BrowserThread;
48 using drive::DriveIntegrationService;
49 using drive::DriveIntegrationServiceFactory;
50 using file_manager::util::EntryDefinition;
51 using file_manager::util::FileDefinition;
52
53 namespace file_browser_private = extensions::api::file_browser_private;
54
55 namespace file_manager {
56 namespace {
57
DirectoryExistsOnBlockingPool(const base::FilePath & directory_path,const base::Closure & success_callback,const base::Closure & failure_callback)58 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path,
59 const base::Closure& success_callback,
60 const base::Closure& failure_callback) {
61 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
62
63 if (base::DirectoryExists(directory_path))
64 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback);
65 else
66 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback);
67 }
68
DirectoryExistsOnUIThread(const base::FilePath & directory_path,const base::Closure & success_callback,const base::Closure & failure_callback)69 void DirectoryExistsOnUIThread(const base::FilePath& directory_path,
70 const base::Closure& success_callback,
71 const base::Closure& failure_callback) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73
74 content::BrowserThread::PostBlockingPoolTask(
75 FROM_HERE,
76 base::Bind(&DirectoryExistsOnBlockingPool,
77 directory_path,
78 success_callback,
79 failure_callback));
80 }
81
82 // Constants for the "transferState" field of onFileTransferUpdated event.
83 const char kFileTransferStateStarted[] = "started";
84 const char kFileTransferStateInProgress[] = "in_progress";
85 const char kFileTransferStateCompleted[] = "completed";
86 const char kFileTransferStateFailed[] = "failed";
87
88 // Frequency of sending onFileTransferUpdated.
89 const int64 kProgressEventFrequencyInMilliseconds = 1000;
90
91 // Utility function to check if |job_info| is a file uploading job.
IsUploadJob(drive::JobType type)92 bool IsUploadJob(drive::JobType type) {
93 return (type == drive::TYPE_UPLOAD_NEW_FILE ||
94 type == drive::TYPE_UPLOAD_EXISTING_FILE);
95 }
96
97 // Converts the job info to a IDL generated type.
JobInfoToTransferStatus(Profile * profile,const std::string & extension_id,const std::string & job_status,const drive::JobInfo & job_info,file_browser_private::FileTransferStatus * status)98 void JobInfoToTransferStatus(
99 Profile* profile,
100 const std::string& extension_id,
101 const std::string& job_status,
102 const drive::JobInfo& job_info,
103 file_browser_private::FileTransferStatus* status) {
104 DCHECK(IsActiveFileTransferJobInfo(job_info));
105
106 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
107 GURL url = util::ConvertDrivePathToFileSystemUrl(
108 profile, job_info.file_path, extension_id);
109 status->file_url = url.spec();
110 status->transfer_state = file_browser_private::ParseTransferState(job_status);
111 status->transfer_type =
112 IsUploadJob(job_info.job_type) ?
113 file_browser_private::TRANSFER_TYPE_UPLOAD :
114 file_browser_private::TRANSFER_TYPE_DOWNLOAD;
115 // JavaScript does not have 64-bit integers. Instead we use double, which
116 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
117 // in C++. Larger values are rounded.
118 status->processed.reset(
119 new double(static_cast<double>(job_info.num_completed_bytes)));
120 status->total.reset(
121 new double(static_cast<double>(job_info.num_total_bytes)));
122 }
123
124 // Checks for availability of the Google+ Photos app.
125 // TODO(mtomasz): Replace with crbug.com/341902 solution.
IsGooglePhotosInstalled(Profile * profile)126 bool IsGooglePhotosInstalled(Profile *profile) {
127 ExtensionService* service =
128 extensions::ExtensionSystem::Get(profile)->extension_service();
129 if (!service)
130 return false;
131
132 // Google+ Photos uses several ids for different channels. Therefore, all of
133 // them should be checked.
134 const std::string kGooglePlusPhotosIds[] = {
135 "ebpbnabdhheoknfklmpddcdijjkmklkp", // G+ Photos staging
136 "efjnaogkjbogokcnohkmnjdojkikgobo", // G+ Photos prod
137 "ejegoaikibpmikoejfephaneibodccma" // G+ Photos dev
138 };
139
140 for (size_t i = 0; i < arraysize(kGooglePlusPhotosIds); ++i) {
141 if (service->GetExtensionById(kGooglePlusPhotosIds[i],
142 false /* include_disable */) != NULL)
143 return true;
144 }
145
146 return false;
147 }
148
149 // Checks if the Recovery Tool is running. This is a temporary solution.
150 // TODO(mtomasz): Replace with crbug.com/341902 solution.
IsRecoveryToolRunning(Profile * profile)151 bool IsRecoveryToolRunning(Profile* profile) {
152 extensions::ExtensionPrefs* extension_prefs =
153 extensions::ExtensionPrefs::Get(profile);
154 if (!extension_prefs)
155 return false;
156
157 const std::string kRecoveryToolIds[] = {
158 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovery tool staging
159 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
160 };
161
162 for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
163 const std::string extension_id = kRecoveryToolIds[i];
164 if (extension_prefs->IsExtensionRunning(extension_id))
165 return true;
166 }
167
168 return false;
169 }
170
171 // Sends an event named |event_name| with arguments |event_args| to extensions.
BroadcastEvent(Profile * profile,const std::string & event_name,scoped_ptr<base::ListValue> event_args)172 void BroadcastEvent(Profile* profile,
173 const std::string& event_name,
174 scoped_ptr<base::ListValue> event_args) {
175 extensions::EventRouter::Get(profile)->BroadcastEvent(
176 make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
177 }
178
179 file_browser_private::MountCompletedStatus
MountErrorToMountCompletedStatus(chromeos::MountError error)180 MountErrorToMountCompletedStatus(chromeos::MountError error) {
181 switch (error) {
182 case chromeos::MOUNT_ERROR_NONE:
183 return file_browser_private::MOUNT_COMPLETED_STATUS_SUCCESS;
184 case chromeos::MOUNT_ERROR_UNKNOWN:
185 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN;
186 case chromeos::MOUNT_ERROR_INTERNAL:
187 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL;
188 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT:
189 return file_browser_private::
190 MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT;
191 case chromeos::MOUNT_ERROR_INVALID_PATH:
192 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH;
193 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED:
194 return file_browser_private::
195 MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED;
196 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED:
197 return file_browser_private::
198 MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED;
199 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED:
200 return file_browser_private
201 ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED;
202 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS:
203 return file_browser_private
204 ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS;
205 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS:
206 return file_browser_private::
207 MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS;
208 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS:
209 return file_browser_private::
210 MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS;
211 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND:
212 return file_browser_private::
213 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND;
214 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED:
215 return file_browser_private::
216 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED;
217 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH:
218 return file_browser_private::
219 MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH;
220 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
221 return file_browser_private::
222 MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM;
223 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
224 return file_browser_private::
225 MOUNT_COMPLETED_STATUS_ERROR_UNSUPORTED_FILESYSTEM;
226 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
227 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE;
228 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
229 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION;
230 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
231 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
232 }
233 NOTREACHED();
234 return file_browser_private::MOUNT_COMPLETED_STATUS_NONE;
235 }
236
BroadcastMountCompletedEvent(Profile * profile,file_browser_private::MountCompletedEventType event_type,chromeos::MountError error,const VolumeInfo & volume_info,bool is_remounting)237 void BroadcastMountCompletedEvent(
238 Profile* profile,
239 file_browser_private::MountCompletedEventType event_type,
240 chromeos::MountError error,
241 const VolumeInfo& volume_info,
242 bool is_remounting) {
243 file_browser_private::MountCompletedEvent event;
244 event.event_type = event_type;
245 event.status = MountErrorToMountCompletedStatus(error);
246 util::VolumeInfoToVolumeMetadata(
247 profile, volume_info, &event.volume_metadata);
248 event.is_remounting = is_remounting;
249
250 BroadcastEvent(
251 profile,
252 file_browser_private::OnMountCompleted::kEventName,
253 file_browser_private::OnMountCompleted::Create(event));
254 }
255
256 file_browser_private::CopyProgressStatusType
CopyProgressTypeToCopyProgressStatusType(fileapi::FileSystemOperation::CopyProgressType type)257 CopyProgressTypeToCopyProgressStatusType(
258 fileapi::FileSystemOperation::CopyProgressType type) {
259 switch (type) {
260 case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY:
261 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
262 case fileapi::FileSystemOperation::END_COPY_ENTRY:
263 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
264 case fileapi::FileSystemOperation::PROGRESS:
265 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
266 }
267 NOTREACHED();
268 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_NONE;
269 }
270
FileErrorToErrorName(base::File::Error error_code)271 std::string FileErrorToErrorName(base::File::Error error_code) {
272 namespace js = extensions::api::file_browser_private;
273 switch (error_code) {
274 case base::File::FILE_ERROR_NOT_FOUND:
275 return "NotFoundError";
276 case base::File::FILE_ERROR_INVALID_OPERATION:
277 case base::File::FILE_ERROR_EXISTS:
278 case base::File::FILE_ERROR_NOT_EMPTY:
279 return "InvalidModificationError";
280 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
281 case base::File::FILE_ERROR_NOT_A_FILE:
282 return "TypeMismatchError";
283 case base::File::FILE_ERROR_ACCESS_DENIED:
284 return "NoModificationAllowedError";
285 case base::File::FILE_ERROR_FAILED:
286 return "InvalidStateError";
287 case base::File::FILE_ERROR_ABORT:
288 return "AbortError";
289 case base::File::FILE_ERROR_SECURITY:
290 return "SecurityError";
291 case base::File::FILE_ERROR_NO_SPACE:
292 return "QuotaExceededError";
293 case base::File::FILE_ERROR_INVALID_URL:
294 return "EncodingError";
295 default:
296 return "InvalidModificationError";
297 }
298 }
299
GrantAccessForAddedProfileToRunningInstance(Profile * added_profile,Profile * running_profile)300 void GrantAccessForAddedProfileToRunningInstance(Profile* added_profile,
301 Profile* running_profile) {
302 extensions::ProcessManager* const process_manager =
303 extensions::ExtensionSystem::Get(running_profile)->process_manager();
304 if (!process_manager)
305 return;
306
307 extensions::ExtensionHost* const extension_host =
308 process_manager->GetBackgroundHostForExtension(kFileManagerAppId);
309 if (!extension_host || !extension_host->render_process_host())
310 return;
311
312 const int id = extension_host->render_process_host()->GetID();
313 file_manager::util::SetupProfileFileAccessPermissions(id, added_profile);
314 }
315
316 // Checks if we should send a progress event or not according to the
317 // |last_time| of sending an event. If |always| is true, the function always
318 // returns true. If the function returns true, the function also updates
319 // |last_time|.
ShouldSendProgressEvent(bool always,base::Time * last_time)320 bool ShouldSendProgressEvent(bool always, base::Time* last_time) {
321 const base::Time now = base::Time::Now();
322 const int64 delta = (now - *last_time).InMilliseconds();
323 // delta < 0 may rarely happen if system clock is synced and rewinded.
324 // To be conservative, we don't skip in that case.
325 if (!always && 0 <= delta && delta < kProgressEventFrequencyInMilliseconds) {
326 return false;
327 } else {
328 *last_time = now;
329 return true;
330 }
331 }
332
333 } // namespace
334
335 // Pass dummy value to JobInfo's constructor for make it default constructible.
DriveJobInfoWithStatus()336 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
337 : job_info(drive::TYPE_DOWNLOAD_FILE) {
338 }
339
DriveJobInfoWithStatus(const drive::JobInfo & info,const std::string & status)340 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
341 const drive::JobInfo& info, const std::string& status)
342 : job_info(info), status(status) {
343 }
344
EventRouter(Profile * profile)345 EventRouter::EventRouter(Profile* profile)
346 : pref_change_registrar_(new PrefChangeRegistrar),
347 profile_(profile),
348 multi_user_window_manager_observer_registered_(false),
349 weak_factory_(this) {
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
351 }
352
~EventRouter()353 EventRouter::~EventRouter() {
354 }
355
Shutdown()356 void EventRouter::Shutdown() {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358
359 DLOG_IF(WARNING, !file_watchers_.empty())
360 << "Not all file watchers are "
361 << "removed. This can happen when Files.app is open during shutdown.";
362 STLDeleteValues(&file_watchers_);
363 if (!profile_) {
364 NOTREACHED();
365 return;
366 }
367
368 pref_change_registrar_->RemoveAll();
369
370 if (NetworkHandler::IsInitialized()) {
371 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
372 FROM_HERE);
373 }
374
375 DriveIntegrationService* integration_service =
376 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
377 profile_);
378 if (integration_service) {
379 integration_service->file_system()->RemoveObserver(this);
380 integration_service->drive_service()->RemoveObserver(this);
381 integration_service->job_list()->RemoveObserver(this);
382 }
383
384 VolumeManager* volume_manager = VolumeManager::Get(profile_);
385 if (volume_manager)
386 volume_manager->RemoveObserver(this);
387
388 chrome::MultiUserWindowManager* const multi_user_window_manager =
389 chrome::MultiUserWindowManager::GetInstance();
390 if (multi_user_window_manager &&
391 multi_user_window_manager_observer_registered_) {
392 multi_user_window_manager_observer_registered_ = false;
393 multi_user_window_manager->RemoveObserver(this);
394 }
395
396 profile_ = NULL;
397 }
398
ObserveEvents()399 void EventRouter::ObserveEvents() {
400 if (!profile_) {
401 NOTREACHED();
402 return;
403 }
404 if (!chromeos::LoginState::IsInitialized() ||
405 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
406 return;
407 }
408
409 // VolumeManager's construction triggers DriveIntegrationService's
410 // construction, so it is necessary to call VolumeManager's Get before
411 // accessing DriveIntegrationService.
412 VolumeManager* volume_manager = VolumeManager::Get(profile_);
413 if (volume_manager)
414 volume_manager->AddObserver(this);
415
416 DriveIntegrationService* integration_service =
417 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
418 profile_);
419 if (integration_service) {
420 integration_service->drive_service()->AddObserver(this);
421 integration_service->file_system()->AddObserver(this);
422 integration_service->job_list()->AddObserver(this);
423 }
424
425 if (NetworkHandler::IsInitialized()) {
426 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
427 FROM_HERE);
428 }
429
430 pref_change_registrar_->Init(profile_->GetPrefs());
431 base::Closure callback =
432 base::Bind(&EventRouter::OnFileManagerPrefsChanged,
433 weak_factory_.GetWeakPtr());
434 pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
435 pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
436 pref_change_registrar_->Add(prefs::kDisableDrive, callback);
437 pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
438
439 notification_registrar_.Add(this,
440 chrome::NOTIFICATION_PROFILE_ADDED,
441 content::NotificationService::AllSources());
442 }
443
444 // File watch setup routines.
AddFileWatch(const base::FilePath & local_path,const base::FilePath & virtual_path,const std::string & extension_id,const BoolCallback & callback)445 void EventRouter::AddFileWatch(const base::FilePath& local_path,
446 const base::FilePath& virtual_path,
447 const std::string& extension_id,
448 const BoolCallback& callback) {
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 DCHECK(!callback.is_null());
451
452 base::FilePath watch_path = local_path;
453 bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
454 // Tweak watch path for remote sources - we need to drop leading /special
455 // directory from there in order to be able to pair these events with
456 // their change notifications.
457 if (is_on_drive)
458 watch_path = drive::util::ExtractDrivePath(watch_path);
459
460 WatcherMap::iterator iter = file_watchers_.find(watch_path);
461 if (iter == file_watchers_.end()) {
462 scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
463 watcher->AddExtension(extension_id);
464
465 if (is_on_drive) {
466 // For Drive, file watching is done via OnDirectoryChanged().
467 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
468 base::Bind(callback, true));
469 } else {
470 // For local files, start watching using FileWatcher.
471 watcher->WatchLocalFile(
472 watch_path,
473 base::Bind(&EventRouter::HandleFileWatchNotification,
474 weak_factory_.GetWeakPtr()),
475 callback);
476 }
477
478 file_watchers_[watch_path] = watcher.release();
479 } else {
480 iter->second->AddExtension(extension_id);
481 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
482 base::Bind(callback, true));
483 }
484 }
485
RemoveFileWatch(const base::FilePath & local_path,const std::string & extension_id)486 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
487 const std::string& extension_id) {
488 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
489
490 base::FilePath watch_path = local_path;
491 // Tweak watch path for remote sources - we need to drop leading /special
492 // directory from there in order to be able to pair these events with
493 // their change notifications.
494 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
495 watch_path = drive::util::ExtractDrivePath(watch_path);
496 }
497 WatcherMap::iterator iter = file_watchers_.find(watch_path);
498 if (iter == file_watchers_.end())
499 return;
500 // Remove the watcher if |watch_path| is no longer watched by any extensions.
501 iter->second->RemoveExtension(extension_id);
502 if (iter->second->GetExtensionIds().empty()) {
503 delete iter->second;
504 file_watchers_.erase(iter);
505 }
506 }
507
OnCopyCompleted(int copy_id,const GURL & source_url,const GURL & destination_url,base::File::Error error)508 void EventRouter::OnCopyCompleted(int copy_id,
509 const GURL& source_url,
510 const GURL& destination_url,
511 base::File::Error error) {
512 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
513
514 file_browser_private::CopyProgressStatus status;
515 if (error == base::File::FILE_OK) {
516 // Send success event.
517 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
518 status.source_url.reset(new std::string(source_url.spec()));
519 status.destination_url.reset(new std::string(destination_url.spec()));
520 } else {
521 // Send error event.
522 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
523 status.error.reset(new std::string(FileErrorToErrorName(error)));
524 }
525
526 BroadcastEvent(
527 profile_,
528 file_browser_private::OnCopyProgress::kEventName,
529 file_browser_private::OnCopyProgress::Create(copy_id, status));
530 }
531
OnCopyProgress(int copy_id,fileapi::FileSystemOperation::CopyProgressType type,const GURL & source_url,const GURL & destination_url,int64 size)532 void EventRouter::OnCopyProgress(
533 int copy_id,
534 fileapi::FileSystemOperation::CopyProgressType type,
535 const GURL& source_url,
536 const GURL& destination_url,
537 int64 size) {
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
539
540 file_browser_private::CopyProgressStatus status;
541 status.type = CopyProgressTypeToCopyProgressStatusType(type);
542 status.source_url.reset(new std::string(source_url.spec()));
543 if (type == fileapi::FileSystemOperation::END_COPY_ENTRY)
544 status.destination_url.reset(new std::string(destination_url.spec()));
545 if (type == fileapi::FileSystemOperation::PROGRESS)
546 status.size.reset(new double(size));
547
548 // Should not skip events other than TYPE_PROGRESS.
549 const bool always =
550 status.type != file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
551 if (!ShouldSendProgressEvent(always, &last_copy_progress_event_))
552 return;
553
554 BroadcastEvent(
555 profile_,
556 file_browser_private::OnCopyProgress::kEventName,
557 file_browser_private::OnCopyProgress::Create(copy_id, status));
558 }
559
DefaultNetworkChanged(const chromeos::NetworkState * network)560 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
561 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
562 NOTREACHED();
563 return;
564 }
565
566 BroadcastEvent(
567 profile_,
568 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
569 file_browser_private::OnDriveConnectionStatusChanged::Create());
570 }
571
OnFileManagerPrefsChanged()572 void EventRouter::OnFileManagerPrefsChanged() {
573 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
574 NOTREACHED();
575 return;
576 }
577
578 BroadcastEvent(
579 profile_,
580 file_browser_private::OnPreferencesChanged::kEventName,
581 file_browser_private::OnPreferencesChanged::Create());
582 }
583
OnJobAdded(const drive::JobInfo & job_info)584 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
585 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
586 OnJobUpdated(job_info);
587 }
588
OnJobUpdated(const drive::JobInfo & job_info)589 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
591 if (!drive::IsActiveFileTransferJobInfo(job_info))
592 return;
593
594 bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
595
596 // Replace with the latest job info.
597 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
598 job_info,
599 is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress);
600
601 // Fire event if needed.
602 bool always = is_new_job;
603 SendDriveFileTransferEvent(always);
604 }
605
OnJobDone(const drive::JobInfo & job_info,drive::FileError error)606 void EventRouter::OnJobDone(const drive::JobInfo& job_info,
607 drive::FileError error) {
608 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
609 if (!drive::IsActiveFileTransferJobInfo(job_info))
610 return;
611
612 // Replace with the latest job info.
613 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
614 job_info,
615 error == drive::FILE_ERROR_OK ? kFileTransferStateCompleted
616 : kFileTransferStateFailed);
617
618 // Fire event if needed.
619 bool always = true;
620 SendDriveFileTransferEvent(always);
621
622 // Forget about the job.
623 drive_jobs_.erase(job_info.job_id);
624 }
625
SendDriveFileTransferEvent(bool always)626 void EventRouter::SendDriveFileTransferEvent(bool always) {
627 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
628
629 // When |always| flag is not set, we don't send the event until certain
630 // amount of time passes after the previous one. This is to avoid
631 // flooding the IPC between extensions by many onFileTransferUpdated events.
632 if (!ShouldSendProgressEvent(always, &last_file_transfer_event_))
633 return;
634
635 // Convert the current |drive_jobs_| to IDL type.
636 std::vector<linked_ptr<file_browser_private::FileTransferStatus> >
637 status_list;
638 for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator
639 iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) {
640 linked_ptr<file_browser_private::FileTransferStatus> status(
641 new file_browser_private::FileTransferStatus());
642 JobInfoToTransferStatus(profile_,
643 kFileManagerAppId,
644 iter->second.status,
645 iter->second.job_info,
646 status.get());
647 status_list.push_back(status);
648 }
649 BroadcastEvent(
650 profile_,
651 file_browser_private::OnFileTransfersUpdated::kEventName,
652 file_browser_private::OnFileTransfersUpdated::Create(status_list));
653 }
654
OnDirectoryChanged(const base::FilePath & drive_path)655 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
656 HandleFileWatchNotification(drive_path, false);
657 }
658
OnDriveSyncError(drive::file_system::DriveSyncErrorType type,const base::FilePath & drive_path)659 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
660 const base::FilePath& drive_path) {
661 file_browser_private::DriveSyncErrorEvent event;
662 switch (type) {
663 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
664 event.type =
665 file_browser_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
666 break;
667 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
668 event.type =
669 file_browser_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
670 break;
671 case drive::file_system::DRIVE_SYNC_ERROR_MISC:
672 event.type =
673 file_browser_private::DRIVE_SYNC_ERROR_TYPE_MISC;
674 break;
675 }
676 event.file_url = util::ConvertDrivePathToFileSystemUrl(
677 profile_, drive_path, kFileManagerAppId).spec();
678 BroadcastEvent(
679 profile_,
680 file_browser_private::OnDriveSyncError::kEventName,
681 file_browser_private::OnDriveSyncError::Create(event));
682 }
683
OnRefreshTokenInvalid()684 void EventRouter::OnRefreshTokenInvalid() {
685 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
686
687 // Raise a DriveConnectionStatusChanged event to notify the status offline.
688 BroadcastEvent(
689 profile_,
690 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
691 file_browser_private::OnDriveConnectionStatusChanged::Create());
692 }
693
HandleFileWatchNotification(const base::FilePath & local_path,bool got_error)694 void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path,
695 bool got_error) {
696 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
697
698 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
699 if (iter == file_watchers_.end()) {
700 return;
701 }
702 DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error,
703 iter->second->GetExtensionIds());
704 }
705
DispatchDirectoryChangeEvent(const base::FilePath & virtual_path,bool got_error,const std::vector<std::string> & extension_ids)706 void EventRouter::DispatchDirectoryChangeEvent(
707 const base::FilePath& virtual_path,
708 bool got_error,
709 const std::vector<std::string>& extension_ids) {
710 if (!profile_) {
711 NOTREACHED();
712 return;
713 }
714
715 for (size_t i = 0; i < extension_ids.size(); ++i) {
716 const std::string& extension_id = extension_ids[i];
717
718 FileDefinition file_definition;
719 file_definition.virtual_path = virtual_path;
720 file_definition.is_directory = true;
721
722 file_manager::util::ConvertFileDefinitionToEntryDefinition(
723 profile_,
724 extension_id,
725 file_definition,
726 base::Bind(
727 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition,
728 weak_factory_.GetWeakPtr(),
729 got_error));
730 }
731 }
732
DispatchDirectoryChangeEventWithEntryDefinition(bool watcher_error,const EntryDefinition & entry_definition)733 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
734 bool watcher_error,
735 const EntryDefinition& entry_definition) {
736 if (entry_definition.error != base::File::FILE_OK ||
737 !entry_definition.is_directory) {
738 DVLOG(1) << "Unable to dispatch event because resolving the directory "
739 << "entry definition failed.";
740 return;
741 }
742
743 file_browser_private::FileWatchEvent event;
744 event.event_type = watcher_error
745 ? file_browser_private::FILE_WATCH_EVENT_TYPE_ERROR
746 : file_browser_private::FILE_WATCH_EVENT_TYPE_CHANGED;
747
748 event.entry.additional_properties.SetString(
749 "fileSystemName", entry_definition.file_system_name);
750 event.entry.additional_properties.SetString(
751 "fileSystemRoot", entry_definition.file_system_root_url);
752 event.entry.additional_properties.SetString(
753 "fileFullPath", "/" + entry_definition.full_path.value());
754 event.entry.additional_properties.SetBoolean("fileIsDirectory",
755 entry_definition.is_directory);
756
757 BroadcastEvent(profile_,
758 file_browser_private::OnDirectoryChanged::kEventName,
759 file_browser_private::OnDirectoryChanged::Create(event));
760 }
761
ShowRemovableDeviceInFileManager(VolumeType type,const base::FilePath & mount_path)762 void EventRouter::ShowRemovableDeviceInFileManager(
763 VolumeType type,
764 const base::FilePath& mount_path) {
765 // Do not attempt to open File Manager while the login is in progress or
766 // the screen is locked or running in kiosk app mode and make sure the file
767 // manager is opened only for the active user.
768 if (chromeos::LoginDisplayHostImpl::default_host() ||
769 chromeos::ScreenLocker::default_screen_locker() ||
770 chrome::IsRunningInForcedAppMode() ||
771 profile_ != ProfileManager::GetActiveUserProfile())
772 return;
773
774 // Do not pop-up the File Manager, if the recovery tool is running.
775 if (IsRecoveryToolRunning(profile_))
776 return;
777
778 // Do not pop-up the File Manager, if Google+ Photos may be launched.
779 if (IsGooglePhotosInstalled(profile_)) {
780 // MTP device is handled by Photos app.
781 if (type == VOLUME_TYPE_MTP)
782 return;
783 // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
784 // cameras should have pictures located in the DCIM root directory.
785 // Such removable disks are handled by Photos app.
786 if (type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
787 DirectoryExistsOnUIThread(
788 mount_path.AppendASCII("DCIM"),
789 base::Bind(&base::DoNothing),
790 base::Bind(&util::OpenRemovableDrive, profile_, mount_path));
791 return;
792 }
793 }
794
795 util::OpenRemovableDrive(profile_, mount_path);
796 }
797
DispatchDeviceEvent(file_browser_private::DeviceEventType type,const std::string & device_path)798 void EventRouter::DispatchDeviceEvent(
799 file_browser_private::DeviceEventType type,
800 const std::string& device_path) {
801 file_browser_private::DeviceEvent event;
802 event.type = type;
803 event.device_path = device_path;
804 BroadcastEvent(profile_,
805 file_browser_private::OnDeviceChanged::kEventName,
806 file_browser_private::OnDeviceChanged::Create(event));
807 }
808
OnDiskAdded(const DiskMountManager::Disk & disk,bool mounting)809 void EventRouter::OnDiskAdded(
810 const DiskMountManager::Disk& disk, bool mounting) {
811 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
812
813 if (!mounting) {
814 // If the disk is not being mounted, we don't want the Scanning
815 // notification to persist.
816 DispatchDeviceEvent(
817 file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
818 disk.system_path_prefix());
819 }
820 }
821
OnDiskRemoved(const DiskMountManager::Disk & disk)822 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
823 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
824 // Do nothing.
825 }
826
OnDeviceAdded(const std::string & device_path)827 void EventRouter::OnDeviceAdded(const std::string& device_path) {
828 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
829
830 // If the policy is set instead of showing the new device notification,
831 // we show a notification that the operation is not permitted.
832 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
833 DispatchDeviceEvent(
834 file_browser_private::DEVICE_EVENT_TYPE_DISABLED,
835 device_path);
836 return;
837 }
838
839 DispatchDeviceEvent(
840 file_browser_private::DEVICE_EVENT_TYPE_ADDED,
841 device_path);
842 }
843
OnDeviceRemoved(const std::string & device_path,bool hard_unplugged)844 void EventRouter::OnDeviceRemoved(const std::string& device_path,
845 bool hard_unplugged) {
846 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
847
848 DispatchDeviceEvent(
849 file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
850 device_path);
851
852 if (hard_unplugged) {
853 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_HARD_UNPLUGGED,
854 device_path);
855 }
856 }
857
OnVolumeMounted(chromeos::MountError error_code,const VolumeInfo & volume_info,bool is_remounting)858 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
859 const VolumeInfo& volume_info,
860 bool is_remounting) {
861 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
862 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
863 // happen at shutdown. This should be removed after removing Drive mounting
864 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
865 // the only path to come here after Shutdown is called).
866 if (!profile_)
867 return;
868
869 BroadcastMountCompletedEvent(
870 profile_,
871 file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
872 error_code,
873 volume_info,
874 is_remounting);
875
876 if ((volume_info.type == VOLUME_TYPE_MTP ||
877 volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) &&
878 !is_remounting) {
879 // If a new device was mounted, a new File manager window may need to be
880 // opened.
881 if (error_code == chromeos::MOUNT_ERROR_NONE)
882 ShowRemovableDeviceInFileManager(volume_info.type,
883 volume_info.mount_path);
884 }
885 }
886
OnVolumeUnmounted(chromeos::MountError error_code,const VolumeInfo & volume_info)887 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
888 const VolumeInfo& volume_info) {
889 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
890 BroadcastMountCompletedEvent(
891 profile_,
892 file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
893 error_code,
894 volume_info,
895 false);
896 }
897
OnFormatStarted(const std::string & device_path,bool success)898 void EventRouter::OnFormatStarted(const std::string& device_path,
899 bool success) {
900 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
901
902 if (success) {
903 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_START,
904 device_path);
905 } else {
906 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
907 device_path);
908 }
909 }
910
OnFormatCompleted(const std::string & device_path,bool success)911 void EventRouter::OnFormatCompleted(const std::string& device_path,
912 bool success) {
913 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
914 DispatchDeviceEvent(success ?
915 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS :
916 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
917 device_path);
918 }
919
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)920 void EventRouter::Observe(int type,
921 const content::NotificationSource& source,
922 const content::NotificationDetails& details) {
923 if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
924 Profile* const added_profile = content::Source<Profile>(source).ptr();
925 if (!added_profile->IsOffTheRecord())
926 GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
927
928 BroadcastEvent(profile_,
929 file_browser_private::OnProfileAdded::kEventName,
930 file_browser_private::OnProfileAdded::Create());
931 }
932 }
933
RegisterMultiUserWindowManagerObserver()934 void EventRouter::RegisterMultiUserWindowManagerObserver() {
935 if (multi_user_window_manager_observer_registered_)
936 return;
937 chrome::MultiUserWindowManager* const multi_user_window_manager =
938 chrome::MultiUserWindowManager::GetInstance();
939 if (multi_user_window_manager) {
940 multi_user_window_manager->AddObserver(this);
941 multi_user_window_manager_observer_registered_ = true;
942 }
943 }
944
OnOwnerEntryChanged(aura::Window * window)945 void EventRouter::OnOwnerEntryChanged(aura::Window* window) {
946 BroadcastEvent(profile_,
947 file_browser_private::OnDesktopChanged::kEventName,
948 file_browser_private::OnDesktopChanged::Create());
949 }
950
951 } // namespace file_manager
952