Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Side by Side Diff: chrome/browser/web_applications/web_app_mac.mm

Issue 15724019: Recreate shortcuts on app update. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « chrome/browser/web_applications/web_app_mac.h ('k') | chrome/browser/web_applications/web_app_mac_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698