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 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 212 | 212 |
| 213 std::string locale = l10n_util::NormalizeLocale( | 213 std::string locale = l10n_util::NormalizeLocale( |
| 214 l10n_util::GetApplicationLocale(std::string())); | 214 l10n_util::GetApplicationLocale(std::string())); |
| 215 | 215 |
| 216 NSString* strings_path = base::mac::FilePathToNSString( | 216 NSString* strings_path = base::mac::FilePathToNSString( |
| 217 localized.Append(locale + ".strings")); | 217 localized.Append(locale + ".strings")); |
| 218 [strings_dict writeToFile:strings_path | 218 [strings_dict writeToFile:strings_path |
| 219 atomically:YES]; | 219 atomically:YES]; |
| 220 } | 220 } |
| 221 | 221 |
| 222 void DeletePathAndParentIfEmpty(const base::FilePath& app_path) { | |
| 223 if (app_path.empty()) | |
| 224 return; | |
| 225 | |
| 226 file_util::Delete(app_path, true); | |
| 227 base::FilePath apps_folder = app_path.DirName(); | |
| 228 if (file_util::IsDirectoryEmpty(apps_folder)) | |
| 229 file_util::Delete(apps_folder, false); | |
| 230 } | |
| 231 | |
| 222 } // namespace | 232 } // namespace |
| 223 | 233 |
| 224 namespace web_app { | 234 namespace web_app { |
| 225 | 235 |
| 226 | 236 |
| 227 WebAppShortcutCreator::WebAppShortcutCreator( | 237 WebAppShortcutCreator::WebAppShortcutCreator( |
| 228 const base::FilePath& app_data_path, | 238 const base::FilePath& app_data_path, |
| 229 const ShellIntegration::ShortcutInfo& shortcut_info, | 239 const ShellIntegration::ShortcutInfo& shortcut_info, |
| 230 const string16& chrome_bundle_id) | 240 const std::string& chrome_bundle_id) |
| 231 : app_data_path_(app_data_path), | 241 : app_data_path_(app_data_path), |
| 232 info_(shortcut_info), | 242 info_(shortcut_info), |
| 233 chrome_bundle_id_(chrome_bundle_id) { | 243 chrome_bundle_id_(chrome_bundle_id) { |
| 234 } | 244 } |
| 235 | 245 |
| 236 WebAppShortcutCreator::~WebAppShortcutCreator() { | 246 WebAppShortcutCreator::~WebAppShortcutCreator() { |
| 237 } | 247 } |
| 238 | 248 |
| 239 base::FilePath WebAppShortcutCreator::GetShortcutPath() const { | 249 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( | 250 base::FilePath app_name = internals::GetSanitizedFileName(UTF8ToUTF16( |
| 245 info_.profile_path.BaseName().value() + " " + info_.extension_id)); | 251 info_.profile_path.BaseName().value() + " " + info_.extension_id)); |
| 246 return dst_path.Append(app_name.ReplaceExtension("app")); | 252 return app_name.ReplaceExtension("app"); |
| 247 } | 253 } |
| 248 | 254 |
| 249 bool WebAppShortcutCreator::CreateShortcut() { | 255 bool WebAppShortcutCreator::BuildShortcut( |
| 250 base::FilePath app_path = GetShortcutPath(); | 256 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 | 257 // 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. | 258 // a Finder bug where the app's icon doesn't properly update. |
| 275 if (!file_util::CopyDirectory(GetAppLoaderPath(), staging_path, true)) { | 259 if (!file_util::CopyDirectory(GetAppLoaderPath(), staging_path, true)) { |
| 276 LOG(ERROR) << "Copying app to staging path: " << staging_path.value() | 260 LOG(ERROR) << "Copying app to staging path: " << staging_path.value() |
| 277 << " failed."; | 261 << " failed."; |
| 278 return false; | 262 return false; |
| 279 } | 263 } |
| 280 | 264 |
| 281 if (!UpdatePlist(staging_path)) | 265 if (!UpdatePlist(staging_path)) |
| 282 return false; | 266 return false; |
| 283 | 267 |
| 284 if (!UpdateDisplayName(staging_path)) | 268 if (!UpdateDisplayName(staging_path)) |
| 285 return false; | 269 return false; |
| 286 | 270 |
| 287 if (!UpdateIcon(staging_path)) | 271 if (!UpdateIcon(staging_path)) |
| 288 return false; | 272 return false; |
| 289 | 273 |
| 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; | 274 return true; |
| 305 } | 275 } |
| 306 | 276 |
| 277 bool WebAppShortcutCreator::CreateShortcutsIn( | |
| 278 const std::vector<base::FilePath>& folders) const { | |
| 279 base::ScopedTempDir scoped_temp_dir; | |
| 280 if (!scoped_temp_dir.CreateUniqueTempDir()) | |
| 281 return false; | |
| 282 | |
| 283 base::FilePath app_name = GetShortcutName(); | |
| 284 base::FilePath staging_path = | |
| 285 scoped_temp_dir.path().Append(app_name); | |
| 286 if (!BuildShortcut(staging_path)) | |
| 287 return false; | |
| 288 | |
| 289 for (std::vector<base::FilePath>::const_iterator it = folders.begin(); | |
| 290 it != folders.end(); ++it) { | |
| 291 const base::FilePath& dst_path = *it; | |
| 292 if (!file_util::CopyDirectory(staging_path, dst_path, true)) { | |
| 293 LOG(ERROR) << "Copying app to dst path: " << dst_path.value() | |
| 294 << " failed"; | |
| 295 return false; | |
| 296 } | |
| 297 | |
| 298 base::mac::RemoveQuarantineAttribute(dst_path.Append(app_name)); | |
| 299 } | |
|
tapted
2013/06/19 00:58:35
this curly needs to be unindented one space
jackhou1
2013/06/19 02:00:19
Done.
| |
| 300 return true; | |
| 301 } | |
| 302 | |
| 303 bool WebAppShortcutCreator::CreateShortcuts() { | |
| 304 base::FilePath dst_path = GetDestinationPath(); | |
| 305 if (dst_path.empty() || !file_util::DirectoryExists(dst_path.DirName())) { | |
| 306 LOG(ERROR) << "Couldn't find an Applications directory to copy app to."; | |
| 307 return false; | |
| 308 } | |
| 309 | |
| 310 if (!file_util::CreateDirectory(app_data_path_)) { | |
| 311 LOG(ERROR) << "Creating app_data_path " << app_data_path_.value() | |
| 312 << " failed."; | |
| 313 return false; | |
| 314 } | |
| 315 | |
| 316 if (!file_util::CreateDirectory(dst_path)) { | |
| 317 LOG(ERROR) << "Creating directory " << dst_path.value() << " failed."; | |
| 318 return false; | |
| 319 } | |
| 320 | |
| 321 UpdateAppShortcutsSubdirLocalizedName(dst_path); | |
| 322 | |
| 323 std::vector<base::FilePath> paths; | |
| 324 paths.push_back(app_data_path_); | |
| 325 paths.push_back(dst_path); | |
| 326 if (!CreateShortcutsIn(paths)) | |
| 327 return false; | |
|
tapted
2013/06/19 00:58:35
What if app_data_path_ succeeds and app_path.DirNa
jackhou1
2013/06/19 02:00:19
Done.
| |
| 328 | |
| 329 UpdateInternalBundleIdentifier(); | |
| 330 RevealAppShimInFinder(); | |
| 331 return true; | |
| 332 } | |
| 333 | |
| 334 void WebAppShortcutCreator::DeleteShortcuts() { | |
| 335 base::FilePath dst_path = GetDestinationPath(); | |
| 336 if (!dst_path.empty()) | |
| 337 DeletePathAndParentIfEmpty(dst_path.Append(GetShortcutName())); | |
| 338 // In case the user has moved/renamed/copied the app bundle. | |
| 339 DeletePathAndParentIfEmpty(GetAppBundleById(GetBundleIdentifier())); | |
|
tapted
2013/06/19 00:58:35
Hmm.. Unfortunately, if they've moved it, we shoul
jackhou1
2013/06/19 02:00:19
Done.
| |
| 340 // Delete the internal one. | |
| 341 DeletePathAndParentIfEmpty(app_data_path_.Append(GetShortcutName())); | |
| 342 } | |
| 343 | |
| 344 bool WebAppShortcutCreator::UpdateShortcuts() { | |
| 345 base::FilePath dst_path = GetDestinationPath(); | |
| 346 base::FilePath app_path = dst_path.Append(GetShortcutName()); | |
| 347 | |
| 348 // If the path does not exist, check if a matching bundle can be found | |
| 349 // elsewhere. | |
| 350 if (dst_path.empty() || !file_util::PathExists(app_path)) | |
| 351 app_path = GetAppBundleById(GetBundleIdentifier()); | |
| 352 | |
| 353 if (app_path.empty()) | |
| 354 return false; | |
|
tapted
2013/06/19 00:58:35
Can we still update the copy in app_data_path_ ?
jackhou1
2013/06/19 02:00:19
Done.
tapted
2013/06/19 08:29:40
sorry this ^^ was hidden in a comment on the last
| |
| 355 | |
| 356 file_util::Delete(app_path, true); | |
| 357 | |
| 358 std::vector<base::FilePath> paths; | |
| 359 paths.push_back(app_data_path_); | |
| 360 paths.push_back(app_path.DirName()); | |
| 361 if (!CreateShortcutsIn(paths)) | |
| 362 return false; | |
| 363 | |
| 364 UpdateInternalBundleIdentifier(); | |
| 365 return true; | |
| 366 } | |
| 367 | |
| 307 base::FilePath WebAppShortcutCreator::GetAppLoaderPath() const { | 368 base::FilePath WebAppShortcutCreator::GetAppLoaderPath() const { |
| 308 return base::mac::PathForFrameworkBundleResource( | 369 return base::mac::PathForFrameworkBundleResource( |
| 309 base::mac::NSToCFCast(@"app_mode_loader.app")); | 370 base::mac::NSToCFCast(@"app_mode_loader.app")); |
| 310 } | 371 } |
| 311 | 372 |
| 312 base::FilePath WebAppShortcutCreator::GetDestinationPath() const { | 373 base::FilePath WebAppShortcutCreator::GetDestinationPath() const { |
| 313 base::FilePath path = GetWritableApplicationsDirectory(); | 374 base::FilePath path = GetWritableApplicationsDirectory(); |
| 314 if (path.empty()) | 375 if (path.empty()) |
| 315 return path; | 376 return path; |
| 316 return path.Append(GetLocalizableAppShortcutsSubdirName()); | 377 return path.Append(GetLocalizableAppShortcutsSubdirName()); |
| 317 } | 378 } |
| 318 | 379 |
| 319 bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const { | 380 bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const { |
| 320 NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); | 381 NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); |
| 321 NSString* extension_title = base::SysUTF16ToNSString(info_.title); | 382 NSString* extension_title = base::SysUTF16ToNSString(info_.title); |
| 322 NSString* extension_url = base::SysUTF8ToNSString(info_.url.spec()); | 383 NSString* extension_url = base::SysUTF8ToNSString(info_.url.spec()); |
| 323 NSString* chrome_bundle_id = base::SysUTF16ToNSString(chrome_bundle_id_); | 384 NSString* chrome_bundle_id = base::SysUTF8ToNSString(chrome_bundle_id_); |
| 324 NSDictionary* replacement_dict = | 385 NSDictionary* replacement_dict = |
| 325 [NSDictionary dictionaryWithObjectsAndKeys: | 386 [NSDictionary dictionaryWithObjectsAndKeys: |
| 326 extension_id, app_mode::kShortcutIdPlaceholder, | 387 extension_id, app_mode::kShortcutIdPlaceholder, |
| 327 extension_title, app_mode::kShortcutNamePlaceholder, | 388 extension_title, app_mode::kShortcutNamePlaceholder, |
| 328 extension_url, app_mode::kShortcutURLPlaceholder, | 389 extension_url, app_mode::kShortcutURLPlaceholder, |
| 329 chrome_bundle_id, app_mode::kShortcutBrowserBundleIDPlaceholder, | 390 chrome_bundle_id, app_mode::kShortcutBrowserBundleIDPlaceholder, |
| 330 nil]; | 391 nil]; |
| 331 | 392 |
| 332 NSString* plist_path = base::mac::FilePathToNSString( | 393 NSString* plist_path = base::mac::FilePathToNSString( |
| 333 app_path.Append("Contents").Append("Info.plist")); | 394 app_path.Append("Contents").Append("Info.plist")); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 344 // Remove leading and trailing '@'s. | 405 // Remove leading and trailing '@'s. |
| 345 NSString* variable = | 406 NSString* variable = |
| 346 [value substringWithRange:NSMakeRange(1, [value length] - 2)]; | 407 [value substringWithRange:NSMakeRange(1, [value length] - 2)]; |
| 347 | 408 |
| 348 NSString* substitution = [replacement_dict valueForKey:variable]; | 409 NSString* substitution = [replacement_dict valueForKey:variable]; |
| 349 if (substitution) | 410 if (substitution) |
| 350 [plist setObject:substitution forKey:key]; | 411 [plist setObject:substitution forKey:key]; |
| 351 } | 412 } |
| 352 | 413 |
| 353 // 2. Fill in other values. | 414 // 2. Fill in other values. |
| 354 [plist setObject:GetBundleIdentifier(plist) | 415 [plist setObject:base::SysUTF8ToNSString(GetBundleIdentifier()) |
| 355 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; | 416 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; |
| 356 [plist setObject:base::mac::FilePathToNSString(app_data_path_) | 417 [plist setObject:base::mac::FilePathToNSString(app_data_path_) |
| 357 forKey:app_mode::kCrAppModeUserDataDirKey]; | 418 forKey:app_mode::kCrAppModeUserDataDirKey]; |
| 358 [plist setObject:base::mac::FilePathToNSString(info_.profile_path.BaseName()) | 419 [plist setObject:base::mac::FilePathToNSString(info_.profile_path.BaseName()) |
| 359 forKey:app_mode::kCrAppModeProfileDirKey]; | 420 forKey:app_mode::kCrAppModeProfileDirKey]; |
| 360 [plist setObject:base::SysUTF8ToNSString(info_.profile_name) | 421 [plist setObject:base::SysUTF8ToNSString(info_.profile_name) |
| 361 forKey:app_mode::kCrAppModeProfileNameKey]; | 422 forKey:app_mode::kCrAppModeProfileNameKey]; |
| 362 [plist setObject:[NSNumber numberWithBool:YES] | 423 [plist setObject:[NSNumber numberWithBool:YES] |
| 363 forKey:app_mode::kLSHasLocalizedDisplayNameKey]; | 424 forKey:app_mode::kLSHasLocalizedDisplayNameKey]; |
| 364 | 425 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 if (!image_added) | 481 if (!image_added) |
| 421 return false; | 482 return false; |
| 422 | 483 |
| 423 base::FilePath resources_path = GetResourcesPath(app_path); | 484 base::FilePath resources_path = GetResourcesPath(app_path); |
| 424 if (!file_util::CreateDirectory(resources_path)) | 485 if (!file_util::CreateDirectory(resources_path)) |
| 425 return false; | 486 return false; |
| 426 | 487 |
| 427 return icon_family.WriteDataToFile(resources_path.Append("app.icns")); | 488 return icon_family.WriteDataToFile(resources_path.Append("app.icns")); |
| 428 } | 489 } |
| 429 | 490 |
| 430 NSString* WebAppShortcutCreator::GetBundleIdentifier(NSDictionary* plist) const | 491 bool WebAppShortcutCreator::UpdateInternalBundleIdentifier() const { |
| 431 { | 492 NSString* plist_path = base::mac::FilePathToNSString( |
| 432 NSString* bundle_id_template = | 493 app_data_path_.Append(GetShortcutName()) |
| 433 base::mac::ObjCCast<NSString>( | 494 .Append("Contents").Append("Info.plist")); |
| 434 [plist objectForKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]); | 495 NSMutableDictionary* plist = |
| 435 NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); | 496 [NSMutableDictionary dictionaryWithContentsOfFile:plist_path]; |
| 436 NSString* placeholder = | 497 |
| 437 [NSString stringWithFormat:@"@%@@", app_mode::kShortcutIdPlaceholder]; | 498 [plist setObject:base::SysUTF8ToNSString(GetInternalBundleIdentifier()) |
| 438 NSString* bundle_id = | 499 forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; |
| 439 [bundle_id_template | 500 return [plist writeToFile:plist_path atomically:YES]; |
|
tapted
2013/06/19 00:58:35
nit: atomically:YES on a new line and align colons
jackhou1
2013/06/19 02:00:19
Done.
| |
| 440 stringByReplacingOccurrencesOfString:placeholder | 501 } |
| 441 withString:extension_id]; | 502 |
| 503 base::FilePath WebAppShortcutCreator::GetAppBundleById( | |
| 504 const std::string& bundle_id) const { | |
| 505 base::mac::ScopedCFTypeRef<CFStringRef> bundle_id_cf( | |
| 506 base::SysUTF8ToCFStringRef(bundle_id)); | |
| 507 CFURLRef url_ref = NULL; | |
| 508 OSStatus status = LSFindApplicationForInfo( | |
| 509 kLSUnknownCreator, bundle_id_cf.get(), NULL, NULL, &url_ref); | |
| 510 if (status != noErr) | |
| 511 return base::FilePath(); | |
| 512 | |
| 513 base::mac::ScopedCFTypeRef<CFURLRef> url(url_ref); | |
| 514 NSString* path_string = [base::mac::CFToNSCast(url.get()) path]; | |
| 515 return base::FilePath([path_string fileSystemRepresentation]); | |
| 516 } | |
| 517 | |
| 518 std::string WebAppShortcutCreator::GetBundleIdentifier() const { | |
| 519 // Replace spaces in the profile path with hyphen. | |
| 520 std::string normalized_profile_path; | |
| 521 ReplaceChars(info_.profile_path.BaseName().value(), | |
| 522 " ", "-", &normalized_profile_path); | |
| 523 | |
| 524 // This matches APP_MODE_APP_BUNDLE_ID in chrome/chrome.gyp. | |
| 525 std::string bundle_id = | |
| 526 chrome_bundle_id_ + std::string(".app.") + | |
| 527 normalized_profile_path + "-" + info_.extension_id; | |
| 528 | |
| 442 return bundle_id; | 529 return bundle_id; |
| 443 } | 530 } |
| 444 | 531 |
| 445 void WebAppShortcutCreator::RevealGeneratedBundleInFinder( | 532 std::string WebAppShortcutCreator::GetInternalBundleIdentifier() const { |
| 446 const base::FilePath& generated_bundle) const { | 533 return GetBundleIdentifier() + "-internal"; |
| 534 } | |
| 535 | |
| 536 void WebAppShortcutCreator::RevealAppShimInFinder() const { | |
| 537 base::FilePath dst_path = GetDestinationPath(); | |
| 538 if (dst_path.empty()) | |
| 539 return; | |
| 540 | |
| 541 base::FilePath app_path = dst_path.Append(GetShortcutName()); | |
| 447 [[NSWorkspace sharedWorkspace] | 542 [[NSWorkspace sharedWorkspace] |
| 448 selectFile:base::mac::FilePathToNSString(generated_bundle) | 543 selectFile:base::mac::FilePathToNSString(app_path) |
| 449 inFileViewerRootedAtPath:nil]; | 544 inFileViewerRootedAtPath:nil]; |
| 450 } | 545 } |
| 451 | 546 |
| 452 base::FilePath GetAppInstallPath( | 547 base::FilePath GetAppInstallPath( |
| 453 const ShellIntegration::ShortcutInfo& shortcut_info) { | 548 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 454 WebAppShortcutCreator shortcut_creator(base::FilePath(), | 549 WebAppShortcutCreator shortcut_creator(base::FilePath(), |
| 455 shortcut_info, | 550 shortcut_info, |
| 456 string16()); | 551 std::string()); |
| 457 return shortcut_creator.GetShortcutPath(); | 552 base::FilePath dst_path = shortcut_creator.GetDestinationPath(); |
| 553 return dst_path.empty() ? | |
| 554 base::FilePath() : dst_path.Append(shortcut_creator.GetShortcutName()); | |
| 458 } | 555 } |
| 459 | 556 |
| 460 void MaybeLaunchShortcut(const ShellIntegration::ShortcutInfo& shortcut_info) { | 557 void MaybeLaunchShortcut(const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 461 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims)) | 558 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims)) |
| 462 return; | 559 return; |
| 463 | 560 |
| 464 content::BrowserThread::PostTask( | 561 content::BrowserThread::PostTask( |
| 465 content::BrowserThread::FILE, FROM_HERE, | 562 content::BrowserThread::FILE, FROM_HERE, |
| 466 base::Bind(&LaunchShimOnFileThread, shortcut_info)); | 563 base::Bind(&LaunchShimOnFileThread, shortcut_info)); |
| 467 } | 564 } |
| 468 | 565 |
| 469 namespace internals { | 566 namespace internals { |
| 470 | 567 |
| 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( | 568 bool CreatePlatformShortcuts( |
| 491 const base::FilePath& app_data_path, | 569 const base::FilePath& app_data_path, |
| 492 const ShellIntegration::ShortcutInfo& shortcut_info, | 570 const ShellIntegration::ShortcutInfo& shortcut_info, |
| 493 const ShellIntegration::ShortcutLocations& creation_locations) { | 571 const ShellIntegration::ShortcutLocations& creation_locations) { |
| 494 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 572 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 495 string16 bundle_id = UTF8ToUTF16(base::mac::BaseBundleID()); | 573 WebAppShortcutCreator shortcut_creator( |
| 496 WebAppShortcutCreator shortcut_creator(app_data_path, shortcut_info, | 574 app_data_path, shortcut_info, base::mac::BaseBundleID()); |
| 497 bundle_id); | 575 return shortcut_creator.CreateShortcuts(); |
| 498 return shortcut_creator.CreateShortcut(); | |
| 499 } | 576 } |
| 500 | 577 |
| 501 void DeletePlatformShortcuts( | 578 void DeletePlatformShortcuts( |
| 502 const base::FilePath& app_data_path, | 579 const base::FilePath& app_data_path, |
| 503 const ShellIntegration::ShortcutInfo& info) { | 580 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 504 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | 581 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 505 | 582 WebAppShortcutCreator shortcut_creator( |
| 506 base::FilePath bundle_path = GetAppBundleByExtensionId(info.extension_id); | 583 app_data_path, shortcut_info, base::mac::BaseBundleID()); |
| 507 file_util::Delete(bundle_path, true); | 584 shortcut_creator.DeleteShortcuts(); |
| 508 } | 585 } |
| 509 | 586 |
| 510 void UpdatePlatformShortcuts( | 587 void UpdatePlatformShortcuts( |
| 511 const base::FilePath& app_data_path, | 588 const base::FilePath& app_data_path, |
| 512 const string16& old_app_title, | 589 const string16& old_app_title, |
| 513 const ShellIntegration::ShortcutInfo& shortcut_info) { | 590 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 514 // TODO(benwells): Implement this when shortcuts / weblings are enabled on | 591 WebAppShortcutCreator shortcut_creator( |
| 515 // mac. | 592 app_data_path, shortcut_info, base::mac::BaseBundleID()); |
| 593 shortcut_creator.UpdateShortcuts(); | |
| 516 } | 594 } |
| 517 | 595 |
| 518 } // namespace internals | 596 } // namespace internals |
| 519 | 597 |
| 520 } // namespace web_app | 598 } // namespace web_app |
| OLD | NEW |