private_api_misc.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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/utf_string_conversions.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chromeos/drive/file_system_util.h"
15#include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
16#include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
17#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
18#include "chrome/browser/chromeos/file_manager/app_installer.h"
19#include "chrome/browser/chromeos/login/user_manager.h"
20#include "chrome/browser/chromeos/settings/cros_settings.h"
21#include "chrome/browser/drive/event_logger.h"
22#include "chrome/browser/lifetime/application_lifetime.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/profiles/profile_manager.h"
25#include "chrome/browser/profiles/profiles_state.h"
26#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
27#include "chrome/browser/signin/signin_manager_factory.h"
28#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
29#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
30#include "chrome/common/extensions/api/file_browser_private.h"
31#include "chrome/common/pref_names.h"
32#include "components/signin/core/browser/profile_oauth2_token_service.h"
33#include "components/signin/core/browser/signin_manager.h"
34#include "content/public/browser/render_view_host.h"
35#include "content/public/browser/web_contents.h"
36#include "content/public/common/page_zoom.h"
37#include "google_apis/drive/auth_service.h"
38#include "ui/base/webui/web_ui_util.h"
39#include "url/gurl.h"
40
41namespace extensions {
42
43namespace {
44const char kCWSScope[] = "https://www.googleapis.com/auth/chromewebstore";
45
46// Obtains the current app window.
47apps::AppWindow* GetCurrentAppWindow(ChromeSyncExtensionFunction* function) {
48  apps::AppWindowRegistry* const app_window_registry =
49      apps::AppWindowRegistry::Get(function->GetProfile());
50  content::WebContents* const contents = function->GetAssociatedWebContents();
51  content::RenderViewHost* const render_view_host =
52      contents ? contents->GetRenderViewHost() : NULL;
53  return render_view_host ? app_window_registry->GetAppWindowForRenderViewHost(
54                                render_view_host)
55                          : NULL;
56}
57
58std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >
59GetLoggedInProfileInfoList(content::WebContents* contents) {
60  DCHECK(chromeos::UserManager::IsInitialized());
61  const std::vector<Profile*>& profiles =
62      g_browser_process->profile_manager()->GetLoadedProfiles();
63  std::set<Profile*> original_profiles;
64  std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >
65      result_profiles;
66
67  for (size_t i = 0; i < profiles.size(); ++i) {
68    // Filter the profile.
69    Profile* const profile = profiles[i]->GetOriginalProfile();
70    if (original_profiles.count(profile))
71      continue;
72    original_profiles.insert(profile);
73    const chromeos::User* const user =
74        chromeos::UserManager::Get()->GetUserByProfile(profile);
75    if (!user || !user->is_logged_in())
76      continue;
77
78    // Make a ProfileInfo.
79    linked_ptr<api::file_browser_private::ProfileInfo> profile_info(
80        new api::file_browser_private::ProfileInfo());
81    profile_info->profile_id = multi_user_util::GetUserIDFromProfile(profile);
82    profile_info->display_name = UTF16ToUTF8(user->GetDisplayName());
83    // TODO(hirono): Remove the property from the profile_info.
84    profile_info->is_current_profile = true;
85
86    // Make an icon URL of the profile.
87    if (contents) {
88      const gfx::Image& image =
89          ash::GetAvatarImageForContext(contents->GetBrowserContext());
90      const gfx::ImageSkia& skia = image.AsImageSkia();
91      profile_info->profile_image.reset(
92          new api::file_browser_private::ImageSet);
93      profile_info->profile_image->scale1x_url =
94          webui::GetBitmapDataUrl(skia.GetRepresentation(1.0f).sk_bitmap());
95      profile_info->profile_image->scale2x_url =
96          webui::GetBitmapDataUrl(skia.GetRepresentation(2.0f).sk_bitmap());
97    }
98    result_profiles.push_back(profile_info);
99  }
100
101  return result_profiles;
102}
103} // namespace
104
105bool FileBrowserPrivateLogoutUserForReauthenticationFunction::RunImpl() {
106  chromeos::User* user =
107      chromeos::UserManager::Get()->GetUserByProfile(GetProfile());
108  if (user) {
109    chromeos::UserManager::Get()->SaveUserOAuthStatus(
110        user->email(),
111        chromeos::User::OAUTH2_TOKEN_STATUS_INVALID);
112  }
113
114  chrome::AttemptUserExit();
115  return true;
116}
117
118bool FileBrowserPrivateGetPreferencesFunction::RunImpl() {
119  api::file_browser_private::Preferences result;
120  const PrefService* const service = GetProfile()->GetPrefs();
121
122  result.drive_enabled = drive::util::IsDriveEnabledForProfile(GetProfile());
123  result.cellular_disabled =
124      service->GetBoolean(prefs::kDisableDriveOverCellular);
125  result.hosted_files_disabled =
126      service->GetBoolean(prefs::kDisableDriveHostedFiles);
127  result.use24hour_clock = service->GetBoolean(prefs::kUse24HourClock);
128  result.allow_redeem_offers = true;
129  if (!chromeos::CrosSettings::Get()->GetBoolean(
130          chromeos::kAllowRedeemChromeOsRegistrationOffers,
131          &result.allow_redeem_offers)) {
132    result.allow_redeem_offers = true;
133  }
134
135  SetResult(result.ToValue().release());
136
137  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
138  if (logger)
139    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
140  return true;
141}
142
143bool FileBrowserPrivateSetPreferencesFunction::RunImpl() {
144  using extensions::api::file_browser_private::SetPreferences::Params;
145  const scoped_ptr<Params> params(Params::Create(*args_));
146  EXTENSION_FUNCTION_VALIDATE(params);
147
148  PrefService* const service = GetProfile()->GetPrefs();
149
150  if (params->change_info.cellular_disabled)
151    service->SetBoolean(prefs::kDisableDriveOverCellular,
152                        *params->change_info.cellular_disabled);
153
154  if (params->change_info.hosted_files_disabled)
155    service->SetBoolean(prefs::kDisableDriveHostedFiles,
156                        *params->change_info.hosted_files_disabled);
157
158  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
159  if (logger)
160    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
161  return true;
162}
163
164FileBrowserPrivateZipSelectionFunction::
165    FileBrowserPrivateZipSelectionFunction() {}
166
167FileBrowserPrivateZipSelectionFunction::
168    ~FileBrowserPrivateZipSelectionFunction() {}
169
170bool FileBrowserPrivateZipSelectionFunction::RunImpl() {
171  using extensions::api::file_browser_private::ZipSelection::Params;
172  const scoped_ptr<Params> params(Params::Create(*args_));
173  EXTENSION_FUNCTION_VALIDATE(params);
174
175  // First param is the source directory URL.
176  if (params->dir_url.empty())
177    return false;
178
179  base::FilePath src_dir = file_manager::util::GetLocalPathFromURL(
180      render_view_host(), GetProfile(), GURL(params->dir_url));
181  if (src_dir.empty())
182    return false;
183
184  // Second param is the list of selected file URLs.
185  if (params->selection_urls.empty())
186    return false;
187
188  std::vector<base::FilePath> files;
189  for (size_t i = 0; i < params->selection_urls.size(); ++i) {
190    base::FilePath path = file_manager::util::GetLocalPathFromURL(
191        render_view_host(), GetProfile(), GURL(params->selection_urls[i]));
192    if (path.empty())
193      return false;
194    files.push_back(path);
195  }
196
197  // Third param is the name of the output zip file.
198  if (params->dest_name.empty())
199    return false;
200
201  // Check if the dir path is under Drive mount point.
202  // TODO(hshi): support create zip file on Drive (crbug.com/158690).
203  if (drive::util::IsUnderDriveMountPoint(src_dir))
204    return false;
205
206  base::FilePath dest_file = src_dir.Append(params->dest_name);
207  std::vector<base::FilePath> src_relative_paths;
208  for (size_t i = 0; i != files.size(); ++i) {
209    const base::FilePath& file_path = files[i];
210
211    // Obtain the relative path of |file_path| under |src_dir|.
212    base::FilePath relative_path;
213    if (!src_dir.AppendRelativePath(file_path, &relative_path))
214      return false;
215    src_relative_paths.push_back(relative_path);
216  }
217
218  zip_file_creator_ = new file_manager::ZipFileCreator(this,
219                                                       src_dir,
220                                                       src_relative_paths,
221                                                       dest_file);
222
223  // Keep the refcount until the zipping is complete on utility process.
224  AddRef();
225
226  zip_file_creator_->Start();
227  return true;
228}
229
230void FileBrowserPrivateZipSelectionFunction::OnZipDone(bool success) {
231  SetResult(new base::FundamentalValue(success));
232  SendResponse(true);
233  Release();
234}
235
236bool FileBrowserPrivateZoomFunction::RunImpl() {
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::RunImpl() {
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::RunImpl() {
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::RunImpl() {
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::RunImpl() {
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  // Check the multi-profile support.
402  if (!profiles::IsMultipleProfilesEnabled()) {
403    SetError("Multi-profile support is not enabled.");
404    return false;
405  }
406
407  chrome::MultiUserWindowManager* const window_manager =
408      chrome::MultiUserWindowManager::GetInstance();
409  DCHECK(window_manager);
410
411  // Check if the target user is logged-in or not.
412  bool logged_in = false;
413  for (size_t i = 0; i < profiles.size(); ++i) {
414    if (profiles[i]->profile_id == params->profile_id) {
415      logged_in = true;
416      break;
417    }
418  }
419  if (!logged_in) {
420    SetError("The user is not logged-in now.");
421    return false;
422  }
423
424  // Look for the current app window.
425  apps::AppWindow* const app_window = GetCurrentAppWindow(this);
426  if (!app_window) {
427    SetError("Target window is not found.");
428    return false;
429  }
430
431  // Observe owner changes of windows.
432  file_manager::EventRouter* const event_router =
433      file_manager::FileBrowserPrivateAPI::Get(GetProfile())->event_router();
434  event_router->RegisterMultiUserWindowManagerObserver();
435
436  // Move the window to the user's desktop.
437  window_manager->ShowWindowForUser(app_window->GetNativeWindow(),
438                                    params->profile_id);
439
440  // Check the result.
441  if (!window_manager->IsWindowOnDesktopOfUser(app_window->GetNativeWindow(),
442                                               params->profile_id)) {
443    SetError("The window cannot visit the desktop.");
444    return false;
445  }
446
447  return true;
448}
449
450}  // namespace extensions
451