file_browser_event_router.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1// Copyright (c) 2011 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_browser_event_router.h"
6
7#include "base/json/json_writer.h"
8#include "base/memory/singleton.h"
9#include "base/stl_util-inl.h"
10#include "base/values.h"
11#include "chrome/browser/chromeos/cros/cros_library.h"
12#include "chrome/browser/chromeos/login/user_manager.h"
13#include "chrome/browser/chromeos/notifications/system_notification.h"
14#include "chrome/browser/extensions/extension_event_names.h"
15#include "chrome/browser/extensions/extension_event_router.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/extensions/file_manager_util.h"
18#include "grit/generated_resources.h"
19#include "grit/theme_resources.h"
20#include "ui/base/l10n/l10n_util.h"
21
22const char kDiskAddedEventType[] = "added";
23const char kDiskRemovedEventType[] = "removed";
24
25const char* DeviceTypeToString(chromeos::DeviceType type) {
26  switch (type) {
27    case chromeos::FLASH:
28      return "flash";
29    case chromeos::HDD:
30      return "hdd";
31    case chromeos::OPTICAL:
32      return "optical";
33    default:
34      break;
35  }
36  return "undefined";
37}
38
39DictionaryValue* DiskToDictionaryValue(
40    const chromeos::MountLibrary::Disk* disk) {
41  DictionaryValue* result = new DictionaryValue();
42  result->SetString("mountPath", disk->mount_path());
43  result->SetString("label", disk->device_label());
44  result->SetString("deviceType", DeviceTypeToString(disk->device_type()));
45  result->SetInteger("totalSizeKB", disk->total_size() / 1024);
46  result->SetBoolean("readOnly", disk->is_read_only());
47  return result;
48}
49
50ExtensionFileBrowserEventRouter::ExtensionFileBrowserEventRouter()
51    : profile_(NULL) {
52}
53
54ExtensionFileBrowserEventRouter::~ExtensionFileBrowserEventRouter() {
55}
56
57void ExtensionFileBrowserEventRouter::ObserveFileSystemEvents(
58    Profile* profile) {
59  if (!profile)
60    return;
61  profile_ = profile;
62  if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
63    return;
64  if (chromeos::UserManager::Get()->user_is_logged_in()) {
65    chromeos::MountLibrary* lib =
66        chromeos::CrosLibrary::Get()->GetMountLibrary();
67    lib->RemoveObserver(this);
68    lib->AddObserver(this);
69    lib->RequestMountInfoRefresh();
70  }
71}
72
73void ExtensionFileBrowserEventRouter::StopObservingFileSystemEvents() {
74  if (!profile_)
75    return;
76  if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
77    return;
78  chromeos::MountLibrary* lib =
79      chromeos::CrosLibrary::Get()->GetMountLibrary();
80  lib->RemoveObserver(this);
81  profile_ = NULL;
82}
83
84// static
85ExtensionFileBrowserEventRouter*
86    ExtensionFileBrowserEventRouter::GetInstance() {
87  return Singleton<ExtensionFileBrowserEventRouter>::get();
88}
89
90void ExtensionFileBrowserEventRouter::DiskChanged(
91    chromeos::MountLibraryEventType event,
92    const chromeos::MountLibrary::Disk* disk) {
93  if (event == chromeos::MOUNT_DISK_ADDED) {
94    OnDiskAdded(disk);
95  } else if (event == chromeos::MOUNT_DISK_REMOVED) {
96    OnDiskRemoved(disk);
97  } else if (event == chromeos::MOUNT_DISK_CHANGED) {
98    OnDiskChanged(disk);
99  }
100}
101
102void ExtensionFileBrowserEventRouter::DeviceChanged(
103    chromeos::MountLibraryEventType event,
104    const std::string& device_path) {
105  if (event == chromeos::MOUNT_DEVICE_ADDED) {
106    OnDeviceAdded(device_path);
107  } else if (event == chromeos::MOUNT_DEVICE_REMOVED) {
108    OnDeviceRemoved(device_path);
109  } else if (event == chromeos::MOUNT_DEVICE_SCANNED) {
110    OnDeviceScanned(device_path);
111  }
112}
113
114void ExtensionFileBrowserEventRouter::DispatchEvent(
115    const chromeos::MountLibrary::Disk* disk, bool added) {
116  if (!profile_) {
117    NOTREACHED();
118    return;
119  }
120
121  ListValue args;
122  DictionaryValue* mount_info = new DictionaryValue();
123  args.Append(mount_info);
124  mount_info->SetString("eventType",
125                        added ? kDiskAddedEventType : kDiskRemovedEventType);
126  DictionaryValue* disk_info = DiskToDictionaryValue(disk);
127  mount_info->Set("volumeInfo", disk_info);
128
129  std::string args_json;
130  base::JSONWriter::Write(&args, false /* pretty_print */, &args_json);
131  profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
132      extension_event_names::kOnFileBrowserDiskChanged, args_json, NULL,
133      GURL());
134}
135
136void ExtensionFileBrowserEventRouter::OnDiskAdded(
137    const chromeos::MountLibrary::Disk* disk) {
138  VLOG(1) << "Disk added: " << disk->device_path();
139  if (disk->device_path().empty()) {
140    VLOG(1) << "Empty system path for " << disk->device_path();
141    return;
142  }
143  if (disk->is_parent()) {
144    if (!disk->has_media())
145      HideDeviceNotification(disk->system_path());
146    return;
147  }
148
149  // If disk is not mounted yet, give it a try.
150  if (disk->mount_path().empty()) {
151    // Initiate disk mount operation.
152    chromeos::MountLibrary* lib =
153        chromeos::CrosLibrary::Get()->GetMountLibrary();
154    lib->MountPath(disk->device_path().c_str());
155  }
156}
157
158void ExtensionFileBrowserEventRouter::OnDiskRemoved(
159    const chromeos::MountLibrary::Disk* disk) {
160  VLOG(1) << "Disk removed: " << disk->device_path();
161  HideDeviceNotification(disk->system_path());
162  MountPointMap::iterator iter = mounted_devices_.find(disk->device_path());
163  if (iter == mounted_devices_.end())
164    return;
165
166  chromeos::MountLibrary* lib =
167      chromeos::CrosLibrary::Get()->GetMountLibrary();
168  // TODO(zelidrag): This for some reason does not work as advertized.
169  // we might need to clean up mount directory on FILE thread here as well.
170  lib->UnmountPath(disk->device_path().c_str());
171
172  DispatchEvent(disk, false);
173  mounted_devices_.erase(iter);
174}
175
176void ExtensionFileBrowserEventRouter::OnDiskChanged(
177    const chromeos::MountLibrary::Disk* disk) {
178  VLOG(1) << "Disk changed : " << disk->device_path();
179  if (!disk->mount_path().empty()) {
180    HideDeviceNotification(disk->system_path());
181    // Remember this mount point.
182    if (mounted_devices_.find(disk->device_path()) == mounted_devices_.end()) {
183      mounted_devices_.insert(
184          std::pair<std::string, std::string>(disk->device_path(),
185                                              disk->mount_path()));
186      DispatchEvent(disk, true);
187      HideDeviceNotification(disk->system_path());
188      FileManagerUtil::ShowFullTabUrl(profile_, FilePath(disk->mount_path()));
189    }
190  }
191}
192
193void ExtensionFileBrowserEventRouter::OnDeviceAdded(
194    const std::string& device_path) {
195  VLOG(1) << "Device added : " << device_path;
196  // TODO(zelidrag): Find better icon here.
197  ShowDeviceNotification(device_path, IDR_PAGEINFO_INFO,
198      l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_SCANNING_MESSAGE));
199
200}
201
202void ExtensionFileBrowserEventRouter::OnDeviceRemoved(
203    const std::string& system_path) {
204  HideDeviceNotification(system_path);
205}
206
207void ExtensionFileBrowserEventRouter::OnDeviceScanned(
208    const std::string& device_path) {
209  VLOG(1) << "Device scanned : " << device_path;
210}
211
212void ExtensionFileBrowserEventRouter::ShowDeviceNotification(
213    const std::string& system_path, int icon_resource_id,
214    const string16& message) {
215  NotificationMap::iterator iter = FindNotificationForPath(system_path);
216  std::string mount_path;
217  if (iter != notifications_.end()) {
218    iter->second->Show(message, false, false);
219  } else {
220    if (!profile_) {
221      NOTREACHED();
222      return;
223    }
224    chromeos::SystemNotification* notification =
225        new chromeos::SystemNotification(
226            profile_,
227            system_path,
228            icon_resource_id,
229            l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_DETECTION_TITLE));
230    notifications_.insert(NotificationMap::value_type(system_path,
231        linked_ptr<chromeos::SystemNotification>(notification)));
232    notification->Show(message, false, false);
233  }
234}
235
236void ExtensionFileBrowserEventRouter::HideDeviceNotification(
237    const std::string& system_path) {
238  NotificationMap::iterator iter = FindNotificationForPath(system_path);
239  if (iter != notifications_.end()) {
240    iter->second->Hide();
241    notifications_.erase(iter);
242  }
243}
244
245ExtensionFileBrowserEventRouter::NotificationMap::iterator
246    ExtensionFileBrowserEventRouter::FindNotificationForPath(
247        const std::string& system_path) {
248  for (NotificationMap::iterator iter = notifications_.begin();
249       iter != notifications_.end();
250       ++iter) {
251    const std::string& notification_device_path = iter->first;
252    // Doing a sub string match so that we find if this new one is a subdevice
253    // of another already inserted device.
254    if (StartsWithASCII(system_path, notification_device_path, true)) {
255      return iter;
256    }
257  }
258  return notifications_.end();
259}
260