media_galleries_api.cc revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
1dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// Use of this source code is governed by a BSD-style license that can be
3dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// found in the LICENSE file.
4dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor
5dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// Implements the Chrome Extensions Media Galleries API.
6dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor
7dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "chrome/browser/extensions/api/media_galleries/media_galleries_api.h"
8dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor
9dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include <set>
10dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include <string>
11dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include <vector>
12dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor
13dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "apps/shell_window.h"
14dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "apps/shell_window_registry.h"
15dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/memory/scoped_ptr.h"
16dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/platform_file.h"
17dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/stl_util.h"
18dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/strings/string_number_conversions.h"
19dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/values.h"
20dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "chrome/browser/browser_process.h"
21dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "chrome/browser/media_galleries/media_file_system_registry.h"
22#include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
23#include "chrome/browser/storage_monitor/storage_monitor.h"
24#include "chrome/browser/ui/chrome_select_file_policy.h"
25#include "chrome/common/extensions/api/media_galleries.h"
26#include "chrome/common/extensions/extension.h"
27#include "chrome/common/extensions/permissions/api_permission.h"
28#include "chrome/common/extensions/permissions/media_galleries_permission.h"
29#include "chrome/common/extensions/permissions/permissions_data.h"
30#include "chrome/common/pref_names.h"
31#include "components/web_modal/web_contents_modal_dialog_manager.h"
32#include "content/public/browser/child_process_security_policy.h"
33#include "content/public/browser/render_process_host.h"
34#include "content/public/browser/render_view_host.h"
35#include "content/public/browser/web_contents.h"
36
37#if defined(OS_WIN)
38#include "base/strings/sys_string_conversions.h"
39#endif
40
41using apps::ShellWindow;
42using chrome::MediaFileSystemInfo;
43using chrome::MediaFileSystemRegistry;
44using chrome::MediaFileSystemsCallback;
45using content::ChildProcessSecurityPolicy;
46using content::WebContents;
47using web_modal::WebContentsModalDialogManager;
48
49namespace MediaGalleries = extensions::api::media_galleries;
50namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
51
52namespace extensions {
53
54namespace {
55
56const char kDisallowedByPolicy[] =
57    "Media Galleries API is disallowed by policy: ";
58
59const char kDeviceIdKey[] = "deviceId";
60const char kGalleryIdKey[] = "galleryId";
61const char kIsMediaDeviceKey[] = "isMediaDevice";
62const char kIsRemovableKey[] = "isRemovable";
63const char kNameKey[] = "name";
64
65// Checks whether the MediaGalleries API is currently accessible (it may be
66// disallowed even if an extension has the requisite permission).
67bool ApiIsAccessible(std::string* error) {
68  if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
69    *error = std::string(kDisallowedByPolicy) +
70        prefs::kAllowFileSelectionDialogs;
71    return false;
72  }
73
74  return true;
75}
76
77}  // namespace
78
79MediaGalleriesGetMediaFileSystemsFunction::
80    ~MediaGalleriesGetMediaFileSystemsFunction() {}
81
82bool MediaGalleriesGetMediaFileSystemsFunction::RunImpl() {
83  if (!ApiIsAccessible(&error_))
84    return false;
85
86  scoped_ptr<GetMediaFileSystems::Params> params(
87      GetMediaFileSystems::Params::Create(*args_));
88  EXTENSION_FUNCTION_VALIDATE(params.get());
89  MediaGalleries::GetMediaFileSystemsInteractivity interactive =
90      MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
91  if (params->details.get() && params->details->interactive != MediaGalleries::
92         GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
93    interactive = params->details->interactive;
94  }
95
96  chrome::StorageMonitor::GetInstance()->EnsureInitialized(base::Bind(
97      &MediaGalleriesGetMediaFileSystemsFunction::OnStorageMonitorInit,
98      this,
99      interactive));
100  return true;
101}
102
103void MediaGalleriesGetMediaFileSystemsFunction::OnStorageMonitorInit(
104    MediaGalleries::GetMediaFileSystemsInteractivity interactive) {
105  switch (interactive) {
106    case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: {
107      // The MediaFileSystemRegistry only updates preferences for extensions
108      // that it knows are in use. Since this may be the first call to
109      // chrome.getMediaFileSystems for this extension, call
110      // GetMediaFileSystemsForExtension() here solely so that
111      // MediaFileSystemRegistry will send preference changes.
112      GetMediaFileSystemsForExtension(base::Bind(
113          &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this));
114      return;
115    }
116    case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
117      GetMediaFileSystemsForExtension(base::Bind(
118          &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
119          this));
120      return;
121    }
122    case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
123      GetAndReturnGalleries();
124      return;
125    case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
126      NOTREACHED();
127  }
128  SendResponse(false);
129}
130
131void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
132    const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
133  ShowDialog();
134}
135
136void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
137    const std::vector<MediaFileSystemInfo>& filesystems) {
138  if (filesystems.empty())
139    ShowDialog();
140  else
141    ReturnGalleries(filesystems);
142}
143
144void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
145  GetMediaFileSystemsForExtension(base::Bind(
146      &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
147}
148
149void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
150    const std::vector<MediaFileSystemInfo>& filesystems) {
151  content::RenderViewHost* rvh = render_view_host();
152  if (!rvh) {
153    SendResponse(false);
154    return;
155  }
156  MediaGalleriesPermission::CheckParam read_param(
157      MediaGalleriesPermission::kReadPermission);
158  bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
159      GetExtension(), APIPermission::kMediaGalleries, &read_param);
160  MediaGalleriesPermission::CheckParam copy_to_param(
161      MediaGalleriesPermission::kCopyToPermission);
162  bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
163      GetExtension(), APIPermission::kMediaGalleries, &copy_to_param);
164
165  const int child_id = rvh->GetProcess()->GetID();
166  base::ListValue* list = new base::ListValue();
167  for (size_t i = 0; i < filesystems.size(); i++) {
168    scoped_ptr<base::DictionaryValue> file_system_dict_value(
169        new base::DictionaryValue());
170
171    // Send the file system id so the renderer can create a valid FileSystem
172    // object.
173    file_system_dict_value->SetStringWithoutPathExpansion(
174        "fsid", filesystems[i].fsid);
175
176    file_system_dict_value->SetStringWithoutPathExpansion(
177        kNameKey, filesystems[i].name);
178    file_system_dict_value->SetStringWithoutPathExpansion(
179        kGalleryIdKey,
180        base::Uint64ToString(filesystems[i].pref_id));
181    if (!filesystems[i].transient_device_id.empty()) {
182      file_system_dict_value->SetStringWithoutPathExpansion(
183          kDeviceIdKey, filesystems[i].transient_device_id);
184    }
185    file_system_dict_value->SetBooleanWithoutPathExpansion(
186        kIsRemovableKey, filesystems[i].removable);
187    file_system_dict_value->SetBooleanWithoutPathExpansion(
188        kIsMediaDeviceKey, filesystems[i].media_device);
189
190    list->Append(file_system_dict_value.release());
191
192    if (filesystems[i].path.empty())
193      continue;
194
195    if (has_read_permission || has_copy_to_permission) {
196      content::ChildProcessSecurityPolicy* policy =
197          ChildProcessSecurityPolicy::GetInstance();
198      if (has_read_permission)
199        policy->GrantReadFileSystem(child_id, filesystems[i].fsid);
200      if (has_copy_to_permission)
201        policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid);
202    }
203  }
204
205  SetResult(list);
206  SendResponse(true);
207}
208
209void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
210  WebContents* contents = WebContents::FromRenderViewHost(render_view_host());
211  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
212      WebContentsModalDialogManager::FromWebContents(contents);
213  if (!web_contents_modal_dialog_manager) {
214    // If there is no WebContentsModalDialogManager, then this contents is
215    // probably the background page for an app. Try to find a shell window to
216    // host the dialog.
217    ShellWindow* window = apps::ShellWindowRegistry::Get(profile())->
218        GetCurrentShellWindowForApp(GetExtension()->id());
219    if (window) {
220      contents = window->web_contents();
221    } else {
222      // Abort showing the dialog. TODO(estade) Perhaps return an error instead.
223      GetAndReturnGalleries();
224      return;
225    }
226  }
227
228  // Controller will delete itself.
229  base::Closure cb = base::Bind(
230      &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
231  new chrome::MediaGalleriesDialogController(contents, *GetExtension(), cb);
232}
233
234void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
235    const chrome::MediaFileSystemsCallback& cb) {
236  if (!render_view_host()) {
237    cb.Run(std::vector<MediaFileSystemInfo>());
238    return;
239  }
240
241  DCHECK(chrome::StorageMonitor::GetInstance()->IsInitialized());
242  MediaFileSystemRegistry* registry =
243      g_browser_process->media_file_system_registry();
244  registry->GetMediaFileSystemsForExtension(
245      render_view_host(), GetExtension(), cb);
246}
247
248}  // namespace extensions
249