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