diff options
author | jackhou@chromium.org <jackhou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-20 18:14:05 +0000 |
---|---|---|
committer | jackhou@chromium.org <jackhou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-20 18:14:05 +0000 |
commit | 19c779b9bc2de83a279eed64e2561f2f7035a628 (patch) | |
tree | 4a7bda29e0114b3545a6502e35d8306ebe07a537 /chrome/browser/web_applications | |
parent | a0f4cb93d270111f99996525c8db26af18f1a498 (diff) | |
download | chromium_src-19c779b9bc2de83a279eed64e2561f2f7035a628.zip chromium_src-19c779b9bc2de83a279eed64e2561f2f7035a628.tar.gz chromium_src-19c779b9bc2de83a279eed64e2561f2f7035a628.tar.bz2 |
Recreate shortcuts on app update.
At the moment, if an app's name or icon changes, the shim is not updated.
This change updates the shim by deleting existing app shims and recreating
them.
Also launch the internal copy of a shim if the one in the applications directory
does not exist.
BUG=168080
TEST=Load an unbundled app.
Create a shortcut for it.
Change the app's name and reload it.
The app shim's display name should be updated.
Review URL: https://chromiumcodereview.appspot.com/15724019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207481 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/web_applications')
-rw-r--r-- | chrome/browser/web_applications/web_app_mac.h | 50 | ||||
-rw-r--r-- | chrome/browser/web_applications/web_app_mac.mm | 279 | ||||
-rw-r--r-- | chrome/browser/web_applications/web_app_mac_unittest.mm | 93 |
3 files changed, 301 insertions, 121 deletions
diff --git a/chrome/browser/web_applications/web_app_mac.h b/chrome/browser/web_applications/web_app_mac.h index e0ba9ab..fed1b1e 100644 --- a/chrome/browser/web_applications/web_app_mac.h +++ b/chrome/browser/web_applications/web_app_mac.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_MAC_H_ #define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_MAC_H_ +#include <vector> + #include "base/files/file_path.h" #include "base/gtest_prod_util.h" #include "base/strings/string16.h" @@ -38,45 +40,63 @@ class WebAppShortcutCreator { WebAppShortcutCreator( const base::FilePath& app_data_path, const ShellIntegration::ShortcutInfo& shortcut_info, - const string16& chrome_bundle_id); + const std::string& chrome_bundle_id); virtual ~WebAppShortcutCreator(); - // Returns a path to the destination where the app should be written to. - base::FilePath GetShortcutPath() const; + // Returns the base name for the shortcut. + base::FilePath GetShortcutName() const; + + // Returns a path to the Chrome Apps folder in the relevant applications + // folder. E.g. ~/Applications or /Applications. + virtual base::FilePath GetDestinationPath() const; - // Copies the app launcher template into place and fills in all relevant - // information. - bool CreateShortcut(); + bool CreateShortcuts(); + void DeleteShortcuts(); + bool UpdateShortcuts(); protected: // Returns a path to the app loader. base::FilePath GetAppLoaderPath() const; - // Returns a path to the destination where the app should be written to. - virtual base::FilePath GetDestinationPath() const; - // Updates the plist inside |app_path| with information about the app. bool UpdatePlist(const base::FilePath& app_path) const; // Updates the icon for the shortcut. bool UpdateIcon(const base::FilePath& app_path) const; + // Returns a path to an app bundle with the given id. Or an empty path if no + // matching bundle was found. + // Protected and virtual so it can be mocked out for testing. + virtual base::FilePath GetAppBundleById(const std::string& bundle_id) const; + private: FRIEND_TEST_ALL_PREFIXES(WebAppShortcutCreatorTest, UpdateIcon); + FRIEND_TEST_ALL_PREFIXES(WebAppShortcutCreatorTest, UpdateShortcuts); + + // Copies the app loader template into a temporary directory and fills in all + // relevant information. + bool BuildShortcut(const base::FilePath& staging_path) const; + + // Builds a shortcut and copies it into the given destination folders. + // Returns with the number of successful copies. Returns on the first failure. + size_t CreateShortcutsIn(const std::vector<base::FilePath>& folders) const; // Updates the InfoPlist.string inside |app_path| with the display name for // the app. bool UpdateDisplayName(const base::FilePath& app_path) const; + // Updates the bundle id of the internal copy of the app shim bundle. + bool UpdateInternalBundleIdentifier() const; + // Returns the bundle identifier to use for this app bundle. - // |plist| is a dictionary containg a copy of the template plist file to - // be used for creating the app bundle. - NSString* GetBundleIdentifier(NSDictionary* plist) const; + std::string GetBundleIdentifier() const; + + // Returns the bundle identifier for the internal copy of the bundle. + std::string GetInternalBundleIdentifier() const; // Show the bundle we just generated in the Finder. - virtual void RevealGeneratedBundleInFinder( - const base::FilePath& generated_bundle) const; + virtual void RevealAppShimInFinder() const; // Path to the data directory for this app. For example: // ~/Library/Application Support/Chromium/Default/Web Applications/_crx_abc/ @@ -86,7 +106,7 @@ class WebAppShortcutCreator { ShellIntegration::ShortcutInfo info_; // The CFBundleIdentifier of the Chrome browser bundle. - string16 chrome_bundle_id_; + std::string chrome_bundle_id_; }; } // namespace web_app diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm index 4d67fc8..fa67351 100644 --- a/chrome/browser/web_applications/web_app_mac.mm +++ b/chrome/browser/web_applications/web_app_mac.mm @@ -166,7 +166,16 @@ void LaunchShimOnFileThread( const ShellIntegration::ShortcutInfo& shortcut_info) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); base::FilePath shim_path = web_app::GetAppInstallPath(shortcut_info); - if (shim_path.empty()) + + if (shim_path.empty() || !file_util::PathExists(shim_path)) { + // The user may have deleted the copy in the Applications folder, use the + // one in the web app's app_data_path. + base::FilePath app_data_path = web_app::GetWebAppDataDirectory( + shortcut_info.profile_path, shortcut_info.extension_id, GURL()); + shim_path = app_data_path.Append(shim_path.BaseName()); + } + + if (!file_util::PathExists(shim_path)) return; CommandLine command_line(CommandLine::NO_PROGRAM); @@ -219,6 +228,14 @@ void UpdateAppShortcutsSubdirLocalizedName( atomically:YES]; } +void DeletePathAndParentIfEmpty(const base::FilePath& app_path) { + DCHECK(!app_path.empty()); + file_util::Delete(app_path, true); + base::FilePath apps_folder = app_path.DirName(); + if (file_util::IsDirectoryEmpty(apps_folder)) + file_util::Delete(apps_folder, false); +} + } // namespace namespace web_app { @@ -227,7 +244,7 @@ namespace web_app { WebAppShortcutCreator::WebAppShortcutCreator( const base::FilePath& app_data_path, const ShellIntegration::ShortcutInfo& shortcut_info, - const string16& chrome_bundle_id) + const std::string& chrome_bundle_id) : app_data_path_(app_data_path), info_(shortcut_info), chrome_bundle_id_(chrome_bundle_id) { @@ -236,11 +253,7 @@ WebAppShortcutCreator::WebAppShortcutCreator( WebAppShortcutCreator::~WebAppShortcutCreator() { } -base::FilePath WebAppShortcutCreator::GetShortcutPath() const { - base::FilePath dst_path = GetDestinationPath(); - if (dst_path.empty()) - return dst_path; - +base::FilePath WebAppShortcutCreator::GetShortcutName() const { std::string app_name; // Check if there should be a separate shortcut made for different profiles. // Such shortcuts will have a |profile_name| set on the ShortcutInfo, @@ -250,65 +263,135 @@ base::FilePath WebAppShortcutCreator::GetShortcutPath() const { app_name += ' '; } app_name += info_.extension_id; - return dst_path.Append(app_name).ReplaceExtension("app"); + return base::FilePath(app_name).ReplaceExtension("app"); } -bool WebAppShortcutCreator::CreateShortcut() { - base::FilePath app_path = GetShortcutPath(); - base::FilePath app_name = app_path.BaseName(); - base::FilePath dst_path = app_path.DirName(); - if (app_path.empty() || !file_util::DirectoryExists(dst_path.DirName())) { +bool WebAppShortcutCreator::BuildShortcut( + const base::FilePath& staging_path) const { + // Update the app's plist and icon in a temp directory. This works around + // a Finder bug where the app's icon doesn't properly update. + if (!file_util::CopyDirectory(GetAppLoaderPath(), staging_path, true)) { + LOG(ERROR) << "Copying app to staging path: " << staging_path.value() + << " failed."; + return false; + } + + if (!UpdatePlist(staging_path)) + return false; + + if (!UpdateDisplayName(staging_path)) + return false; + + if (!UpdateIcon(staging_path)) + return false; + + return true; +} + +size_t WebAppShortcutCreator::CreateShortcutsIn( + const std::vector<base::FilePath>& folders) const { + size_t succeeded = 0; + + base::ScopedTempDir scoped_temp_dir; + if (!scoped_temp_dir.CreateUniqueTempDir()) + return 0; + + base::FilePath app_name = GetShortcutName(); + base::FilePath staging_path = + scoped_temp_dir.path().Append(app_name); + if (!BuildShortcut(staging_path)) + return 0; + + for (std::vector<base::FilePath>::const_iterator it = folders.begin(); + it != folders.end(); ++it) { + const base::FilePath& dst_path = *it; + if (!file_util::CopyDirectory(staging_path, dst_path, true)) { + LOG(ERROR) << "Copying app to dst path: " << dst_path.value() + << " failed"; + return succeeded; + } + + base::mac::RemoveQuarantineAttribute(dst_path.Append(app_name)); + ++succeeded; + } + + return succeeded; +} + +bool WebAppShortcutCreator::CreateShortcuts() { + base::FilePath dst_path = GetDestinationPath(); + if (dst_path.empty() || !file_util::DirectoryExists(dst_path.DirName())) { LOG(ERROR) << "Couldn't find an Applications directory to copy app to."; return false; } + if (!file_util::CreateDirectory(app_data_path_)) { LOG(ERROR) << "Creating app_data_path " << app_data_path_.value() << " failed."; return false; } + if (!file_util::CreateDirectory(dst_path)) { LOG(ERROR) << "Creating directory " << dst_path.value() << " failed."; return false; } + UpdateAppShortcutsSubdirLocalizedName(dst_path); - base::ScopedTempDir scoped_temp_dir; - if (!scoped_temp_dir.CreateUniqueTempDir()) + std::vector<base::FilePath> paths; + paths.push_back(app_data_path_); + paths.push_back(dst_path); + size_t success_count = CreateShortcutsIn(paths); + if (success_count == 0) return false; - base::FilePath staging_path = scoped_temp_dir.path().Append(app_name); - // Update the app's plist and icon in a temp directory. This works around - // a Finder bug where the app's icon doesn't properly update. - if (!file_util::CopyDirectory(GetAppLoaderPath(), staging_path, true)) { - LOG(ERROR) << "Copying app to staging path: " << staging_path.value() - << " failed."; - return false; - } + UpdateInternalBundleIdentifier(); - if (!UpdatePlist(staging_path)) + if (success_count != paths.size()) return false; - if (!UpdateDisplayName(staging_path)) - return false; + RevealAppShimInFinder(); + return true; +} - if (!UpdateIcon(staging_path)) - return false; +void WebAppShortcutCreator::DeleteShortcuts() { + base::FilePath dst_path = GetDestinationPath(); + if (!dst_path.empty()) + DeletePathAndParentIfEmpty(dst_path.Append(GetShortcutName())); - // Put one copy in the app's app_data_path so we can still run it if the user - // deletes the one in the applications folder. - if (!file_util::CopyDirectory(staging_path, app_data_path_, true)) { - NOTREACHED(); - return false; + // In case the user has moved/renamed/copied the app bundle. + base::FilePath bundle_path = GetAppBundleById(GetBundleIdentifier()); + if (!bundle_path.empty()) + file_util::Delete(bundle_path, true); + + // Delete the internal one. + DeletePathAndParentIfEmpty(app_data_path_.Append(GetShortcutName())); +} + +bool WebAppShortcutCreator::UpdateShortcuts() { + std::vector<base::FilePath> paths; + file_util::Delete(app_data_path_.Append(GetShortcutName()), true); + paths.push_back(app_data_path_); + + base::FilePath dst_path = GetDestinationPath(); + base::FilePath app_path = dst_path.Append(GetShortcutName()); + + // If the path does not exist, check if a matching bundle can be found + // elsewhere. + if (dst_path.empty() || !file_util::PathExists(app_path)) + app_path = GetAppBundleById(GetBundleIdentifier()); + + if (!app_path.empty()) { + file_util::Delete(app_path, true); + paths.push_back(app_path.DirName()); } - base::mac::RemoveQuarantineAttribute(app_data_path_.Append(app_name)); - if (!file_util::CopyDirectory(staging_path, dst_path, true)) + size_t success_count = CreateShortcutsIn(paths); + if (success_count == 0) return false; - base::mac::RemoveQuarantineAttribute(app_path); - RevealGeneratedBundleInFinder(app_path); - - return true; + UpdateInternalBundleIdentifier(); + return success_count == paths.size() && !app_path.empty(); } base::FilePath WebAppShortcutCreator::GetAppLoaderPath() const { @@ -327,7 +410,7 @@ bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const { NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); NSString* extension_title = base::SysUTF16ToNSString(info_.title); NSString* extension_url = base::SysUTF8ToNSString(info_.url.spec()); - NSString* chrome_bundle_id = base::SysUTF16ToNSString(chrome_bundle_id_); + NSString* chrome_bundle_id = base::SysUTF8ToNSString(chrome_bundle_id_); NSDictionary* replacement_dict = [NSDictionary dictionaryWithObjectsAndKeys: extension_id, app_mode::kShortcutIdPlaceholder, @@ -358,7 +441,7 @@ bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const { } // 2. Fill in other values. - [plist setObject:GetBundleIdentifier(plist) + [plist setObject:base::SysUTF8ToNSString(GetBundleIdentifier()) forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; [plist setObject:base::mac::FilePathToNSString(app_data_path_) forKey:app_mode::kCrAppModeUserDataDirKey]; @@ -373,7 +456,8 @@ bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const { [plist setObject:base::mac::FilePathToNSString(app_name) forKey:base::mac::CFToNSCast(kCFBundleNameKey)]; - return [plist writeToFile:plist_path atomically:YES]; + return [plist writeToFile:plist_path + atomically:YES]; } bool WebAppShortcutCreator::UpdateDisplayName( @@ -434,25 +518,60 @@ bool WebAppShortcutCreator::UpdateIcon(const base::FilePath& app_path) const { return icon_family.WriteDataToFile(resources_path.Append("app.icns")); } -NSString* WebAppShortcutCreator::GetBundleIdentifier(NSDictionary* plist) const -{ - NSString* bundle_id_template = - base::mac::ObjCCast<NSString>( - [plist objectForKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]); - NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id); - NSString* placeholder = - [NSString stringWithFormat:@"@%@@", app_mode::kShortcutIdPlaceholder]; - NSString* bundle_id = - [bundle_id_template - stringByReplacingOccurrencesOfString:placeholder - withString:extension_id]; +bool WebAppShortcutCreator::UpdateInternalBundleIdentifier() const { + NSString* plist_path = base::mac::FilePathToNSString( + app_data_path_.Append(GetShortcutName()) + .Append("Contents").Append("Info.plist")); + NSMutableDictionary* plist = + [NSMutableDictionary dictionaryWithContentsOfFile:plist_path]; + + [plist setObject:base::SysUTF8ToNSString(GetInternalBundleIdentifier()) + forKey:base::mac::CFToNSCast(kCFBundleIdentifierKey)]; + return [plist writeToFile:plist_path + atomically:YES]; +} + +base::FilePath WebAppShortcutCreator::GetAppBundleById( + const std::string& bundle_id) const { + base::mac::ScopedCFTypeRef<CFStringRef> bundle_id_cf( + base::SysUTF8ToCFStringRef(bundle_id)); + CFURLRef url_ref = NULL; + OSStatus status = LSFindApplicationForInfo( + kLSUnknownCreator, bundle_id_cf.get(), NULL, NULL, &url_ref); + if (status != noErr) + return base::FilePath(); + + base::mac::ScopedCFTypeRef<CFURLRef> url(url_ref); + NSString* path_string = [base::mac::CFToNSCast(url.get()) path]; + return base::FilePath([path_string fileSystemRepresentation]); +} + +std::string WebAppShortcutCreator::GetBundleIdentifier() const { + // Replace spaces in the profile path with hyphen. + std::string normalized_profile_path; + ReplaceChars(info_.profile_path.BaseName().value(), + " ", "-", &normalized_profile_path); + + // This matches APP_MODE_APP_BUNDLE_ID in chrome/chrome.gyp. + std::string bundle_id = + chrome_bundle_id_ + std::string(".app.") + + normalized_profile_path + "-" + info_.extension_id; + return bundle_id; } -void WebAppShortcutCreator::RevealGeneratedBundleInFinder( - const base::FilePath& generated_bundle) const { +std::string WebAppShortcutCreator::GetInternalBundleIdentifier() const { + return GetBundleIdentifier() + "-internal"; +} + +void WebAppShortcutCreator::RevealAppShimInFinder() const { + base::FilePath dst_path = GetDestinationPath(); + if (dst_path.empty()) + return; + + base::FilePath app_path = dst_path.Append(GetShortcutName()); [[NSWorkspace sharedWorkspace] - selectFile:base::mac::FilePathToNSString(generated_bundle) + selectFile:base::mac::FilePathToNSString(app_path) inFileViewerRootedAtPath:nil]; } @@ -460,8 +579,10 @@ base::FilePath GetAppInstallPath( const ShellIntegration::ShortcutInfo& shortcut_info) { WebAppShortcutCreator shortcut_creator(base::FilePath(), shortcut_info, - string16()); - return shortcut_creator.GetShortcutPath(); + std::string()); + base::FilePath dst_path = shortcut_creator.GetDestinationPath(); + return dst_path.empty() ? + base::FilePath() : dst_path.Append(shortcut_creator.GetShortcutName()); } void MaybeLaunchShortcut(const ShellIntegration::ShortcutInfo& shortcut_info) { @@ -475,51 +596,33 @@ void MaybeLaunchShortcut(const ShellIntegration::ShortcutInfo& shortcut_info) { namespace internals { -base::FilePath GetAppBundleByExtensionId(std::string extension_id) { - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); - // This matches APP_MODE_APP_BUNDLE_ID in chrome/chrome.gyp. - std::string bundle_id = - base::mac::BaseBundleID() + std::string(".app.") + extension_id; - base::mac::ScopedCFTypeRef<CFStringRef> bundle_id_cf( - base::SysUTF8ToCFStringRef(bundle_id)); - CFURLRef url_ref = NULL; - OSStatus status = LSFindApplicationForInfo( - kLSUnknownCreator, bundle_id_cf.get(), NULL, NULL, &url_ref); - base::mac::ScopedCFTypeRef<CFURLRef> url(url_ref); - - if (status != noErr) - return base::FilePath(); - - NSString* path_string = [base::mac::CFToNSCast(url.get()) path]; - return base::FilePath([path_string fileSystemRepresentation]); -} - bool CreatePlatformShortcuts( const base::FilePath& app_data_path, const ShellIntegration::ShortcutInfo& shortcut_info, const ShellIntegration::ShortcutLocations& creation_locations) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); - string16 bundle_id = UTF8ToUTF16(base::mac::BaseBundleID()); - WebAppShortcutCreator shortcut_creator(app_data_path, shortcut_info, - bundle_id); - return shortcut_creator.CreateShortcut(); + WebAppShortcutCreator shortcut_creator( + app_data_path, shortcut_info, base::mac::BaseBundleID()); + return shortcut_creator.CreateShortcuts(); } void DeletePlatformShortcuts( const base::FilePath& app_data_path, - const ShellIntegration::ShortcutInfo& info) { + const ShellIntegration::ShortcutInfo& shortcut_info) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); - - base::FilePath bundle_path = GetAppBundleByExtensionId(info.extension_id); - file_util::Delete(bundle_path, true); + WebAppShortcutCreator shortcut_creator( + app_data_path, shortcut_info, base::mac::BaseBundleID()); + shortcut_creator.DeleteShortcuts(); } void UpdatePlatformShortcuts( const base::FilePath& app_data_path, const string16& old_app_title, const ShellIntegration::ShortcutInfo& shortcut_info) { - // TODO(benwells): Implement this when shortcuts / weblings are enabled on - // mac. + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + WebAppShortcutCreator shortcut_creator( + app_data_path, shortcut_info, base::mac::BaseBundleID()); + shortcut_creator.UpdateShortcuts(); } } // namespace internals diff --git a/chrome/browser/web_applications/web_app_mac_unittest.mm b/chrome/browser/web_applications/web_app_mac_unittest.mm index 18ada70..6112245 100644 --- a/chrome/browser/web_applications/web_app_mac_unittest.mm +++ b/chrome/browser/web_applications/web_app_mac_unittest.mm @@ -30,27 +30,31 @@ using ::testing::NiceMock; namespace { +const char kFakeChromeBundleId[] = "fake.cfbundleidentifier"; + class WebAppShortcutCreatorMock : public web_app::WebAppShortcutCreator { public: explicit WebAppShortcutCreatorMock( const base::FilePath& app_data_path, const ShellIntegration::ShortcutInfo& shortcut_info) - : WebAppShortcutCreator(app_data_path, shortcut_info, - UTF8ToUTF16("fake.cfbundleidentifier")) { + : WebAppShortcutCreator(app_data_path, + shortcut_info, + kFakeChromeBundleId) { } MOCK_CONST_METHOD0(GetDestinationPath, base::FilePath()); - MOCK_CONST_METHOD1(RevealGeneratedBundleInFinder, - void (const base::FilePath&)); + MOCK_CONST_METHOD1(GetAppBundleById, + base::FilePath(const std::string& bundle_id)); + MOCK_CONST_METHOD0(RevealAppShimInFinder, void()); }; ShellIntegration::ShortcutInfo GetShortcutInfo() { ShellIntegration::ShortcutInfo info; - info.extension_id = "extension_id"; + info.extension_id = "extensionid"; info.extension_path = base::FilePath("/fake/extension/path"); info.title = ASCIIToUTF16("Shortcut Title"); info.url = GURL("http://example.com/"); - info.profile_path = base::FilePath("Default"); + info.profile_path = base::FilePath("Profile 1"); info.profile_name = "profile name"; return info; } @@ -59,7 +63,7 @@ ShellIntegration::ShortcutInfo GetShortcutInfo() { namespace web_app { -TEST(WebAppShortcutCreatorTest, CreateShortcut) { +TEST(WebAppShortcutCreatorTest, CreateShortcuts) { base::ScopedTempDir temp_app_data_path; EXPECT_TRUE(temp_app_data_path.CreateUniqueTempDir()); base::ScopedTempDir temp_dst_dir; @@ -67,7 +71,6 @@ TEST(WebAppShortcutCreatorTest, CreateShortcut) { ShellIntegration::ShortcutInfo info = GetShortcutInfo(); - base::FilePath app_name( info.profile_path.value() + " " + info.extension_id + ".app"); base::FilePath app_in_app_data_path_path = @@ -79,12 +82,12 @@ TEST(WebAppShortcutCreatorTest, CreateShortcut) { temp_app_data_path.path(), info); EXPECT_CALL(shortcut_creator, GetDestinationPath()) .WillRepeatedly(Return(dst_folder)); - EXPECT_CALL(shortcut_creator, RevealGeneratedBundleInFinder(dst_path)); + EXPECT_CALL(shortcut_creator, RevealAppShimInFinder()); - EXPECT_TRUE(shortcut_creator.CreateShortcut()); + EXPECT_TRUE(shortcut_creator.CreateShortcuts()); EXPECT_TRUE(file_util::PathExists(app_in_app_data_path_path)); EXPECT_TRUE(file_util::PathExists(dst_path)); - EXPECT_EQ(dst_path.value(), shortcut_creator.GetShortcutPath().value()); + EXPECT_EQ(dst_path.BaseName(), shortcut_creator.GetShortcutName()); base::FilePath plist_path = dst_path.Append("Contents").Append("Info.plist"); NSDictionary* plist = [NSDictionary dictionaryWithContentsOfFile: @@ -107,12 +110,66 @@ TEST(WebAppShortcutCreatorTest, CreateShortcut) { } } +TEST(WebAppShortcutCreatorTest, UpdateShortcuts) { + base::ScopedTempDir temp_app_data_path; + EXPECT_TRUE(temp_app_data_path.CreateUniqueTempDir()); + base::ScopedTempDir temp_dst_dir; + EXPECT_TRUE(temp_dst_dir.CreateUniqueTempDir()); + base::ScopedTempDir temp_dst_dir_other; + EXPECT_TRUE(temp_dst_dir_other.CreateUniqueTempDir()); + + ShellIntegration::ShortcutInfo info = GetShortcutInfo(); + + base::FilePath app_name( + info.profile_path.value() + " " + info.extension_id + ".app"); + base::FilePath app_in_app_data_path_path = + temp_app_data_path.path().Append(app_name); + base::FilePath dst_folder = temp_dst_dir.path(); + base::FilePath other_folder = temp_dst_dir_other.path(); + + NiceMock<WebAppShortcutCreatorMock> shortcut_creator( + temp_app_data_path.path(), info); + EXPECT_CALL(shortcut_creator, GetDestinationPath()) + .WillRepeatedly(Return(dst_folder)); + + std::string expected_bundle_id = kFakeChromeBundleId; + expected_bundle_id += ".app.Profile-1-" + info.extension_id; + EXPECT_CALL(shortcut_creator, GetAppBundleById(expected_bundle_id)) + .WillOnce(Return(other_folder.Append(app_name))); + + shortcut_creator.BuildShortcut(other_folder.Append(app_name)); + + EXPECT_TRUE(file_util::Delete( + other_folder.Append(app_name).Append("Contents"), true)); + + EXPECT_TRUE(shortcut_creator.UpdateShortcuts()); + EXPECT_FALSE(file_util::PathExists(dst_folder.Append(app_name))); + EXPECT_TRUE(file_util::PathExists( + other_folder.Append(app_name).Append("Contents"))); + + // Also test case where GetAppBundleById fails. + EXPECT_CALL(shortcut_creator, GetAppBundleById(expected_bundle_id)) + .WillOnce(Return(base::FilePath())); + + shortcut_creator.BuildShortcut(other_folder.Append(app_name)); + + EXPECT_TRUE(file_util::Delete( + other_folder.Append(app_name).Append("Contents"), true)); + + EXPECT_FALSE(shortcut_creator.UpdateShortcuts()); + EXPECT_FALSE(file_util::PathExists(dst_folder.Append(app_name))); + EXPECT_FALSE(file_util::PathExists( + other_folder.Append(app_name).Append("Contents"))); +} + TEST(WebAppShortcutCreatorTest, CreateAppListShortcut) { - base::ScopedTempDir scoped_temp_dir; - EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); - base::FilePath dst_folder = scoped_temp_dir.path(); + base::ScopedTempDir temp_dst_dir; + EXPECT_TRUE(temp_dst_dir.CreateUniqueTempDir()); + ShellIntegration::ShortcutInfo info = GetShortcutInfo(); + base::FilePath dst_folder = temp_dst_dir.path(); + // With an empty |profile_name|, the shortcut path should not have the profile // directory prepended to the extension id on the app bundle name. info.profile_name.clear(); @@ -121,7 +178,7 @@ TEST(WebAppShortcutCreatorTest, CreateAppListShortcut) { NiceMock<WebAppShortcutCreatorMock> shortcut_creator(base::FilePath(), info); EXPECT_CALL(shortcut_creator, GetDestinationPath()) .WillRepeatedly(Return(dst_folder)); - EXPECT_EQ(dst_path.value(), shortcut_creator.GetShortcutPath().value()); + EXPECT_EQ(dst_path.BaseName(), shortcut_creator.GetShortcutName()); } TEST(WebAppShortcutCreatorTest, RunShortcut) { @@ -140,9 +197,9 @@ TEST(WebAppShortcutCreatorTest, RunShortcut) { temp_app_data_path.path(), info); EXPECT_CALL(shortcut_creator, GetDestinationPath()) .WillRepeatedly(Return(dst_folder)); - EXPECT_CALL(shortcut_creator, RevealGeneratedBundleInFinder(dst_path)); + EXPECT_CALL(shortcut_creator, RevealAppShimInFinder()); - EXPECT_TRUE(shortcut_creator.CreateShortcut()); + EXPECT_TRUE(shortcut_creator.CreateShortcuts()); EXPECT_TRUE(file_util::PathExists(dst_path)); ssize_t status = getxattr( @@ -164,7 +221,7 @@ TEST(WebAppShortcutCreatorTest, CreateFailure) { temp_app_data_path.path(), GetShortcutInfo()); EXPECT_CALL(shortcut_creator, GetDestinationPath()) .WillRepeatedly(Return(non_existent_path)); - EXPECT_FALSE(shortcut_creator.CreateShortcut()); + EXPECT_FALSE(shortcut_creator.CreateShortcuts()); } TEST(WebAppShortcutCreatorTest, UpdateIcon) { |