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