| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "apps/launcher.h" | 5 #include "apps/launcher.h" |
| 6 | 6 |
| 7 #include "apps/browser/api/app_runtime/app_runtime_api.h" | 7 #include "apps/browser/api/app_runtime/app_runtime_api.h" |
| 8 #include "apps/browser/file_handler_util.h" | 8 #include "apps/browser/file_handler_util.h" |
| 9 #include "apps/common/api/app_runtime.h" | 9 #include "apps/common/api/app_runtime.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 56 using extensions::Extension; | 56 using extensions::Extension; |
| 57 using extensions::ExtensionHost; | 57 using extensions::ExtensionHost; |
| 58 using extensions::ExtensionSystem; | 58 using extensions::ExtensionSystem; |
| 59 | 59 |
| 60 namespace apps { | 60 namespace apps { |
| 61 | 61 |
| 62 namespace { | 62 namespace { |
| 63 | 63 |
| 64 const char kFallbackMimeType[] = "application/octet-stream"; | 64 const char kFallbackMimeType[] = "application/octet-stream"; |
| 65 | 65 |
| 66 bool MakePathAbsolute(const base::FilePath& current_directory, | 66 bool DoMakePathAbsolute(const base::FilePath& current_directory, |
| 67 base::FilePath* file_path) { | 67 base::FilePath* file_path) { |
| 68 DCHECK(file_path); | 68 DCHECK(file_path); |
| 69 if (file_path->IsAbsolute()) | 69 if (file_path->IsAbsolute()) |
| 70 return true; | 70 return true; |
| 71 | 71 |
| 72 if (current_directory.empty()) { | 72 if (current_directory.empty()) { |
| 73 *file_path = base::MakeAbsoluteFilePath(*file_path); | 73 *file_path = base::MakeAbsoluteFilePath(*file_path); |
| 74 return !file_path->empty(); | 74 return !file_path->empty(); |
| 75 } | 75 } |
| 76 | 76 |
| 77 if (!current_directory.IsAbsolute()) | 77 if (!current_directory.IsAbsolute()) |
| 78 return false; | 78 return false; |
| 79 | 79 |
| 80 *file_path = current_directory.Append(*file_path); | 80 *file_path = current_directory.Append(*file_path); |
| 81 return true; | 81 return true; |
| 82 } | 82 } |
| 83 | 83 |
| 84 bool GetAbsolutePathFromCommandLine(const CommandLine& command_line, | |
| 85 const base::FilePath& current_directory, | |
| 86 base::FilePath* path) { | |
| 87 if (!command_line.GetArgs().size()) | |
| 88 return false; | |
| 89 | |
| 90 base::FilePath relative_path(command_line.GetArgs()[0]); | |
| 91 base::FilePath absolute_path(relative_path); | |
| 92 if (!MakePathAbsolute(current_directory, &absolute_path)) { | |
| 93 LOG(WARNING) << "Cannot make absolute path from " << relative_path.value(); | |
| 94 return false; | |
| 95 } | |
| 96 *path = absolute_path; | |
| 97 return true; | |
| 98 } | |
| 99 | |
| 100 // Helper method to launch the platform app |extension| with no data. This | 84 // Helper method to launch the platform app |extension| with no data. This |
| 101 // should be called in the fallback case, where it has been impossible to | 85 // should be called in the fallback case, where it has been impossible to |
| 102 // load or obtain file launch data. | 86 // load or obtain file launch data. |
| 103 void LaunchPlatformAppWithNoData(Profile* profile, const Extension* extension) { | 87 void LaunchPlatformAppWithNoData(Profile* profile, const Extension* extension) { |
| 104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 88 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 105 AppEventRouter::DispatchOnLaunchedEvent(profile, extension); | 89 AppEventRouter::DispatchOnLaunchedEvent(profile, extension); |
| 106 } | 90 } |
| 107 | 91 |
| 108 // Class to handle launching of platform apps to open a specific path. | 92 // Class to handle launching of platform apps to open a specific path. |
| 109 // An instance of this class is created for each launch. The lifetime of these | 93 // An instance of this class is created for each launch. The lifetime of these |
| 110 // instances is managed by reference counted pointers. As long as an instance | 94 // instances is managed by reference counted pointers. As long as an instance |
| 111 // has outstanding tasks on a message queue it will be retained; once all | 95 // has outstanding tasks on a message queue it will be retained; once all |
| 112 // outstanding tasks are completed it will be deleted. | 96 // outstanding tasks are completed it will be deleted. |
| 113 class PlatformAppPathLauncher | 97 class PlatformAppPathLauncher |
| 114 : public base::RefCountedThreadSafe<PlatformAppPathLauncher> { | 98 : public base::RefCountedThreadSafe<PlatformAppPathLauncher> { |
| 115 public: | 99 public: |
| 116 PlatformAppPathLauncher(Profile* profile, | 100 PlatformAppPathLauncher(Profile* profile, |
| 117 const Extension* extension, | 101 const Extension* extension, |
| 118 const base::FilePath& file_path) | 102 const base::FilePath& file_path) |
| 119 : profile_(profile), extension_(extension), file_path_(file_path) {} | 103 : profile_(profile), extension_(extension), file_path_(file_path) {} |
| 120 | 104 |
| 121 void Launch() { | 105 void Launch() { |
| 122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 106 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 123 if (file_path_.empty()) { | 107 if (file_path_.empty()) { |
| 124 LaunchPlatformAppWithNoData(profile_, extension_); | 108 LaunchPlatformAppWithNoData(profile_, extension_); |
| 125 return; | 109 return; |
| 126 } | 110 } |
| 127 | 111 |
| 128 DCHECK(file_path_.IsAbsolute()); | 112 DCHECK(file_path_.IsAbsolute()); |
| 129 | 113 |
| 130 if (HasFileSystemWritePermission(extension_)) { | 114 if (HasFileSystemWritePermission(extension_)) { |
| 131 std::vector<base::FilePath> paths; | 115 std::vector<base::FilePath> paths; |
| 132 paths.push_back(file_path_); | 116 paths.push_back(file_path_); |
| 133 CheckWritableFiles( | 117 CheckWritableFiles( |
| 134 paths, | 118 paths, |
| 135 profile_, | 119 profile_, |
| 136 false, | 120 false, |
| 137 base::Bind(&PlatformAppPathLauncher::OnFileValid, this), | 121 base::Bind(&PlatformAppPathLauncher::OnFileValid, this), |
| 138 base::Bind(&PlatformAppPathLauncher::OnFileInvalid, this)); | 122 base::Bind(&PlatformAppPathLauncher::OnFileInvalid, this)); |
| 139 return; | 123 return; |
| 140 } | 124 } |
| 141 | 125 |
| 142 OnFileValid(); | 126 OnFileValid(); |
| 143 } | 127 } |
| 144 | 128 |
| 145 void LaunchWithHandler(const std::string& handler_id) { | 129 void LaunchWithHandler(const std::string& handler_id) { |
| 146 handler_id_ = handler_id; | 130 handler_id_ = handler_id; |
| 147 Launch(); | 131 Launch(); |
| 148 } | 132 } |
| 149 | 133 |
| 134 void LaunchWithRelativePath(const base::FilePath& current_directory) { |
| 135 BrowserThread::PostTask( |
| 136 BrowserThread::FILE, |
| 137 FROM_HERE, |
| 138 base::Bind(&PlatformAppPathLauncher::MakePathAbsolute, |
| 139 this, |
| 140 current_directory)); |
| 141 } |
| 142 |
| 150 private: | 143 private: |
| 151 friend class base::RefCountedThreadSafe<PlatformAppPathLauncher>; | 144 friend class base::RefCountedThreadSafe<PlatformAppPathLauncher>; |
| 152 | 145 |
| 153 virtual ~PlatformAppPathLauncher() {} | 146 virtual ~PlatformAppPathLauncher() {} |
| 154 | 147 |
| 148 void MakePathAbsolute(const base::FilePath& current_directory) { |
| 149 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 150 |
| 151 if (!DoMakePathAbsolute(current_directory, &file_path_)) { |
| 152 LOG(WARNING) << "Cannot make absolute path from " << file_path_.value(); |
| 153 file_path_ = base::FilePath(); |
| 154 } |
| 155 |
| 156 BrowserThread::PostTask(BrowserThread::UI, |
| 157 FROM_HERE, |
| 158 base::Bind(&PlatformAppPathLauncher::Launch, this)); |
| 159 } |
| 160 |
| 155 void OnFileValid() { | 161 void OnFileValid() { |
| 156 #if defined(OS_CHROMEOS) | 162 #if defined(OS_CHROMEOS) |
| 157 if (drive::util::IsUnderDriveMountPoint(file_path_)) { | 163 if (drive::util::IsUnderDriveMountPoint(file_path_)) { |
| 158 PlatformAppPathLauncher::GetMimeTypeAndLaunchForDriveFile(); | 164 PlatformAppPathLauncher::GetMimeTypeAndLaunchForDriveFile(); |
| 159 return; | 165 return; |
| 160 } | 166 } |
| 161 #endif | 167 #endif |
| 162 | 168 |
| 163 BrowserThread::PostTask( | 169 BrowserThread::PostTask( |
| 164 BrowserThread::FILE, | 170 BrowserThread::FILE, |
| 165 FROM_HERE, | 171 FROM_HERE, |
| 166 base::Bind(&PlatformAppPathLauncher::GetMimeTypeAndLaunch, this)); | 172 base::Bind(&PlatformAppPathLauncher::GetMimeTypeAndLaunch, this)); |
| 167 } | 173 } |
| 168 | 174 |
| 169 void OnFileInvalid(const base::FilePath& /* error_path */) { | 175 void OnFileInvalid(const base::FilePath& /* error_path */) { |
| 170 LaunchWithNoLaunchData(); | 176 LaunchWithNoLaunchData(); |
| 171 } | 177 } |
| 172 | 178 |
| 173 void GetMimeTypeAndLaunch() { | 179 void GetMimeTypeAndLaunch() { |
| 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 180 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 175 | 181 |
| 176 // If the file doesn't exist, or is a directory, launch with no launch data. | 182 // If the file doesn't exist, or is a directory, launch with no launch data. |
| 177 if (!base::PathExists(file_path_) || | 183 if (!base::PathExists(file_path_) || |
| 178 base::DirectoryExists(file_path_)) { | 184 base::DirectoryExists(file_path_)) { |
| 179 LOG(WARNING) << "No file exists with path " << file_path_.value(); | 185 LOG(WARNING) << "No file exists with path " << file_path_.value(); |
| 180 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 186 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 181 &PlatformAppPathLauncher::LaunchWithNoLaunchData, this)); | 187 &PlatformAppPathLauncher::LaunchWithNoLaunchData, this)); |
| 182 return; | 188 return; |
| 183 } | 189 } |
| 184 | 190 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 198 if (mime_type.empty()) | 204 if (mime_type.empty()) |
| 199 mime_type = kFallbackMimeType; | 205 mime_type = kFallbackMimeType; |
| 200 } | 206 } |
| 201 | 207 |
| 202 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 208 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 203 &PlatformAppPathLauncher::LaunchWithMimeType, this, mime_type)); | 209 &PlatformAppPathLauncher::LaunchWithMimeType, this, mime_type)); |
| 204 } | 210 } |
| 205 | 211 |
| 206 #if defined(OS_CHROMEOS) | 212 #if defined(OS_CHROMEOS) |
| 207 void GetMimeTypeAndLaunchForDriveFile() { | 213 void GetMimeTypeAndLaunchForDriveFile() { |
| 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 214 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 209 | 215 |
| 210 drive::FileSystemInterface* file_system = | 216 drive::FileSystemInterface* file_system = |
| 211 drive::util::GetFileSystemByProfile(profile_); | 217 drive::util::GetFileSystemByProfile(profile_); |
| 212 if (!file_system) { | 218 if (!file_system) { |
| 213 LaunchWithNoLaunchData(); | 219 LaunchWithNoLaunchData(); |
| 214 return; | 220 return; |
| 215 } | 221 } |
| 216 | 222 |
| 217 file_system->GetFile( | 223 file_system->GetFile( |
| 218 drive::util::ExtractDrivePath(file_path_), | 224 drive::util::ExtractDrivePath(file_path_), |
| 219 base::Bind(&PlatformAppPathLauncher::OnGotDriveFile, this)); | 225 base::Bind(&PlatformAppPathLauncher::OnGotDriveFile, this)); |
| 220 } | 226 } |
| 221 | 227 |
| 222 void OnGotDriveFile(drive::FileError error, | 228 void OnGotDriveFile(drive::FileError error, |
| 223 const base::FilePath& file_path, | 229 const base::FilePath& file_path, |
| 224 scoped_ptr<drive::ResourceEntry> entry) { | 230 scoped_ptr<drive::ResourceEntry> entry) { |
| 225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 231 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 226 | 232 |
| 227 if (error != drive::FILE_ERROR_OK || | 233 if (error != drive::FILE_ERROR_OK || |
| 228 !entry || entry->file_specific_info().is_hosted_document()) { | 234 !entry || entry->file_specific_info().is_hosted_document()) { |
| 229 LaunchWithNoLaunchData(); | 235 LaunchWithNoLaunchData(); |
| 230 return; | 236 return; |
| 231 } | 237 } |
| 232 | 238 |
| 233 const std::string& mime_type = | 239 const std::string& mime_type = |
| 234 entry->file_specific_info().content_mime_type(); | 240 entry->file_specific_info().content_mime_type(); |
| 235 LaunchWithMimeType(mime_type.empty() ? kFallbackMimeType : mime_type); | 241 LaunchWithMimeType(mime_type.empty() ? kFallbackMimeType : mime_type); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 303 host->render_process_host()->GetID(), | 309 host->render_process_host()->GetID(), |
| 304 file_path_, | 310 file_path_, |
| 305 false); | 311 false); |
| 306 AppEventRouter::DispatchOnLaunchedEventWithFileEntry( | 312 AppEventRouter::DispatchOnLaunchedEventWithFileEntry( |
| 307 profile_, extension_, handler_id_, mime_type, file_entry); | 313 profile_, extension_, handler_id_, mime_type, file_entry); |
| 308 } | 314 } |
| 309 | 315 |
| 310 // The profile the app should be run in. | 316 // The profile the app should be run in. |
| 311 Profile* profile_; | 317 Profile* profile_; |
| 312 // The extension providing the app. | 318 // The extension providing the app. |
| 319 // TODO(benwells): Hold onto the extension ID instead of a pointer as it |
| 320 // is possible the extension will be unloaded while we're doing our thing. |
| 321 // See http://crbug.com/372270 for details. |
| 313 const Extension* extension_; | 322 const Extension* extension_; |
| 314 // The path to be passed through to the app. | 323 // The path to be passed through to the app. |
| 315 const base::FilePath file_path_; | 324 base::FilePath file_path_; |
| 316 // The ID of the file handler used to launch the app. | 325 // The ID of the file handler used to launch the app. |
| 317 std::string handler_id_; | 326 std::string handler_id_; |
| 318 | 327 |
| 319 DISALLOW_COPY_AND_ASSIGN(PlatformAppPathLauncher); | 328 DISALLOW_COPY_AND_ASSIGN(PlatformAppPathLauncher); |
| 320 }; | 329 }; |
| 321 | 330 |
| 322 } // namespace | 331 } // namespace |
| 323 | 332 |
| 324 void LaunchPlatformAppWithCommandLine(Profile* profile, | 333 void LaunchPlatformAppWithCommandLine(Profile* profile, |
| 325 const Extension* extension, | 334 const Extension* extension, |
| 326 const CommandLine& command_line, | 335 const CommandLine& command_line, |
| 327 const base::FilePath& current_directory) { | 336 const base::FilePath& current_directory) { |
| 328 // An app with "kiosk_only" should not be installed and launched | 337 // An app with "kiosk_only" should not be installed and launched |
| 329 // outside of ChromeOS kiosk mode in the first place. This is a defensive | 338 // outside of ChromeOS kiosk mode in the first place. This is a defensive |
| 330 // check in case this scenario does occur. | 339 // check in case this scenario does occur. |
| 331 if (extensions::KioskModeInfo::IsKioskOnly(extension)) { | 340 if (extensions::KioskModeInfo::IsKioskOnly(extension)) { |
| 332 bool in_kiosk_mode = false; | 341 bool in_kiosk_mode = false; |
| 333 #if defined(OS_CHROMEOS) | 342 #if defined(OS_CHROMEOS) |
| 334 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); | 343 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); |
| 335 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp(); | 344 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp(); |
| 336 #endif | 345 #endif |
| 337 if (!in_kiosk_mode) { | 346 if (!in_kiosk_mode) { |
| 338 LOG(ERROR) << "App with 'kiosk_only' attribute must be run in " | 347 LOG(ERROR) << "App with 'kiosk_only' attribute must be run in " |
| 339 << " ChromeOS kiosk mode."; | 348 << " ChromeOS kiosk mode."; |
| 340 NOTREACHED(); | 349 NOTREACHED(); |
| 341 return; | 350 return; |
| 342 } | 351 } |
| 343 } | 352 } |
| 344 | 353 |
| 345 base::FilePath path; | 354 if (command_line.GetArgs().empty()) { |
| 346 if (!GetAbsolutePathFromCommandLine(command_line, current_directory, &path)) { | |
| 347 LaunchPlatformAppWithNoData(profile, extension); | 355 LaunchPlatformAppWithNoData(profile, extension); |
| 348 return; | 356 return; |
| 349 } | 357 } |
| 350 | 358 |
| 351 // TODO(benwells): add a command-line argument to provide a handler ID. | 359 base::FilePath file_path(command_line.GetArgs()[0]); |
| 352 LaunchPlatformAppWithPath(profile, extension, path); | 360 scoped_refptr<PlatformAppPathLauncher> launcher = |
| 361 new PlatformAppPathLauncher(profile, extension, file_path); |
| 362 launcher->LaunchWithRelativePath(current_directory); |
| 353 } | 363 } |
| 354 | 364 |
| 355 void LaunchPlatformAppWithPath(Profile* profile, | 365 void LaunchPlatformAppWithPath(Profile* profile, |
| 356 const Extension* extension, | 366 const Extension* extension, |
| 357 const base::FilePath& file_path) { | 367 const base::FilePath& file_path) { |
| 358 // launcher will be freed when nothing has a reference to it. The message | |
| 359 // queue will retain a reference for any outstanding task, so when the | |
| 360 // launcher has finished it will be freed. | |
| 361 scoped_refptr<PlatformAppPathLauncher> launcher = | 368 scoped_refptr<PlatformAppPathLauncher> launcher = |
| 362 new PlatformAppPathLauncher(profile, extension, file_path); | 369 new PlatformAppPathLauncher(profile, extension, file_path); |
| 363 launcher->Launch(); | 370 launcher->Launch(); |
| 364 } | 371 } |
| 365 | 372 |
| 366 void LaunchPlatformApp(Profile* profile, const Extension* extension) { | 373 void LaunchPlatformApp(Profile* profile, const Extension* extension) { |
| 367 LaunchPlatformAppWithCommandLine(profile, | 374 LaunchPlatformAppWithCommandLine(profile, |
| 368 extension, | 375 extension, |
| 369 CommandLine(CommandLine::NO_PROGRAM), | 376 CommandLine(CommandLine::NO_PROGRAM), |
| 370 base::FilePath()); | 377 base::FilePath()); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 405 void LaunchPlatformAppWithUrl(Profile* profile, | 412 void LaunchPlatformAppWithUrl(Profile* profile, |
| 406 const Extension* extension, | 413 const Extension* extension, |
| 407 const std::string& handler_id, | 414 const std::string& handler_id, |
| 408 const GURL& url, | 415 const GURL& url, |
| 409 const GURL& referrer_url) { | 416 const GURL& referrer_url) { |
| 410 AppEventRouter::DispatchOnLaunchedEventWithUrl( | 417 AppEventRouter::DispatchOnLaunchedEventWithUrl( |
| 411 profile, extension, handler_id, url, referrer_url); | 418 profile, extension, handler_id, url, referrer_url); |
| 412 } | 419 } |
| 413 | 420 |
| 414 } // namespace apps | 421 } // namespace apps |
| OLD | NEW |