private_api_misc.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 "base/files/file_path.h"
10#include "base/prefs/pref_service.h"
11#include "base/strings/utf_string_conversions.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/chromeos/drive/file_system_util.h"
14#include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
15#include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
16#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
17#include "chrome/browser/chromeos/file_manager/app_installer.h"
18#include "chrome/browser/chromeos/login/user_manager.h"
19#include "chrome/browser/chromeos/settings/cros_settings.h"
20#include "chrome/browser/drive/event_logger.h"
21#include "chrome/browser/lifetime/application_lifetime.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/profiles/profile_info_util.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.h"
27#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28#include "chrome/browser/signin/signin_manager.h"
29#include "chrome/browser/signin/signin_manager_factory.h"
30#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
31#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
32#include "chrome/common/extensions/api/file_browser_private.h"
33#include "chrome/common/pref_names.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() {
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    const int kImageSize = 30;
88    const gfx::Image& image = profiles::GetAvatarIconForTitleBar(
89        gfx::Image(user->image()), true, kImageSize, kImageSize);
90    const SkBitmap* const bitmap = image.ToSkBitmap();
91    if (bitmap) {
92      profile_info->image_uri.reset(new std::string(
93          webui::GetBitmapDataUrl(*bitmap)));
94    }
95    result_profiles.push_back(profile_info);
96  }
97
98  return result_profiles;
99}
100} // namespace
101
102bool FileBrowserPrivateLogoutUserForReauthenticationFunction::RunImpl() {
103  chromeos::User* user =
104      chromeos::UserManager::Get()->GetUserByProfile(GetProfile());
105  if (user) {
106    chromeos::UserManager::Get()->SaveUserOAuthStatus(
107        user->email(),
108        chromeos::User::OAUTH2_TOKEN_STATUS_INVALID);
109  }
110
111  chrome::AttemptUserExit();
112  return true;
113}
114
115bool FileBrowserPrivateGetPreferencesFunction::RunImpl() {
116  api::file_browser_private::Preferences result;
117  const PrefService* const service = GetProfile()->GetPrefs();
118
119  result.drive_enabled = drive::util::IsDriveEnabledForProfile(GetProfile());
120  result.cellular_disabled =
121      service->GetBoolean(prefs::kDisableDriveOverCellular);
122  result.hosted_files_disabled =
123      service->GetBoolean(prefs::kDisableDriveHostedFiles);
124  result.use24hour_clock = service->GetBoolean(prefs::kUse24HourClock);
125  result.allow_redeem_offers = true;
126  if (!chromeos::CrosSettings::Get()->GetBoolean(
127          chromeos::kAllowRedeemChromeOsRegistrationOffers,
128          &result.allow_redeem_offers)) {
129    result.allow_redeem_offers = true;
130  }
131
132  SetResult(result.ToValue().release());
133
134  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
135  if (logger)
136    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
137  return true;
138}
139
140bool FileBrowserPrivateSetPreferencesFunction::RunImpl() {
141  using extensions::api::file_browser_private::SetPreferences::Params;
142  const scoped_ptr<Params> params(Params::Create(*args_));
143  EXTENSION_FUNCTION_VALIDATE(params);
144
145  PrefService* const service = GetProfile()->GetPrefs();
146
147  if (params->change_info.cellular_disabled)
148    service->SetBoolean(prefs::kDisableDriveOverCellular,
149                        *params->change_info.cellular_disabled);
150
151  if (params->change_info.hosted_files_disabled)
152    service->SetBoolean(prefs::kDisableDriveHostedFiles,
153                        *params->change_info.hosted_files_disabled);
154
155  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
156  if (logger)
157    logger->Log(logging::LOG_INFO, "%s succeeded.", name().c_str());
158  return true;
159}
160
161FileBrowserPrivateZipSelectionFunction::
162    FileBrowserPrivateZipSelectionFunction() {}
163
164FileBrowserPrivateZipSelectionFunction::
165    ~FileBrowserPrivateZipSelectionFunction() {}
166
167bool FileBrowserPrivateZipSelectionFunction::RunImpl() {
168  using extensions::api::file_browser_private::ZipSelection::Params;
169  const scoped_ptr<Params> params(Params::Create(*args_));
170  EXTENSION_FUNCTION_VALIDATE(params);
171
172  // First param is the source directory URL.
173  if (params->dir_url.empty())
174    return false;
175
176  base::FilePath src_dir = file_manager::util::GetLocalPathFromURL(
177      render_view_host(), GetProfile(), GURL(params->dir_url));
178  if (src_dir.empty())
179    return false;
180
181  // Second param is the list of selected file URLs.
182  if (params->selection_urls.empty())
183    return false;
184
185  std::vector<base::FilePath> files;
186  for (size_t i = 0; i < params->selection_urls.size(); ++i) {
187    base::FilePath path = file_manager::util::GetLocalPathFromURL(
188        render_view_host(), GetProfile(), GURL(params->selection_urls[i]));
189    if (path.empty())
190      return false;
191    files.push_back(path);
192  }
193
194  // Third param is the name of the output zip file.
195  if (params->dest_name.empty())
196    return false;
197
198  // Check if the dir path is under Drive mount point.
199  // TODO(hshi): support create zip file on Drive (crbug.com/158690).
200  if (drive::util::IsUnderDriveMountPoint(src_dir))
201    return false;
202
203  base::FilePath dest_file = src_dir.Append(params->dest_name);
204  std::vector<base::FilePath> src_relative_paths;
205  for (size_t i = 0; i != files.size(); ++i) {
206    const base::FilePath& file_path = files[i];
207
208    // Obtain the relative path of |file_path| under |src_dir|.
209    base::FilePath relative_path;
210    if (!src_dir.AppendRelativePath(file_path, &relative_path))
211      return false;
212    src_relative_paths.push_back(relative_path);
213  }
214
215  zip_file_creator_ = new file_manager::ZipFileCreator(this,
216                                                       src_dir,
217                                                       src_relative_paths,
218                                                       dest_file);
219
220  // Keep the refcount until the zipping is complete on utility process.
221  AddRef();
222
223  zip_file_creator_->Start();
224  return true;
225}
226
227void FileBrowserPrivateZipSelectionFunction::OnZipDone(bool success) {
228  SetResult(new base::FundamentalValue(success));
229  SendResponse(true);
230  Release();
231}
232
233bool FileBrowserPrivateZoomFunction::RunImpl() {
234  using extensions::api::file_browser_private::Zoom::Params;
235  const scoped_ptr<Params> params(Params::Create(*args_));
236  EXTENSION_FUNCTION_VALIDATE(params);
237
238  content::PageZoom zoom_type;
239  switch (params->operation) {
240    case api::file_browser_private::ZOOM_OPERATION_TYPE_IN:
241      zoom_type = content::PAGE_ZOOM_IN;
242      break;
243    case api::file_browser_private::ZOOM_OPERATION_TYPE_OUT:
244      zoom_type = content::PAGE_ZOOM_OUT;
245      break;
246    case api::file_browser_private::ZOOM_OPERATION_TYPE_RESET:
247      zoom_type = content::PAGE_ZOOM_RESET;
248      break;
249    default:
250      NOTREACHED();
251      return false;
252  }
253  render_view_host()->Zoom(zoom_type);
254  return true;
255}
256
257bool FileBrowserPrivateInstallWebstoreItemFunction::RunImpl() {
258  using extensions::api::file_browser_private::InstallWebstoreItem::Params;
259  const scoped_ptr<Params> params(Params::Create(*args_));
260  EXTENSION_FUNCTION_VALIDATE(params);
261
262  if (params->item_id.empty())
263    return false;
264
265  const extensions::WebstoreStandaloneInstaller::Callback callback =
266      base::Bind(
267          &FileBrowserPrivateInstallWebstoreItemFunction::OnInstallComplete,
268          this);
269
270  scoped_refptr<file_manager::AppInstaller> installer(
271      new file_manager::AppInstaller(
272          GetAssociatedWebContents(),
273          params->item_id,
274          GetProfile(),
275          callback));
276  // installer will be AddRef()'d in BeginInstall().
277  installer->BeginInstall();
278  return true;
279}
280
281void FileBrowserPrivateInstallWebstoreItemFunction::OnInstallComplete(
282    bool success,
283    const std::string& error) {
284  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
285  if (success) {
286    if (logger) {
287      logger->Log(logging::LOG_INFO,
288                  "App install succeeded. (item id: %s)",
289                  webstore_item_id_.c_str());
290    }
291  } else {
292    if (logger) {
293      logger->Log(logging::LOG_ERROR,
294                  "App install failed. (item id: %s, reason: %s)",
295                  webstore_item_id_.c_str(),
296                  error.c_str());
297    }
298    SetError(error);
299  }
300
301  SendResponse(success);
302}
303
304FileBrowserPrivateRequestWebStoreAccessTokenFunction::
305    FileBrowserPrivateRequestWebStoreAccessTokenFunction() {
306}
307
308FileBrowserPrivateRequestWebStoreAccessTokenFunction::
309    ~FileBrowserPrivateRequestWebStoreAccessTokenFunction() {
310}
311
312bool FileBrowserPrivateRequestWebStoreAccessTokenFunction::RunImpl() {
313  std::vector<std::string> scopes;
314  scopes.push_back(kCWSScope);
315
316  ProfileOAuth2TokenService* oauth_service =
317      ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
318  net::URLRequestContextGetter* url_request_context_getter =
319      g_browser_process->system_request_context();
320
321  if (!oauth_service) {
322    drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
323    if (logger) {
324      logger->Log(logging::LOG_ERROR,
325                  "CWS OAuth token fetch failed. OAuth2TokenService can't "
326                  "be retrieved.");
327    }
328    SetResult(base::Value::CreateNullValue());
329    return false;
330  }
331
332  SigninManagerBase* signin_manager =
333      SigninManagerFactory::GetForProfile(GetProfile());
334  auth_service_.reset(new google_apis::AuthService(
335      oauth_service,
336      signin_manager->GetAuthenticatedAccountId(),
337      url_request_context_getter,
338      scopes));
339  auth_service_->StartAuthentication(base::Bind(
340      &FileBrowserPrivateRequestWebStoreAccessTokenFunction::
341          OnAccessTokenFetched,
342      this));
343
344  return true;
345}
346
347void FileBrowserPrivateRequestWebStoreAccessTokenFunction::OnAccessTokenFetched(
348    google_apis::GDataErrorCode code,
349    const std::string& access_token) {
350  drive::EventLogger* logger = file_manager::util::GetLogger(GetProfile());
351
352  if (code == google_apis::HTTP_SUCCESS) {
353    DCHECK(auth_service_->HasAccessToken());
354    DCHECK(access_token == auth_service_->access_token());
355    if (logger)
356      logger->Log(logging::LOG_INFO, "CWS OAuth token fetch succeeded.");
357    SetResult(new base::StringValue(access_token));
358    SendResponse(true);
359  } else {
360    if (logger) {
361      logger->Log(logging::LOG_ERROR,
362                  "CWS OAuth token fetch failed. (GDataErrorCode: %s)",
363                  google_apis::GDataErrorCodeToString(code).c_str());
364    }
365    SetResult(base::Value::CreateNullValue());
366    SendResponse(false);
367  }
368}
369
370bool FileBrowserPrivateGetProfilesFunction::RunImpl() {
371  const std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >&
372      profiles = GetLoggedInProfileInfoList();
373
374  // Obtains the display profile ID.
375  apps::AppWindow* const app_window = GetCurrentAppWindow(this);
376  chrome::MultiUserWindowManager* const window_manager =
377      chrome::MultiUserWindowManager::GetInstance();
378  const std::string current_profile_id =
379      multi_user_util::GetUserIDFromProfile(GetProfile());
380  const std::string display_profile_id =
381      window_manager && app_window ? window_manager->GetUserPresentingWindow(
382                                         app_window->GetNativeWindow())
383                                   : "";
384
385  results_ = api::file_browser_private::GetProfiles::Results::Create(
386      profiles,
387      current_profile_id,
388      display_profile_id.empty() ? current_profile_id : display_profile_id);
389  return true;
390}
391
392bool FileBrowserPrivateVisitDesktopFunction::RunImpl() {
393  using api::file_browser_private::VisitDesktop::Params;
394  const scoped_ptr<Params> params(Params::Create(*args_));
395  const std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >&
396      profiles = GetLoggedInProfileInfoList();
397
398  // Check the multi-profile support.
399  if (!profiles::IsMultipleProfilesEnabled()) {
400    SetError("Multi-profile support is not enabled.");
401    return false;
402  }
403
404  chrome::MultiUserWindowManager* const window_manager =
405      chrome::MultiUserWindowManager::GetInstance();
406  DCHECK(window_manager);
407
408  // Check if the target user is logged-in or not.
409  bool logged_in = false;
410  for (size_t i = 0; i < profiles.size(); ++i) {
411    if (profiles[i]->profile_id == params->profile_id) {
412      logged_in = true;
413      break;
414    }
415  }
416  if (!logged_in) {
417    SetError("The user is not logged-in now.");
418    return false;
419  }
420
421  // Look for the current app window.
422  apps::AppWindow* const app_window = GetCurrentAppWindow(this);
423  if (!app_window) {
424    SetError("Target window is not found.");
425    return false;
426  }
427
428  // Observe owner changes of windows.
429  file_manager::EventRouter* const event_router =
430      file_manager::FileBrowserPrivateAPI::Get(GetProfile())->event_router();
431  event_router->RegisterMultiUserWindowManagerObserver();
432
433  // Move the window to the user's desktop.
434  window_manager->ShowWindowForUser(app_window->GetNativeWindow(),
435                                    params->profile_id);
436
437  // Check the result.
438  if (!window_manager->IsWindowOnDesktopOfUser(app_window->GetNativeWindow(),
439                                               params->profile_id)) {
440    SetError("The window cannot visit the desktop.");
441    return false;
442  }
443
444  return true;
445}
446
447}  // namespace extensions
448