Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #import "chrome/browser/web_applications/web_app_mac.h" | 5 #import "chrome/browser/web_applications/web_app_mac.h" |
| 6 | 6 |
| 7 #import <Carbon/Carbon.h> | 7 #import <Carbon/Carbon.h> |
| 8 #import <Cocoa/Cocoa.h> | 8 #import <Cocoa/Cocoa.h> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 } | 159 } |
| 160 } | 160 } |
| 161 | 161 |
| 162 return false; | 162 return false; |
| 163 } | 163 } |
| 164 | 164 |
| 165 void LaunchShimOnFileThread( | 165 void LaunchShimOnFileThread( |
| 166 const ShellIntegration::ShortcutInfo& shortcut_info) { | 166 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 167 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 167 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 168 base::FilePath shim_path = web_app::GetAppInstallPath(shortcut_info); | 168 base::FilePath shim_path = web_app::GetAppInstallPath(shortcut_info); |
| 169 if (shim_path.empty()) | 169 |
| 170 if (shim_path.empty() || !file_util::PathExists(shim_path)) { | |
| 171 // The user may have deleted the copy in the Applications folder, use the | |
| 172 // one in the web app's app_data_path. | |
| 173 base::FilePath app_data_path = web_app::GetWebAppDataDirectory( | |
| 174 shortcut_info.profile_path, shortcut_info.extension_id, GURL()); | |
| 175 shim_path = app_data_path.Append(shim_path.BaseName()); | |
| 176 } | |
| 177 | |
| 178 if (!file_util::PathExists(shim_path)) | |
| 170 return; | 179 return; |
| 171 | 180 |
| 172 CommandLine command_line(CommandLine::NO_PROGRAM); | 181 CommandLine command_line(CommandLine::NO_PROGRAM); |
| 173 command_line.AppendSwitch(app_mode::kNoLaunchApp); | 182 command_line.AppendSwitch(app_mode::kNoLaunchApp); |
| 174 base::mac::OpenApplicationWithPath(shim_path, command_line, NULL); | 183 base::mac::OpenApplicationWithPath(shim_path, command_line, NULL); |
| 175 } | 184 } |
| 176 | 185 |
| 177 base::FilePath GetLocalizableAppShortcutsSubdirName() { | 186 base::FilePath GetLocalizableAppShortcutsSubdirName() { |
| 178 #if defined(GOOGLE_CHROME_BUILD) | 187 #if defined(GOOGLE_CHROME_BUILD) |
| 179 static const char kChromeAppDirName[] = "Chrome Apps.localized"; | 188 static const char kChromeAppDirName[] = "Chrome Apps.localized"; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 212 | 221 |
| 213 std::string locale = l10n_util::NormalizeLocale( | 222 std::string locale = l10n_util::NormalizeLocale( |
| 214 l10n_util::GetApplicationLocale(std::string())); | 223 l10n_util::GetApplicationLocale(std::string())); |
| 215 | 224 |
| 216 NSString* strings_path = base::mac::FilePathToNSString( | 225 NSString* strings_path = base::mac::FilePathToNSString( |
| 217 localized.Append(locale + ".strings")); | 226 localized.Append(locale + ".strings")); |
| 218 [strings_dict writeToFile:strings_path | 227 [strings_dict writeToFile:strings_path |
| 219 atomically:YES]; | 228 atomically:YES]; |
| 220 } | 229 } |
| 221 | 230 |
| 231 void DeletePathAndParentIfEmpty(const base::FilePath& app_path) { | |
| 232 DCHECK(!app_path.empty()); | |
| 233 file_util::Delete(app_path, true); | |
| 234 base::FilePath apps_folder = app_path.DirName(); | |
| 235 if (file_util::IsDirectoryEmpty(apps_folder)) | |
| 236 file_util::Delete(apps_folder, false); | |
| 237 } | |
| 238 | |
| 222 } // namespace | 239 } // namespace |
| 223 | 240 |
| 224 namespace web_app { | 241 namespace web_app { |
| 225 | 242 |
| 226 | 243 |
| 227 WebAppShortcutCreator::WebAppShortcutCreator( | 244 WebAppShortcutCreator::WebAppShortcutCreator( |
| 228 const base::FilePath& app_data_path, | 245 const base::FilePath& app_data_path, |
| 229 const ShellIntegration::ShortcutInfo& shortcut_info, | 246 const ShellIntegration::ShortcutInfo& shortcut_info, |
| 230 const string16& chrome_bundle_id) | 247 const std::string& chrome_bundle_id) |
| 231 : app_data_path_(app_data_path), | 248 : app_data_path_(app_data_path), |
| 232 info_(shortcut_info), | 249 info_(shortcut_info), |
| 233 chrome_bundle_id_(chrome_bundle_id) { | 250 chrome_bundle_id_(chrome_bundle_id) { |
| 234 } | 251 } |
| 235 | 252 |
| 236 WebAppShortcutCreator::~WebAppShortcutCreator() { | 253 WebAppShortcutCreator::~WebAppShortcutCreator() { |
| 237 } | 254 } |
| 238 | 255 |
| 239 base::FilePath WebAppShortcutCreator::GetShortcutPath() const { | 256 base::FilePath WebAppShortcutCreator::GetShortcutName() const { |
| 240 base::FilePath dst_path = GetDestinationPath(); | |
| 241 if (dst_path.empty()) | |
| 242 return dst_path; | |
| 243 | |
| 244 base::FilePath app_name = internals::GetSanitizedFileName(UTF8ToUTF16( | 257 base::FilePath app_name = internals::GetSanitizedFileName(UTF8ToUTF16( |
| 245 info_.profile_path.BaseName().value() + " " + info_.extension_id)); | 258 info_.profile_path.BaseName().value() + " " + info_.extension_id)); |
| 246 return dst_path.Append(app_name.ReplaceExtension("app")); | 259 return app_name.ReplaceExtension("app"); |
| 247 } | 260 } |
| 248 | 261 |
| 249 bool WebAppShortcutCreator::CreateShortcut() { | 262 bool WebAppShortcutCreator::BuildShortcut( |
| 250 base::FilePath app_path = GetShortcutPath(); | 263 const base::FilePath& staging_path) const { |
| 251 base::FilePath app_name = app_path.BaseName(); | |
| 252 base::FilePath dst_path = app_path.DirName(); | |
| 253 if (app_path.empty() || !file_util::DirectoryExists(dst_path.DirName())) { | |
| 254 LOG(ERROR) << "Couldn't find an Applications directory to copy app to."; | |
| 255 return false; | |
| 256 } | |
| 257 if (!file_util::CreateDirectory(app_data_path_)) { | |
| 258 LOG(ERROR) << "Creating app_data_path " << app_data_path_.value() | |
| 259 << " failed."; | |
| 260 return false; | |
| 261 } | |
| 262 if (!file_util::CreateDirectory(dst_path)) { | |
| 263 LOG(ERROR) << "Creating directory " << dst_path.value() << " failed."; | |
| 264 return false; | |
| 265 } | |
| 266 UpdateAppShortcutsSubdirLocalizedName(dst_path); | |
| 267 | |
| 268 base::ScopedTempDir scoped_temp_dir; | |
| 269 if (!scoped_temp_dir.CreateUniqueTempDir()) | |
| 270 return false; | |
| 271 base::FilePath staging_path = scoped_temp_dir.path().Append(app_name); | |
| 272 | |
| 273 // Update the app's plist and icon in a temp directory. This works around | 264 // Update the app's plist and icon in a temp directory. This works around |
| 274 // a Finder bug where the app's icon doesn't properly update. | 265 // a Finder bug where the app's icon doesn't properly update. |
| 275 if (!file_util::CopyDirectory(GetAppLoaderPath(), staging_path, true)) { | 266 if (!file_util::CopyDirectory(GetAppLoaderPath(), staging_path, true)) { |
| 276 LOG(ERROR) << "Copying app to staging path: " << staging_path.value() | 267 LOG(ERROR) << "Copying app to staging path: " << staging_path.value() |
| 277 << " failed."; | 268 << " failed."; |
| 278 return false; | 269 return false; |
| 279 } | 270 } |
| 280 | 271 |
| 281 if (!UpdatePlist(staging_path)) | 272 if (!UpdatePlist(staging_path)) |
| 282 return false; | 273 return false; |
| 283 | 274 |
| 284 if (!UpdateDisplayName(staging_path)) | 275 if (!UpdateDisplayName(staging_path)) |
| 285 return false; | 276 return false; |
| 286 | 277 |
| 287 if (!UpdateIcon(staging_path)) | 278 if (!UpdateIcon(staging_path)) |
| 288 return false; | 279 return false; |
| 289 | 280 |
| 290 // Put one copy in the app's app_data_path so we can still run it if the user | |
| 291 // deletes the one in the applications folder. | |
| 292 if (!file_util::CopyDirectory(staging_path, app_data_path_, true)) { | |
| 293 NOTREACHED(); | |
| 294 return false; | |
| 295 } | |
| 296 base::mac::RemoveQuarantineAttribute(app_data_path_.Append(app_name)); | |
| 297 | |
| 298 if (!file_util::CopyDirectory(staging_path, dst_path, true)) | |
| 299 return false; | |
| 300 | |
| 301 base::mac::RemoveQuarantineAttribute(app_path); | |
| 302 RevealGeneratedBundleInFinder(app_path); | |
| 303 | |
| 304 return true; | 281 return true; |
| 305 } | 282 } |
| 306 | 283 |
| 284 size_t WebAppShortcutCreator::CreateShortcutsIn( | |
| 285 const std::vector<base::FilePath>& folders) const { | |
| 286 size_t succeeded = 0; | |
| 287 | |
| 288 base::ScopedTempDir scoped_temp_dir; | |
| 289 if (!scoped_temp_dir.CreateUniqueTempDir()) | |
| 290 return false; | |
|
tapted
2013/06/19 08:29:40
false -> 0
jackhou1
2013/06/19 12:49:04
Done.
| |
| 291 | |
| 292 base::FilePath app_name = GetShortcutName(); | |
| 293 base::FilePath staging_path = | |
| 294 scoped_temp_dir.path().Append(app_name); | |
| 295 if (!BuildShortcut(staging_path)) | |
| 296 return false; | |
|
tapted
2013/06/19 08:29:40
false -> 0
jackhou1
2013/06/19 12:49:04
Done.
| |
| 297 | |
| 298 for (std::vector<base::FilePath>::const_iterator it = folders.begin(); | |
| 299 it != folders.end(); ++it) { | |
| 300 const base::FilePath& dst_path = *it; | |
| 301 if (!file_util::CopyDirectory(staging_path, dst_path, true)) { | |
| 302 LOG(ERROR) << "Copying app to dst path: " << dst_path.value() | |
| 303 << " failed"; | |
| 304 return false; | |
|
tapted
2013/06/19 08:29:40
this should be `return succeeded`
jackhou1
2013/06/19 12:49:04
Done.
| |
| 305 } | |
| 306 | |
| 307 base::mac::RemoveQuarantineAttribute(dst_path.Append(app_name)); | |
| 308 ++succeeded; | |
| 309 } | |
| 310 | |
| 311 return succeeded; | |
| 312 } | |
| 313 | |
| 314 bool WebAppShortcutCreator::CreateShortcuts() { | |
| 315 base::FilePath dst_path = GetDestinationPath(); | |
| 316 if (dst_path.empty() || !file_util::DirectoryExists(dst_path.DirName())) { | |
| 317 LOG(ERROR) << "Couldn't find an Applications directory to copy app to."; | |
| 318 return false; | |
| 319 } | |
| 320 | |
| 321 if (!file_util::CreateDirectory(app_data_path_)) { | |
| 322 LOG(ERROR) << "Creating app_data_path " << app_data_path_.value() | |
| 323 << " failed."; | |
| 324 return false; | |
| 325 } | |
| 326 | |
| 327 if (!file_util::CreateDirectory(dst_path)) { | |
| 328 LOG(ERROR) << "Creating directory " << dst_path.value() << " failed."; | |
| 329 return false; | |
| 330 } | |
| 331 | |
| 332 UpdateAppShortcutsSubdirLocalizedName(dst_path); | |
| 333 | |
| 334 std::vector<base::FilePath> paths; | |
| 335 paths.push_back(app_data_path_); | |
| 336 paths.push_back(dst_path); | |
| 337 size_t succeeded = CreateShortcutsIn(paths); | |
|
tapted
2013/06/19 08:29:40
nit: maybe num_succeeded or success_count, to make
jackhou1
2013/06/19 12:49:04
Done.
| |
| 338 if (!succeeded) | |
|
tapted
2013/06/19 08:29:40
nit: I think it's typically preferred to vs 0 for
jackhou1
2013/06/19 12:49:04
Done.
| |
| 339 return false; | |
| 340 | |
| 341 UpdateInternalBundleIdentifier(); | |
| 342 | |
| 343 if (succeeded != paths.size()) | |
| 344 return false; | |
| 345 | |
| 346 RevealAppShimInFinder(); | |
| 347 return true; | |
| 348 } | |
| 349 | |
| 350 void WebAppShortcutCreator::DeleteShortcuts() { | |
| 351 base::FilePath dst_path = GetDestinationPath(); | |
| 352 if (!dst_path.empty()) | |
| 353 DeletePathAndParentIfEmpty(dst_path.Append(GetShortcutName())); | |
| 354 | |
| 355 // In case the user has moved/renamed/copied the app bundle. | |
| 356 base::FilePath bundle_path = GetAppBundleById(GetBundleIdentifier()); | |
| 357 if (!bundle_path.empty()) | |
| 358 file_util::Delete(bundle_path, true); | |
| 359 | |
| 360 // Delete the internal one. | |
| 361 DeletePathAndParentIfEmpty(app_data_path_.Append(GetShortcutName())); | |
| 362 } | |
| 363 | |
| 364 bool WebAppShortcutCreator::UpdateShortcuts() { | |
| 365 base::FilePath dst_path = GetDestinationPath(); | |
| 366 base::FilePath app_path = dst_path.Append(GetShortcutName()); | |
| 367 | |
| 368 // If the path does not exist, check if a matching bundle can be found | |
| 369 // elsewhere. | |
| 370 if (dst_path.empty() || !file_util::PathExists(app_path)) | |
| 371 app_path = GetAppBundleById(GetBundleIdentifier()); | |
| 372 | |
| 373 std::vector<base::FilePath> paths; | |
| 374 paths.push_back(app_data_path_); | |
|
tapted
2013/06/19 08:29:40
Do we need to delete this folder, before updating
jackhou1
2013/06/19 12:49:04
I think whatever reasons for deleting the other on
| |
| 375 | |
| 376 if (!app_path.empty()) { | |
| 377 file_util::Delete(app_path, true); | |
| 378 paths.push_back(app_path.DirName()); | |
| 379 } | |
| 380 | |
| 381 size_t succeeded = CreateShortcutsIn(paths); | |
|
tapted
2013/06/19 08:29:40
nit: less booly
jackhou1
2013/06/19 12:49:04
Done.
| |
| 382 if (!succeeded) | |
|
tapted
2013/06/19 08:29:40
nit: less booly
jackhou1
2013/06/19 12:49:04
Done.
| |
| 383 return false; | |
| 384 | |
| 385 UpdateInternalBundleIdentifier(); | |
| 386 return succeeded == paths.size() && !app_path.empty(); | |
| 387 } | |
| 388 | |
| 307 base::FilePath WebAppShortcutCreator::GetAppLoaderPath() const { | 389 base::FilePath WebAppShortcutCreator::GetAppLoaderPath() const { |
| 308 return base::mac::PathForFrameworkBundleResource( | 390 return base::mac::PathForFrameworkBundleResource( |
| 309 base::mac::NSToCFCast(@"app_mode_loader.app")); | 391 base::mac::NSToCFCast(@"app_mode_loader.app")); |
| 310 } | 392 } |
| 311 | 393 |
| 312 base::FilePath WebAppShortcutCreator::GetDestinationPath() const { | 394 base::FilePath WebAppShortcutCreator::GetDestinationPath() const { |
| 313 base::FilePath path = GetWritableApplicationsDirectory(); | 395 base::FilePath path = GetWritableApplicationsDirectory(); |
| 314 if (path.empty()) | 396 if (path.empty()) |
| 315 return path; | 397 return path; |
| 316 return path.Append(GetLocalizableAppShortcutsSubdirName()); | 398 return path.Append(GetLocalizableAppShortcutsSubdirName()); |
| 317 } | 399 } |
| 318 | 400 |
| 319 bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const { | 401 bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const { |
| 320 NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); | 402 NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); |
| 321 NSString* extension_title = base::SysUTF16ToNSString(info_.title); | 403 NSString* extension_title = base::SysUTF16ToNSString(info_.title); |
| 322 NSString* extension_url = base::SysUTF8ToNSString(info_.url.spec()); | 404 NSString* extension_url = base::SysUTF8ToNSString(info_.url.spec()); |
| 323 NSString* chrome_bundle_id = base::SysUTF16ToNSString(chrome_bundle_id_); | 405 NSString* chrome_bundle_id = base::SysUTF8ToNSString(chrome_bundle_id_); |
| 324 NSDictionary* replacement_dict = | 406 NSDictionary* replacement_dict = |
| 325 [NSDictionary dictionaryWithObjectsAndKeys: | 407 [NSDictionary dictionaryWithObjectsAndKeys: |
| 326 extension_id, app_mode::kShortcutIdPlaceholder, | 408 extension_id, app_mode::kShortcutIdPlaceholder, |
| 327 extension_title, app_mode::kShortcutNamePlaceholder, | 409 extension_title, app_mode::kShortcutNamePlaceholder, |
| 328 extension_url, app_mode::kShortcutURLPlaceholder, | 410 extension_url, app_mode::kShortcutURLPlaceholder, |
| 329 chrome_bundle_id, app_mode::kShortcutBrowserBundleIDPlaceholder, | 411 chrome_bundle_id, app_mode::kShortcutBrowserBundleIDPlaceholder, |
| 330 nil]; | 412 nil]; |
| 331 | 413 |
| 332 NSString* plist_path = base::mac::FilePathToNSString( | 414 NSString* plist_path = base::mac::FilePathToNSString( |
| 333 app_path.Append("Contents").Append("Info.plist")); | 415 app_path.Append("Contents").Append("Info.plist")); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 344 // Remove leading and trailing '@'s. | 426 // Remove leading and trailing '@'s. |
| 345 NSString* variable = | 427 NSString* variable = |
| 346 [value substringWithRange:NSMakeRange(1, [value length] - 2)]; | 428 [value substringWithRange:NSMakeRange(1, [value length] - 2)]; |
| 347 | 429 |
| 348 NSString* substitution = [replacement_dict valueForKey:variable]; | 430 NSString* substitution = [replacement_dict valueForKey:variable]; |
| 349 if (substitution) | 431 if (substitution) |
| 350 [plist setObject:substitution forKey:key]; | 432 [plist setObject:substitution forKey:key]; |
| 351 } | 433 } |
| 352 | 434 |
| 353 // 2. Fill in other values. | 435 // 2. Fill in other values. |
| 354 [plist setObject:GetBundleIdentifier(plist) | 436 [plist setObject:base::SysUTF8ToNSString(GetBundleIdentifier()) |
| 355 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; | 437 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; |
| 356 [plist setObject:base::mac::FilePathToNSString(app_data_path_) | 438 [plist setObject:base::mac::FilePathToNSString(app_data_path_) |
| 357 forKey:app_mode::kCrAppModeUserDataDirKey]; | 439 forKey:app_mode::kCrAppModeUserDataDirKey]; |
| 358 [plist setObject:base::mac::FilePathToNSString(info_.profile_path.BaseName()) | 440 [plist setObject:base::mac::FilePathToNSString(info_.profile_path.BaseName()) |
| 359 forKey:app_mode::kCrAppModeProfileDirKey]; | 441 forKey:app_mode::kCrAppModeProfileDirKey]; |
| 360 [plist setObject:base::SysUTF8ToNSString(info_.profile_name) | 442 [plist setObject:base::SysUTF8ToNSString(info_.profile_name) |
| 361 forKey:app_mode::kCrAppModeProfileNameKey]; | 443 forKey:app_mode::kCrAppModeProfileNameKey]; |
| 362 [plist setObject:[NSNumber numberWithBool:YES] | 444 [plist setObject:[NSNumber numberWithBool:YES] |
| 363 forKey:app_mode::kLSHasLocalizedDisplayNameKey]; | 445 forKey:app_mode::kLSHasLocalizedDisplayNameKey]; |
| 364 | 446 |
| 365 base::FilePath app_name = app_path.BaseName().RemoveExtension(); | 447 base::FilePath app_name = app_path.BaseName().RemoveExtension(); |
| 366 [plist setObject:base::mac::FilePathToNSString(app_name) | 448 [plist setObject:base::mac::FilePathToNSString(app_name) |
| 367 forKey:base::mac::CFToNSCast(kCFBundleNameKey)]; | 449 forKey:base::mac::CFToNSCast(kCFBundleNameKey)]; |
| 368 | 450 |
| 369 return [plist writeToFile:plist_path atomically:YES]; | 451 return [plist writeToFile:plist_path |
| 452 atomically:YES]; | |
| 370 } | 453 } |
| 371 | 454 |
| 372 bool WebAppShortcutCreator::UpdateDisplayName( | 455 bool WebAppShortcutCreator::UpdateDisplayName( |
| 373 const base::FilePath& app_path) const { | 456 const base::FilePath& app_path) const { |
| 374 // OSX searches for the best language in the order of preferred languages. | 457 // OSX searches for the best language in the order of preferred languages. |
| 375 // Since we only have one localization directory, it will choose this one. | 458 // Since we only have one localization directory, it will choose this one. |
| 376 base::FilePath localized_dir = GetResourcesPath(app_path).Append("en.lproj"); | 459 base::FilePath localized_dir = GetResourcesPath(app_path).Append("en.lproj"); |
| 377 if (!file_util::CreateDirectory(localized_dir)) | 460 if (!file_util::CreateDirectory(localized_dir)) |
| 378 return false; | 461 return false; |
| 379 | 462 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 if (!image_added) | 503 if (!image_added) |
| 421 return false; | 504 return false; |
| 422 | 505 |
| 423 base::FilePath resources_path = GetResourcesPath(app_path); | 506 base::FilePath resources_path = GetResourcesPath(app_path); |
| 424 if (!file_util::CreateDirectory(resources_path)) | 507 if (!file_util::CreateDirectory(resources_path)) |
| 425 return false; | 508 return false; |
| 426 | 509 |
| 427 return icon_family.WriteDataToFile(resources_path.Append("app.icns")); | 510 return icon_family.WriteDataToFile(resources_path.Append("app.icns")); |
| 428 } | 511 } |
| 429 | 512 |
| 430 NSString* WebAppShortcutCreator::GetBundleIdentifier(NSDictionary* plist) const | 513 bool WebAppShortcutCreator::UpdateInternalBundleIdentifier() const { |
| 431 { | 514 NSString* plist_path = base::mac::FilePathToNSString( |
| 432 NSString* bundle_id_template = | 515 app_data_path_.Append(GetShortcutName()) |
| 433 base::mac::ObjCCast<NSString>( | 516 .Append("Contents").Append("Info.plist")); |
| 434 [plist objectForKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]); | 517 NSMutableDictionary* plist = |
| 435 NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); | 518 [NSMutableDictionary dictionaryWithContentsOfFile:plist_path]; |
| 436 NSString* placeholder = | 519 |
| 437 [NSString stringWithFormat:@"@%@@", app_mode::kShortcutIdPlaceholder]; | 520 [plist setObject:base::SysUTF8ToNSString(GetInternalBundleIdentifier()) |
| 438 NSString* bundle_id = | 521 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; |
| 439 [bundle_id_template | 522 return [plist writeToFile:plist_path |
| 440 stringByReplacingOccurrencesOfString:placeholder | 523 atomically:YES]; |
| 441 withString:extension_id]; | 524 } |
| 525 | |
| 526 base::FilePath WebAppShortcutCreator::GetAppBundleById( | |
| 527 const std::string& bundle_id) const { | |
| 528 base::mac::ScopedCFTypeRef<CFStringRef> bundle_id_cf( | |
| 529 base::SysUTF8ToCFStringRef(bundle_id)); | |
| 530 CFURLRef url_ref = NULL; | |
| 531 OSStatus status = LSFindApplicationForInfo( | |
| 532 kLSUnknownCreator, bundle_id_cf.get(), NULL, NULL, &url_ref); | |
| 533 if (status != noErr) | |
| 534 return base::FilePath(); | |
| 535 | |
| 536 base::mac::ScopedCFTypeRef<CFURLRef> url(url_ref); | |
| 537 NSString* path_string = [base::mac::CFToNSCast(url.get()) path]; | |
| 538 return base::FilePath([path_string fileSystemRepresentation]); | |
| 539 } | |
| 540 | |
| 541 std::string WebAppShortcutCreator::GetBundleIdentifier() const { | |
| 542 // Replace spaces in the profile path with hyphen. | |
| 543 std::string normalized_profile_path; | |
| 544 ReplaceChars(info_.profile_path.BaseName().value(), | |
| 545 " ", "-", &normalized_profile_path); | |
| 546 | |
| 547 // This matches APP_MODE_APP_BUNDLE_ID in chrome/chrome.gyp. | |
| 548 std::string bundle_id = | |
| 549 chrome_bundle_id_ + std::string(".app.") + | |
| 550 normalized_profile_path + "-" + info_.extension_id; | |
| 551 | |
| 442 return bundle_id; | 552 return bundle_id; |
| 443 } | 553 } |
| 444 | 554 |
| 445 void WebAppShortcutCreator::RevealGeneratedBundleInFinder( | 555 std::string WebAppShortcutCreator::GetInternalBundleIdentifier() const { |
| 446 const base::FilePath& generated_bundle) const { | 556 return GetBundleIdentifier() + "-internal"; |
| 557 } | |
| 558 | |
| 559 void WebAppShortcutCreator::RevealAppShimInFinder() const { | |
| 560 base::FilePath dst_path = GetDestinationPath(); | |
| 561 if (dst_path.empty()) | |
| 562 return; | |
| 563 | |
| 564 base::FilePath app_path = dst_path.Append(GetShortcutName()); | |
| 447 [[NSWorkspace sharedWorkspace] | 565 [[NSWorkspace sharedWorkspace] |
| 448 selectFile:base::mac::FilePathToNSString(generated_bundle) | 566 selectFile:base::mac::FilePathToNSString(app_path) |
| 449 inFileViewerRootedAtPath:nil]; | 567 inFileViewerRootedAtPath:nil]; |
| 450 } | 568 } |
| 451 | 569 |
| 452 base::FilePath GetAppInstallPath( | 570 base::FilePath GetAppInstallPath( |
| 453 const ShellIntegration::ShortcutInfo& shortcut_info) { | 571 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 454 WebAppShortcutCreator shortcut_creator(base::FilePath(), | 572 WebAppShortcutCreator shortcut_creator(base::FilePath(), |
| 455 shortcut_info, | 573 shortcut_info, |
| 456 string16()); | 574 std::string()); |
| 457 return shortcut_creator.GetShortcutPath(); | 575 base::FilePath dst_path = shortcut_creator.GetDestinationPath(); |
| 576 return dst_path.empty() ? | |
| 577 base::FilePath() : dst_path.Append(shortcut_creator.GetShortcutName()); | |
| 458 } | 578 } |
| 459 | 579 |
| 460 void MaybeLaunchShortcut(const ShellIntegration::ShortcutInfo& shortcut_info) { | 580 void MaybeLaunchShortcut(const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 461 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims)) | 581 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims)) |
| 462 return; | 582 return; |
| 463 | 583 |
| 464 content::BrowserThread::PostTask( | 584 content::BrowserThread::PostTask( |
| 465 content::BrowserThread::FILE, FROM_HERE, | 585 content::BrowserThread::FILE, FROM_HERE, |
| 466 base::Bind(&LaunchShimOnFileThread, shortcut_info)); | 586 base::Bind(&LaunchShimOnFileThread, shortcut_info)); |
| 467 } | 587 } |
| 468 | 588 |
| 469 namespace internals { | 589 namespace internals { |
| 470 | 590 |
| 471 base::FilePath GetAppBundleByExtensionId(std::string extension_id) { | |
| 472 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
| 473 // This matches APP_MODE_APP_BUNDLE_ID in chrome/chrome.gyp. | |
| 474 std::string bundle_id = | |
| 475 base::mac::BaseBundleID() + std::string(".app.") + extension_id; | |
| 476 base::mac::ScopedCFTypeRef<CFStringRef> bundle_id_cf( | |
| 477 base::SysUTF8ToCFStringRef(bundle_id)); | |
| 478 CFURLRef url_ref = NULL; | |
| 479 OSStatus status = LSFindApplicationForInfo( | |
| 480 kLSUnknownCreator, bundle_id_cf.get(), NULL, NULL, &url_ref); | |
| 481 base::mac::ScopedCFTypeRef<CFURLRef> url(url_ref); | |
| 482 | |
| 483 if (status != noErr) | |
| 484 return base::FilePath(); | |
| 485 | |
| 486 NSString* path_string = [base::mac::CFToNSCast(url.get()) path]; | |
| 487 return base::FilePath([path_string fileSystemRepresentation]); | |
| 488 } | |
| 489 | |
| 490 bool CreatePlatformShortcuts( | 591 bool CreatePlatformShortcuts( |
| 491 const base::FilePath& app_data_path, | 592 const base::FilePath& app_data_path, |
| 492 const ShellIntegration::ShortcutInfo& shortcut_info, | 593 const ShellIntegration::ShortcutInfo& shortcut_info, |
| 493 const ShellIntegration::ShortcutLocations& creation_locations) { | 594 const ShellIntegration::ShortcutLocations& creation_locations) { |
| 494 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 595 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 495 string16 bundle_id = UTF8ToUTF16(base::mac::BaseBundleID()); | 596 WebAppShortcutCreator shortcut_creator( |
| 496 WebAppShortcutCreator shortcut_creator(app_data_path, shortcut_info, | 597 app_data_path, shortcut_info, base::mac::BaseBundleID()); |
| 497 bundle_id); | 598 return shortcut_creator.CreateShortcuts(); |
| 498 return shortcut_creator.CreateShortcut(); | |
| 499 } | 599 } |
| 500 | 600 |
| 501 void DeletePlatformShortcuts( | 601 void DeletePlatformShortcuts( |
| 502 const base::FilePath& app_data_path, | 602 const base::FilePath& app_data_path, |
| 503 const ShellIntegration::ShortcutInfo& info) { | 603 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 504 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 604 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 505 | 605 WebAppShortcutCreator shortcut_creator( |
| 506 base::FilePath bundle_path = GetAppBundleByExtensionId(info.extension_id); | 606 app_data_path, shortcut_info, base::mac::BaseBundleID()); |
| 507 file_util::Delete(bundle_path, true); | 607 shortcut_creator.DeleteShortcuts(); |
| 508 } | 608 } |
| 509 | 609 |
| 510 void UpdatePlatformShortcuts( | 610 void UpdatePlatformShortcuts( |
| 511 const base::FilePath& app_data_path, | 611 const base::FilePath& app_data_path, |
| 512 const string16& old_app_title, | 612 const string16& old_app_title, |
| 513 const ShellIntegration::ShortcutInfo& shortcut_info) { | 613 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 514 // TODO(benwells): Implement this when shortcuts / weblings are enabled on | 614 WebAppShortcutCreator shortcut_creator( |
| 515 // mac. | 615 app_data_path, shortcut_info, base::mac::BaseBundleID()); |
| 616 shortcut_creator.UpdateShortcuts(); | |
| 516 } | 617 } |
| 517 | 618 |
| 518 } // namespace internals | 619 } // namespace internals |
| 519 | 620 |
| 520 } // namespace web_app | 621 } // namespace web_app |
| OLD | NEW |