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
22 const char kDiskAddedEventType[] = "added";
23 const char kDiskRemovedEventType[] = "removed";
24
DeviceTypeToString(chromeos::DeviceType type)25 const 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
DiskToDictionaryValue(const chromeos::MountLibrary::Disk * disk)39 DictionaryValue* 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
ExtensionFileBrowserEventRouter()50 ExtensionFileBrowserEventRouter::ExtensionFileBrowserEventRouter()
51 : profile_(NULL) {
52 }
53
~ExtensionFileBrowserEventRouter()54 ExtensionFileBrowserEventRouter::~ExtensionFileBrowserEventRouter() {
55 }
56
ObserveFileSystemEvents(Profile * profile)57 void 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
StopObservingFileSystemEvents()73 void 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
85 ExtensionFileBrowserEventRouter*
GetInstance()86 ExtensionFileBrowserEventRouter::GetInstance() {
87 return Singleton<ExtensionFileBrowserEventRouter>::get();
88 }
89
DiskChanged(chromeos::MountLibraryEventType event,const chromeos::MountLibrary::Disk * disk)90 void 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
DeviceChanged(chromeos::MountLibraryEventType event,const std::string & device_path)102 void 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
DispatchEvent(const chromeos::MountLibrary::Disk * disk,bool added)114 void 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
OnDiskAdded(const chromeos::MountLibrary::Disk * disk)136 void 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
OnDiskRemoved(const chromeos::MountLibrary::Disk * disk)158 void 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
OnDiskChanged(const chromeos::MountLibrary::Disk * disk)176 void 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
OnDeviceAdded(const std::string & device_path)193 void 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
OnDeviceRemoved(const std::string & system_path)202 void ExtensionFileBrowserEventRouter::OnDeviceRemoved(
203 const std::string& system_path) {
204 HideDeviceNotification(system_path);
205 }
206
OnDeviceScanned(const std::string & device_path)207 void ExtensionFileBrowserEventRouter::OnDeviceScanned(
208 const std::string& device_path) {
209 VLOG(1) << "Device scanned : " << device_path;
210 }
211
ShowDeviceNotification(const std::string & system_path,int icon_resource_id,const string16 & message)212 void 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
HideDeviceNotification(const std::string & system_path)236 void 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
245 ExtensionFileBrowserEventRouter::NotificationMap::iterator
FindNotificationForPath(const std::string & system_path)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