private_api_misc.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 "chrome/browser/chromeos/extensions/file_manager/private_api_misc.h"
6
7#include "apps/app_window.h"
8#include "apps/app_window_registry.h"
9#include "ash/frame/frame_util.h"
10#include "base/files/file_path.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/chromeos/drive/file_system_util.h"
16#include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
17#include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
18#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
19#include "chrome/browser/chromeos/file_manager/app_installer.h"
20#include "chrome/browser/chromeos/file_manager/zip_file_creator.h"
21#include "chrome/browser/chromeos/login/users/user_manager.h"
22#include "chrome/browser/chromeos/profiles/profile_helper.h"
23#include "chrome/browser/chromeos/settings/cros_settings.h"
24#include "chrome/browser/devtools/devtools_window.h"
25#include "chrome/browser/drive/event_logger.h"
26#include "chrome/browser/extensions/devtools_util.h"
27#include "chrome/browser/lifetime/application_lifetime.h"
28#include "chrome/browser/profiles/profile.h"
29#include "chrome/browser/profiles/profile_manager.h"
30#include "chrome/browser/profiles/profiles_state.h"
31#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
32#include "chrome/browser/signin/signin_manager_factory.h"
33#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
34#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
35#include "chrome/common/extensions/api/file_browser_private.h"
36#include "chrome/common/pref_names.h"
37#include "components/signin/core/browser/profile_oauth2_token_service.h"
38#include "components/signin/core/browser/signin_manager.h"
39#include "content/public/browser/render_view_host.h"
40#include "content/public/browser/web_contents.h"
41#include "content/public/common/page_zoom.h"
42#include "google_apis/drive/auth_service.h"
43#include "ui/base/webui/web_ui_util.h"
44#include "url/gurl.h"
45
46namespace extensions {
47
48namespace {
49const char kCWSScope[] = "https://www.googleapis.com/auth/chromewebstore";
50
51// Obtains the current app window.
52apps::AppWindow* GetCurrentAppWindow(ChromeSyncExtensionFunction* function) {
53  apps::AppWindowRegistry* const app_window_registry =
54      apps::AppWindowRegistry::Get(function->GetProfile());
55  content::WebContents* const contents = function->GetAssociatedWebContents();
56  content::RenderViewHost* const render_view_host =
57      contents ? contents->GetRenderViewHost() : NULL;
58  return render_view_host ? app_window_registry->GetAppWindowForRenderViewHost(
59                                render_view_host)
60                          : NULL;
61}
62
63std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >
64GetLoggedInProfileInfoList(content::WebContents* contents) {
65  DCHECK(chromeos::UserManager::IsInitialized());
66  const std::vector<Profile*>& profiles =
67      g_browser_process->profile_manager()->GetLoadedProfiles();
68  std::set<Profile*> original_profiles;
69  std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >
70      result_profiles;
71
72  for (size_t i = 0; i < profiles.size(); ++i) {
73    // Filter the profile.
74    Profile* const profile = profiles[i]->GetOriginalProfile();
75    if (original_profiles.count(profile))
76      continue;
77    original_profiles.insert(profile);
78    const chromeos::User* const user =
79        chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
80    if (!user || !user->is_logged_in())
81      continue;
82
83    // Make a ProfileInfo.
84    linked_ptr<api::file_browser_private::ProfileInfo> profile_info(
85        new api::file_browser_private::ProfileInfo());
86    profile_info->profile_id = multi_user_util::GetUserIDFromProfile(profile);
87    profile_info->display_name = UTF16ToUTF8(user->GetDisplayName());
88    // TODO(hirono): Remove the property from the profile_info.
89    profile_info->is_current_profile = true;
90
91    // Make an icon URL of the profile.
92    if (contents) {
93      const gfx::Image& image =
94          ash::GetAvatarImageForContext(contents->GetBrowserContext());
95      const gfx::ImageSkia& skia = image.AsImageSkia();
96      profile_info->profile_image.reset(
97          new api::file_browser_private::ImageSet);
98      profile_info->profile_image->scale1x_url =
99          webui::GetBitmapDataUrl(skia.GetRepresentation(1.0f).sk_bitmap());
100      profile_info->profile_image->scale2x_url =
101          webui::GetBitmapDataUrl(skia.GetRepresentation(2.0f).sk_bitmap());
102    }
103    result_profiles.push_back(profile_info);
104  }
105
106  return result_profiles;
107}
108} // namespace
109
110bool FileBrowserPrivateLogoutUserForReauthenticationFunction::RunSync() {
111  chromeos::User* user =
112      chromeos::ProfileHelper::Get()->GetUserByProfile(GetProfile());
113  if (user) {
114    chromeos::UserManager::Get()->SaveUserOAuthStatus(
115        user->email(),
116        chromeos::User::OAUTH2_TOKEN_STATUS_INVALID);
117  }
118
119  chrome::AttemptUserExit();
120  return true;
121}
122
123bool FileBrowserPrivateGetPreferencesFunction::RunSync() {
124  api::file_browser_private::Preferences result;
125  const PrefService* const service = GetProfile()->GetPrefs();
126
127  result.drive_enabled = drive::util::IsDriveEnabledForProfile(GetProfile());
128  result.cellular_disabled =
129      service->GetBoolean(prefs::kDisableDriveOverCellular);
130  result.hosted_files_disabled =
131      service->GetBoolean(prefs::kDisableDriveHostedFiles);
132  result.use24hour_clock = service->GetBoolean(prefs::kUse24HourClock);
133  result.allow_redeem_offers = true;
134  if (!chromeos::CrosSettings::Get()->GetBoolean(
135          chromeos::kAllowRedeemChromeOsRegistrationOffers,
136          &result.allow_redeem_offers)) {
137    result.allow_redeem_offers = true;
138  }
139
140  SetResult(result.ToValue().release());
141
142  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
143  if (logger)
144    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
145  return true;
146}
147
148bool FileBrowserPrivateSetPreferencesFunction::RunSync() {
149  using extensions::api::file_browser_private::SetPreferences::Params;
150  const scoped_ptr<Params> params(Params::Create(*args_));
151  EXTENSION_FUNCTION_VALIDATE(params);
152
153  PrefService* const service = GetProfile()->GetPrefs();
154
155  if (params->change_info.cellular_disabled)
156    service->SetBoolean(prefs::kDisableDriveOverCellular,
157                        *params->change_info.cellular_disabled);
158
159  if (params->change_info.hosted_files_disabled)
160    service->SetBoolean(prefs::kDisableDriveHostedFiles,
161                        *params->change_info.hosted_files_disabled);
162
163  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
164  if (logger)
165    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
166  return true;
167}
168
169FileBrowserPrivateZipSelectionFunction::
170    FileBrowserPrivateZipSelectionFunction() {}
171
172FileBrowserPrivateZipSelectionFunction::
173    ~FileBrowserPrivateZipSelectionFunction() {}
174
175bool FileBrowserPrivateZipSelectionFunction::RunAsync() {
176  using extensions::api::file_browser_private::ZipSelection::Params;
177  const scoped_ptr<Params> params(Params::Create(*args_));
178  EXTENSION_FUNCTION_VALIDATE(params);
179
180  // First param is the source directory URL.
181  if (params->dir_url.empty())
182    return false;
183
184  base::FilePath src_dir = file_manager::util::GetLocalPathFromURL(
185      render_view_host(), GetProfile(), GURL(params->dir_url));
186  if (src_dir.empty())
187    return false;
188
189  // Second param is the list of selected file URLs.
190  if (params->selection_urls.empty())
191    return false;
192
193  std::vector<base::FilePath> files;
194  for (size_t i = 0; i < params->selection_urls.size(); ++i) {
195    base::FilePath path = file_manager::util::GetLocalPathFromURL(
196        render_view_host(), GetProfile(), GURL(params->selection_urls[i]));
197    if (path.empty())
198      return false;
199    files.push_back(path);
200  }
201
202  // Third param is the name of the output zip file.
203  if (params->dest_name.empty())
204    return false;
205
206  // Check if the dir path is under Drive mount point.
207  // TODO(hshi): support create zip file on Drive (crbug.com/158690).
208  if (drive::util::IsUnderDriveMountPoint(src_dir))
209    return false;
210
211  base::FilePath dest_file = src_dir.Append(params->dest_name);
212  std::vector<base::FilePath> src_relative_paths;
213  for (size_t i = 0; i != files.size(); ++i) {
214    const base::FilePath& file_path = files[i];
215
216    // Obtain the relative path of |file_path| under |src_dir|.
217    base::FilePath relative_path;
218    if (!src_dir.AppendRelativePath(file_path, &relative_path))
219      return false;
220    src_relative_paths.push_back(relative_path);
221  }
222
223  (new file_manager::ZipFileCreator(
224       base::Bind(&FileBrowserPrivateZipSelectionFunction::OnZipDone, this),
225       src_dir,
226       src_relative_paths,
227       dest_file))->Start();
228  return true;
229}
230
231void FileBrowserPrivateZipSelectionFunction::OnZipDone(bool success) {
232  SetResult(new base::FundamentalValue(success));
233  SendResponse(true);
234}
235
236bool FileBrowserPrivateZoomFunction::RunSync() {
237  using extensions::api::file_browser_private::Zoom::Params;
238  const scoped_ptr<Params> params(Params::Create(*args_));
239  EXTENSION_FUNCTION_VALIDATE(params);
240
241  content::PageZoom zoom_type;
242  switch (params->operation) {
243    case api::file_browser_private::ZOOM_OPERATION_TYPE_IN:
244      zoom_type = content::PAGE_ZOOM_IN;
245      break;
246    case api::file_browser_private::ZOOM_OPERATION_TYPE_OUT:
247      zoom_type = content::PAGE_ZOOM_OUT;
248      break;
249    case api::file_browser_private::ZOOM_OPERATION_TYPE_RESET:
250      zoom_type = content::PAGE_ZOOM_RESET;
251      break;
252    default:
253      NOTREACHED();
254      return false;
255  }
256  render_view_host()->Zoom(zoom_type);
257  return true;
258}
259
260bool FileBrowserPrivateInstallWebstoreItemFunction::RunAsync() {
261  using extensions::api::file_browser_private::InstallWebstoreItem::Params;
262  const scoped_ptr<Params> params(Params::Create(*args_));
263  EXTENSION_FUNCTION_VALIDATE(params);
264
265  if (params->item_id.empty())
266    return false;
267
268  const extensions::WebstoreStandaloneInstaller::Callback callback =
269      base::Bind(
270          &FileBrowserPrivateInstallWebstoreItemFunction::OnInstallComplete,
271          this);
272
273  scoped_refptr<file_manager::AppInstaller> installer(
274      new file_manager::AppInstaller(
275          GetAssociatedWebContents(),
276          params->item_id,
277          GetProfile(),
278          callback));
279  // installer will be AddRef()'d in BeginInstall().
280  installer->BeginInstall();
281  return true;
282}
283
284void FileBrowserPrivateInstallWebstoreItemFunction::OnInstallComplete(
285    bool success,
286    const std::string& error) {
287  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
288  if (success) {
289    if (logger) {
290      logger->Log(logging::LOG_INFO,
291                  "App install succeeded. (item id: %s)",
292                  webstore_item_id_.c_str());
293    }
294  } else {
295    if (logger) {
296      logger->Log(logging::LOG_ERROR,
297                  "App install failed. (item id: %s, reason: %s)",
298                  webstore_item_id_.c_str(),
299                  error.c_str());
300    }
301    SetError(error);
302  }
303
304  SendResponse(success);
305}
306
307FileBrowserPrivateRequestWebStoreAccessTokenFunction::
308    FileBrowserPrivateRequestWebStoreAccessTokenFunction() {
309}
310
311FileBrowserPrivateRequestWebStoreAccessTokenFunction::
312    ~FileBrowserPrivateRequestWebStoreAccessTokenFunction() {
313}
314
315bool FileBrowserPrivateRequestWebStoreAccessTokenFunction::RunAsync() {
316  std::vector<std::string> scopes;
317  scopes.push_back(kCWSScope);
318
319  ProfileOAuth2TokenService* oauth_service =
320      ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
321  net::URLRequestContextGetter* url_request_context_getter =
322      g_browser_process->system_request_context();
323
324  if (!oauth_service) {
325    drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
326    if (logger) {
327      logger->Log(logging::LOG_ERROR,
328                  "CWS OAuth token fetch failed. OAuth2TokenService can't "
329                  "be retrieved.");
330    }
331    SetResult(base::Value::CreateNullValue());
332    return false;
333  }
334
335  SigninManagerBase* signin_manager =
336      SigninManagerFactory::GetForProfile(GetProfile());
337  auth_service_.reset(new google_apis::AuthService(
338      oauth_service,
339      signin_manager->GetAuthenticatedAccountId(),
340      url_request_context_getter,
341      scopes));
342  auth_service_->StartAuthentication(base::Bind(
343      &FileBrowserPrivateRequestWebStoreAccessTokenFunction::
344          OnAccessTokenFetched,
345      this));
346
347  return true;
348}
349
350void FileBrowserPrivateRequestWebStoreAccessTokenFunction::OnAccessTokenFetched(
351    google_apis::GDataErrorCode code,
352    const std::string& access_token) {
353  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
354
355  if (code == google_apis::HTTP_SUCCESS) {
356    DCHECK(auth_service_->HasAccessToken());
357    DCHECK(access_token == auth_service_->access_token());
358    if (logger)
359      logger->Log(logging::LOG_INFO, "CWS OAuth token fetch succeeded.");
360    SetResult(new base::StringValue(access_token));
361    SendResponse(true);
362  } else {
363    if (logger) {
364      logger->Log(logging::LOG_ERROR,
365                  "CWS OAuth token fetch failed. (GDataErrorCode: %s)",
366                  google_apis::GDataErrorCodeToString(code).c_str());
367    }
368    SetResult(base::Value::CreateNullValue());
369    SendResponse(false);
370  }
371}
372
373bool FileBrowserPrivateGetProfilesFunction::RunSync() {
374  const std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >&
375      profiles = GetLoggedInProfileInfoList(GetAssociatedWebContents());
376
377  // Obtains the display profile ID.
378  apps::AppWindow* const app_window = GetCurrentAppWindow(this);
379  chrome::MultiUserWindowManager* const window_manager =
380      chrome::MultiUserWindowManager::GetInstance();
381  const std::string current_profile_id =
382      multi_user_util::GetUserIDFromProfile(GetProfile());
383  const std::string display_profile_id =
384      window_manager && app_window ? window_manager->GetUserPresentingWindow(
385                                         app_window->GetNativeWindow())
386                                   : "";
387
388  results_ = api::file_browser_private::GetProfiles::Results::Create(
389      profiles,
390      current_profile_id,
391      display_profile_id.empty() ? current_profile_id : display_profile_id);
392  return true;
393}
394
395bool FileBrowserPrivateVisitDesktopFunction::RunSync() {
396  using api::file_browser_private::VisitDesktop::Params;
397  const scoped_ptr<Params> params(Params::Create(*args_));
398  const std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >&
399      profiles = GetLoggedInProfileInfoList(GetAssociatedWebContents());
400
401  chrome::MultiUserWindowManager* const window_manager =
402      chrome::MultiUserWindowManager::GetInstance();
403  DCHECK(window_manager);
404
405  // Check if the target user is logged-in or not.
406  bool logged_in = false;
407  for (size_t i = 0; i < profiles.size(); ++i) {
408    if (profiles[i]->profile_id == params->profile_id) {
409      logged_in = true;
410      break;
411    }
412  }
413  if (!logged_in) {
414    SetError("The user is not logged-in now.");
415    return false;
416  }
417
418  // Look for the current app window.
419  apps::AppWindow* const app_window = GetCurrentAppWindow(this);
420  if (!app_window) {
421    SetError("Target window is not found.");
422    return false;
423  }
424
425  // Observe owner changes of windows.
426  file_manager::EventRouter* const event_router =
427      file_manager::FileBrowserPrivateAPI::Get(GetProfile())->event_router();
428  event_router->RegisterMultiUserWindowManagerObserver();
429
430  // Move the window to the user's desktop.
431  window_manager->ShowWindowForUser(app_window->GetNativeWindow(),
432                                    params->profile_id);
433
434  // Check the result.
435  if (!window_manager->IsWindowOnDesktopOfUser(app_window->GetNativeWindow(),
436                                               params->profile_id)) {
437    SetError("The window cannot visit the desktop.");
438    return false;
439  }
440
441  return true;
442}
443
444bool FileBrowserPrivateOpenInspectorFunction::RunSync() {
445  using extensions::api::file_browser_private::OpenInspector::Params;
446  const scoped_ptr<Params> params(Params::Create(*args_));
447  EXTENSION_FUNCTION_VALIDATE(params);
448
449  switch (params->type) {
450    case extensions::api::file_browser_private::INSPECTION_TYPE_NORMAL:
451      // Open inspector for foreground page.
452      DevToolsWindow::OpenDevToolsWindow(render_view_host());
453      break;
454    case extensions::api::file_browser_private::INSPECTION_TYPE_CONSOLE:
455      // Open inspector for foreground page and bring focus to the console.
456      DevToolsWindow::OpenDevToolsWindow(render_view_host(),
457                                         DevToolsToggleAction::ShowConsole());
458      break;
459    case extensions::api::file_browser_private::INSPECTION_TYPE_ELEMENT:
460      // Open inspector for foreground page in inspect element mode.
461      DevToolsWindow::OpenDevToolsWindow(render_view_host(),
462                                         DevToolsToggleAction::Inspect());
463      break;
464    case extensions::api::file_browser_private::INSPECTION_TYPE_BACKGROUND:
465      // Open inspector for background page.
466      extensions::devtools_util::InspectBackgroundPage(GetExtension(),
467                                                       GetProfile());
468      break;
469    default:
470      NOTREACHED();
471      SetError(
472          base::StringPrintf("Unexpected inspection type(%d) is specified.",
473                             static_cast<int>(params->type)));
474      return false;
475  }
476  return true;
477}
478
479}  // namespace extensions
480