summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjackhou@chromium.org <jackhou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-07 14:50:13 +0000
committerjackhou@chromium.org <jackhou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-07 14:50:13 +0000
commitd6c779d8e1fc7a21c53a79c25da8399a65c716cd (patch)
tree30938c536905ba1a55f483eaa406b6a11503228a
parent3c3511199d11988807d01e968e3667268f08bef1 (diff)
downloadchromium_src-d6c779d8e1fc7a21c53a79c25da8399a65c716cd.zip
chromium_src-d6c779d8e1fc7a21c53a79c25da8399a65c716cd.tar.gz
chromium_src-d6c779d8e1fc7a21c53a79c25da8399a65c716cd.tar.bz2
Use unique names for app shim bundles based on profile dir and extension id.
Instead of the app's title, the app shim bundle's filename is set to: "<profile_dir> <extension_id>.app" A localization file is added to the bundle: Content/Resources/en.lproj/InfoPlist.string in which the app's display name is set to the actual title. Also check if there are multiple app shims for the same extension and append the profile name to the title if so. This allows: app shims for the same app but different profiles: - to have a consistent path without appending arbitrary numbers, - to be identifiable by the profile name, which can change. apps that have the same title - to display their actual title without appending arbitrary numbers. BUG=168080 Review URL: https://chromiumcodereview.appspot.com/16301002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204836 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/shell_integration.h1
-rw-r--r--chrome/browser/ui/web_applications/web_app_ui.cc4
-rw-r--r--chrome/browser/web_applications/web_app_mac.h4
-rw-r--r--chrome/browser/web_applications/web_app_mac.mm97
-rw-r--r--chrome/browser/web_applications/web_app_mac_unittest.mm30
-rw-r--r--chrome/common/mac/app_mode_common.h12
-rw-r--r--chrome/common/mac/app_mode_common.mm3
7 files changed, 106 insertions, 45 deletions
diff --git a/chrome/browser/shell_integration.h b/chrome/browser/shell_integration.h
index 1306027..f781b00 100644
--- a/chrome/browser/shell_integration.h
+++ b/chrome/browser/shell_integration.h
@@ -103,6 +103,7 @@ class ShellIntegration {
base::FilePath extension_path;
gfx::ImageFamily favicon;
base::FilePath profile_path;
+ std::string profile_name;
};
// Info about which locations to create app shortcuts in.
diff --git a/chrome/browser/ui/web_applications/web_app_ui.cc b/chrome/browser/ui/web_applications/web_app_ui.cc
index b807bc7..c185bfd 100644
--- a/chrome/browser/ui/web_applications/web_app_ui.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui.cc
@@ -8,6 +8,7 @@
#include "base/bind_helpers.h"
#include "base/file_util.h"
#include "base/path_service.h"
+#include "base/prefs/pref_service.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/image_loader.h"
@@ -20,6 +21,7 @@
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
+#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_registrar.h"
@@ -423,6 +425,8 @@ void UpdateShortcutInfoForApp(const extensions::Extension& app,
shortcut_info->description = UTF8ToUTF16(app.description());
shortcut_info->extension_path = app.path();
shortcut_info->profile_path = profile->GetPath();
+ shortcut_info->profile_name =
+ profile->GetPrefs()->GetString(prefs::kProfileName);
}
void UpdateShortcutInfoAndIconForApp(
diff --git a/chrome/browser/web_applications/web_app_mac.h b/chrome/browser/web_applications/web_app_mac.h
index 91d46d7..d20aa22 100644
--- a/chrome/browser/web_applications/web_app_mac.h
+++ b/chrome/browser/web_applications/web_app_mac.h
@@ -65,6 +65,10 @@ class WebAppShortcutCreator {
private:
FRIEND_TEST_ALL_PREFIXES(WebAppShortcutCreatorTest, UpdateIcon);
+ // Updates the InfoPlist.string inside |app_path| with the display name for
+ // the app.
+ bool UpdateDisplayName(const base::FilePath& app_path) const;
+
// Path to the app's user data directory. For example:
// ~/Library/Application Support/Chromium/Default/Web Applications/_crx_abc/
// Note, the user data directory is the parent of the profile directory.
diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm
index a884e84..64caabd 100644
--- a/chrome/browser/web_applications/web_app_mac.mm
+++ b/chrome/browser/web_applications/web_app_mac.mm
@@ -18,6 +18,7 @@
#include "base/mac/scoped_cftyperef.h"
#include "base/memory/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
+#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/ui/web_applications/web_app_ui.h"
#include "chrome/browser/web_applications/web_app.h"
@@ -132,6 +133,31 @@ base::FilePath GetWritableApplicationsDirectory() {
return base::FilePath();
}
+// Given the path to an app bundle, return the resources directory.
+base::FilePath GetResourcesPath(const base::FilePath& app_path) {
+ return app_path.Append("Contents").Append("Resources");
+}
+
+bool HasExistingExtensionShim(const base::FilePath& destination_directory,
+ const std::string& extension_id,
+ const base::FilePath& own_basename) {
+ // Check if there any any other shims for the same extension.
+ file_util::FileEnumerator enumerator(destination_directory,
+ false /* recursive */,
+ file_util::FileEnumerator::DIRECTORIES);
+ for (base::FilePath shim_path = enumerator.Next();
+ !shim_path.empty(); shim_path = enumerator.Next()) {
+ if (shim_path.BaseName() != own_basename &&
+ EndsWith(shim_path.RemoveExtension().value(),
+ extension_id,
+ true /* case_sensitive */)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
} // namespace
namespace web_app {
@@ -155,15 +181,16 @@ base::FilePath WebAppShortcutCreator::GetShortcutPath() const {
if (dst_path.empty())
return dst_path;
- base::FilePath app_name = internals::GetSanitizedFileName(info_.title);
+ base::FilePath app_name = internals::GetSanitizedFileName(UTF8ToUTF16(
+ info_.profile_path.BaseName().value() + " " + info_.extension_id));
return dst_path.Append(app_name.ReplaceExtension("app"));
}
bool WebAppShortcutCreator::CreateShortcut() {
- base::FilePath app_name = internals::GetSanitizedFileName(info_.title);
- base::FilePath app_file_name = app_name.ReplaceExtension("app");
- base::FilePath dst_path = GetDestinationPath();
- if (dst_path.empty() || !file_util::DirectoryExists(dst_path.DirName())) {
+ 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())) {
LOG(ERROR) << "Couldn't find an Applications directory to copy app to.";
return false;
}
@@ -172,19 +199,10 @@ bool WebAppShortcutCreator::CreateShortcut() {
return false;
}
- base::FilePath app_path = dst_path.Append(app_file_name);
- app_path = file_util::MakeUniqueDirectory(app_path);
- if (app_path.empty()) {
- LOG(ERROR) << "Couldn't create a unique directory for app path: "
- << app_path.value();
- return false;
- }
- app_file_name = app_path.BaseName();
-
base::ScopedTempDir scoped_temp_dir;
if (!scoped_temp_dir.CreateUniqueTempDir())
return false;
- base::FilePath staging_path = scoped_temp_dir.path().Append(app_file_name);
+ 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.
@@ -197,6 +215,9 @@ bool WebAppShortcutCreator::CreateShortcut() {
if (!UpdatePlist(staging_path))
return false;
+ if (!UpdateDisplayName(staging_path))
+ return false;
+
if (!UpdateIcon(staging_path))
return false;
@@ -224,9 +245,6 @@ base::FilePath WebAppShortcutCreator::GetDestinationPath() const {
}
bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const {
- NSString* plist_path = base::mac::FilePathToNSString(
- app_path.Append("Contents").Append("Info.plist"));
-
NSString* extension_id = base::SysUTF8ToNSString(info_.extension_id);
NSString* extension_title = base::SysUTF16ToNSString(info_.title);
NSString* extension_url = base::SysUTF8ToNSString(info_.url.spec());
@@ -239,6 +257,8 @@ bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const {
chrome_bundle_id, app_mode::kShortcutBrowserBundleIDPlaceholder,
nil];
+ NSString* plist_path = base::mac::FilePathToNSString(
+ app_path.Append("Contents").Append("Info.plist"));
NSMutableDictionary* plist =
[NSMutableDictionary dictionaryWithContentsOfFile:plist_path];
NSArray* keys = [plist allKeys];
@@ -265,9 +285,47 @@ bool WebAppShortcutCreator::UpdatePlist(const base::FilePath& app_path) const {
forKey:app_mode::kCrAppModeUserDataDirKey];
[plist setObject:base::mac::FilePathToNSString(info_.profile_path.BaseName())
forKey:app_mode::kCrAppModeProfileDirKey];
+ [plist setObject:base::SysUTF8ToNSString(info_.profile_name)
+ forKey:app_mode::kCrAppModeProfileNameKey];
+ [plist setObject:[NSNumber numberWithBool:YES]
+ forKey:app_mode::kLSHasLocalizedDisplayNameKey];
+
+ base::FilePath app_name = app_path.BaseName().RemoveExtension();
+ [plist setObject:base::mac::FilePathToNSString(app_name)
+ forKey:base::mac::CFToNSCast(kCFBundleNameKey)];
+
return [plist writeToFile:plist_path atomically:YES];
}
+bool WebAppShortcutCreator::UpdateDisplayName(
+ const base::FilePath& app_path) const {
+ // OSX searches for the best language in the order of preferred languages.
+ // Since we only have one localization directory, it will choose this one.
+ base::FilePath localized_dir = GetResourcesPath(app_path).Append("en.lproj");
+ if (!file_util::CreateDirectory(localized_dir))
+ return false;
+
+ NSString* bundle_name = base::SysUTF16ToNSString(info_.title);
+ NSString* display_name = base::SysUTF16ToNSString(info_.title);
+ if (HasExistingExtensionShim(GetDestinationPath(),
+ info_.extension_id,
+ app_path.BaseName())) {
+ display_name = [bundle_name
+ stringByAppendingString:base::SysUTF8ToNSString(
+ " (" + info_.profile_name + ")")];
+ }
+
+ NSDictionary* strings_plist = @{
+ base::mac::CFToNSCast(kCFBundleNameKey) : bundle_name,
+ app_mode::kCFBundleDisplayNameKey : display_name
+ };
+
+ NSString* localized_path = base::mac::FilePathToNSString(
+ localized_dir.Append("InfoPlist.strings"));
+ return [strings_plist writeToFile:localized_path
+ atomically:YES];
+}
+
bool WebAppShortcutCreator::UpdateIcon(const base::FilePath& app_path) const {
if (info_.favicon.empty())
return true;
@@ -290,8 +348,7 @@ bool WebAppShortcutCreator::UpdateIcon(const base::FilePath& app_path) const {
if (!image_added)
return false;
- base::FilePath resources_path =
- app_path.Append("Contents").Append("Resources");
+ base::FilePath resources_path = GetResourcesPath(app_path);
if (!file_util::CreateDirectory(resources_path))
return false;
diff --git a/chrome/browser/web_applications/web_app_mac_unittest.mm b/chrome/browser/web_applications/web_app_mac_unittest.mm
index 0c08366..653bbb5 100644
--- a/chrome/browser/web_applications/web_app_mac_unittest.mm
+++ b/chrome/browser/web_applications/web_app_mac_unittest.mm
@@ -50,6 +50,7 @@ ShellIntegration::ShortcutInfo GetShortcutInfo() {
info.title = ASCIIToUTF16("Shortcut Title");
info.url = GURL("http://example.com/");
info.profile_path = base::FilePath("Default");
+ info.profile_name = "profile name";
return info;
}
@@ -64,7 +65,8 @@ TEST(WebAppShortcutCreatorTest, CreateShortcut) {
ShellIntegration::ShortcutInfo info = GetShortcutInfo();
base::FilePath dst_folder = scoped_temp_dir.path();
- base::FilePath dst_path = dst_folder.Append(UTF16ToUTF8(info.title) + ".app");
+ base::FilePath dst_path = dst_folder.Append(
+ info.profile_path.value() + " " + info.extension_id + ".app");
NiceMock<WebAppShortcutCreatorMock> shortcut_creator(info);
EXPECT_CALL(shortcut_creator, GetDestinationPath())
@@ -103,7 +105,8 @@ TEST(WebAppShortcutCreatorTest, RunShortcut) {
ShellIntegration::ShortcutInfo info = GetShortcutInfo();
base::FilePath dst_folder = scoped_temp_dir.path();
- base::FilePath dst_path = dst_folder.Append(UTF16ToUTF8(info.title) + ".app");
+ base::FilePath dst_path = dst_folder.Append(
+ info.profile_path.value() + " " + info.extension_id + ".app");
NiceMock<WebAppShortcutCreatorMock> shortcut_creator(info);
EXPECT_CALL(shortcut_creator, GetDestinationPath())
@@ -132,29 +135,6 @@ TEST(WebAppShortcutCreatorTest, CreateFailure) {
EXPECT_FALSE(shortcut_creator.CreateShortcut());
}
-TEST(WebAppShortcutCreatorTest, CreateUnique) {
- base::ScopedTempDir scoped_temp_dir;
- EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
-
- ShellIntegration::ShortcutInfo info = GetShortcutInfo();
-
- base::FilePath dst_folder = scoped_temp_dir.path();
- base::FilePath dst_path = dst_folder.Append(UTF16ToUTF8(info.title) + ".app");
-
- file_util::CreateDirectory(dst_path);
- base::FilePath expected_app_path =
- dst_path.InsertBeforeExtensionASCII(" (1)");
-
- NiceMock<WebAppShortcutCreatorMock> shortcut_creator(info);
- EXPECT_CALL(shortcut_creator, GetDestinationPath())
- .WillRepeatedly(Return(dst_folder));
- EXPECT_CALL(shortcut_creator,
- RevealGeneratedBundleInFinder(expected_app_path));
-
- EXPECT_TRUE(shortcut_creator.CreateShortcut());
- EXPECT_TRUE(file_util::PathExists(expected_app_path));
-}
-
TEST(WebAppShortcutCreatorTest, UpdateIcon) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
diff --git a/chrome/common/mac/app_mode_common.h b/chrome/common/mac/app_mode_common.h
index 8bb114f..b9045a6 100644
--- a/chrome/common/mac/app_mode_common.h
+++ b/chrome/common/mac/app_mode_common.h
@@ -33,6 +33,15 @@ extern const char kAppListModeId[];
// associates the shim without launching the app.
extern const char kNoLaunchApp[];
+// The display name of the bundle as shown in Finder and the Dock. For localized
+// bundles, this overrides the bundle's file name.
+extern NSString* const kCFBundleDisplayNameKey;
+
+// The key specifying whether the display name should be localized. This makes
+// Finder look in localization folders in the app bundle for a display name.
+// (e.g. Content/Resources/en.lproj/)
+extern NSString* const kLSHasLocalizedDisplayNameKey;
+
// The key under which the browser's bundle ID will be stored in the
// app mode launcher bundle's Info.plist.
extern NSString* const kBrowserBundleIDKey;
@@ -52,6 +61,9 @@ extern NSString* const kCrAppModeUserDataDirKey;
// Key for the app's extension path.
extern NSString* const kCrAppModeProfileDirKey;
+// Key for the app's profile display name.
+extern NSString* const kCrAppModeProfileNameKey;
+
// When the Chrome browser is run, it stores its location in the defaults
// system using this key.
extern NSString* const kLastRunAppBundlePathPrefsKey;
diff --git a/chrome/common/mac/app_mode_common.mm b/chrome/common/mac/app_mode_common.mm
index ae5e2f7..f67eb2f 100644
--- a/chrome/common/mac/app_mode_common.mm
+++ b/chrome/common/mac/app_mode_common.mm
@@ -12,12 +12,15 @@ const char kAppListModeId[] = "app_list";
const char kNoLaunchApp[] = "no-launch-app";
+NSString* const kCFBundleDisplayNameKey = @"CFBundleDisplayName";
+NSString* const kLSHasLocalizedDisplayNameKey = @"LSHasLocalizedDisplayName";
NSString* const kBrowserBundleIDKey = @"CrBundleIdentifier";
NSString* const kCrAppModeShortcutIDKey = @"CrAppModeShortcutID";
NSString* const kCrAppModeShortcutNameKey = @"CrAppModeShortcutName";
NSString* const kCrAppModeShortcutURLKey = @"CrAppModeShortcutURL";
NSString* const kCrAppModeUserDataDirKey = @"CrAppModeUserDataDir";
NSString* const kCrAppModeProfileDirKey = @"CrAppModeProfileDir";
+NSString* const kCrAppModeProfileNameKey = @"CrAppModeProfileName";
NSString* const kLastRunAppBundlePathPrefsKey = @"LastRunAppBundlePath";