summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-06 15:53:20 +0000
committergab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-06 15:53:20 +0000
commit23baa52378747306f9ac9e366944df909b883f12 (patch)
treebfad6021995603cfeeeb8608aec19aa5eac7c5b1
parent9cf2a33be0f52eaf452aaa9eb9a59f2d92a1516a (diff)
downloadchromium_src-23baa52378747306f9ac9e366944df909b883f12.zip
chromium_src-23baa52378747306f9ac9e366944df909b883f12.tar.gz
chromium_src-23baa52378747306f9ac9e366944df909b883f12.tar.bz2
Use a better registration suffix that will always be unique while respecting the MSDN rules (2nd try).
The suffix will now be the base32 encoding of the md5 hash of the user's username. This is always unique and can be determined many times deterministically without I/O access (as opposed to other methods we considered). It respects all MSDN constraints for properties onto which it is appended. This replaces the prior-style username suffixes for new installs. Old-style suffixes (i.e. unsuffixed and username suffixed) will however be kept as is through future updates. Design doc: https://docs.google.com/a/chromium.org/document/d/1qmcV3uYBh3JwvXhYkI7asg0nN7KfVMWVOzND4p0jQ3E/edit Note: This is the continuation of http://codereview.chromium.org/10617002/ which was reverted in https://chromiumcodereview.appspot.com/10667006/ BUG=133810, 133173 TEST= http://goo.gl/ZZ7gE installer_util_unittests.exe --gtest_filter=ShellUtilTest.* Review URL: https://chromiumcodereview.appspot.com/10662052 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145596 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/installer/setup/uninstall.cc23
-rw-r--r--chrome/installer/util/shell_util.cc231
-rw-r--r--chrome/installer/util/shell_util.h34
-rw-r--r--chrome/installer/util/shell_util_unittest.cc51
4 files changed, 283 insertions, 56 deletions
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index e09be35..5f7578b 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -533,15 +533,9 @@ bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root,
// Delete Software\Classes\Chrome (Same comment as above applies for this too)
string16 chrome_app_id(ShellUtil::kRegClasses);
chrome_app_id.push_back(FilePath::kSeparators[0]);
- if (browser_entry_suffix.empty()) {
- // An unsuffixed appid used to be registered on some user-level install
- // (dev-channel 21.0.1171.0). Make sure it gets cleaned up here.
- // Note: this couldn't be cleaned on update as a currently running old
- // chrome might still be using the unsuffixed appid when the registration
- // update steps run.
- InstallUtil::DeleteRegistryKey(root, chrome_app_id + dist->GetBaseAppId());
- }
- chrome_app_id.append(ShellUtil::GetBrowserModelId(dist, chrome_exe.value()));
+ // Append the requested suffix manually here (as ShellUtil::GetBrowserModelId
+ // would otherwise try to figure out the currently installed suffix).
+ chrome_app_id.append(dist->GetBaseAppId() + browser_entry_suffix);
InstallUtil::DeleteRegistryKey(root, chrome_app_id);
// Delete all Start Menu Internet registrations that refer to this Chrome.
@@ -809,6 +803,17 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
if (!suffix.empty()) {
DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER, string16(),
installer_state.target_path(), &ret);
+
+ // For similar reasons it is possible in very few installs (from 21.0.1180.0
+ // and fixed shortly after) to be installed with the new-style suffix, but
+ // have some old-style suffix registrations left behind.
+ string16 old_style_suffix;
+ if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix) &&
+ suffix != old_style_suffix) {
+ DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER,
+ old_style_suffix,
+ installer_state.target_path(), &ret);
+ }
}
// Chrome is registered in HKLM for all system-level installs and for
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc
index 0d56569..ae69767 100644
--- a/chrome/installer/util/shell_util.cc
+++ b/chrome/installer/util/shell_util.cc
@@ -9,15 +9,18 @@
#include "chrome/installer/util/shell_util.h"
-#include <windows.h>
#include <shlobj.h>
+#include <windows.h>
+#include <limits>
#include <list>
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/stl_util.h"
@@ -28,6 +31,7 @@
#include "base/values.h"
#include "base/win/registry.h"
#include "base/win/scoped_comptr.h"
+#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
@@ -85,6 +89,80 @@ bool IsChromeMetroSupported() {
return VerifyVersionInfo(&min_version_info, type_mask, condition_mask) != 0;
}
+// Returns the current (or installed) browser's ProgId (e.g.
+// "ChromeHTML|suffix|").
+// |suffix| can be the empty string.
+string16 GetBrowserProgId(const string16& suffix) {
+ string16 chrome_html(ShellUtil::kChromeHTMLProgId);
+ chrome_html.append(suffix);
+
+ // ProgIds cannot be longer than 39 characters.
+ // Ref: http://msdn.microsoft.com/en-us/library/aa911706.aspx.
+ // Make all new registrations comply with this requirement (existing
+ // registrations must be preserved).
+ string16 new_style_suffix;
+ if (ShellUtil::GetUserSpecificRegistrySuffix(&new_style_suffix) &&
+ suffix == new_style_suffix && chrome_html.length() > 39) {
+ NOTREACHED();
+ chrome_html.erase(39);
+ }
+ return chrome_html;
+}
+
+// This class is used to initialize and cache a base 32 encoding of the md5 hash
+// of this user's sid preceded by a dot.
+// This is guaranteed to be unique on the machine and 27 characters long
+// (including the '.').
+// This is then meant to be used as a suffix on all registrations that may
+// conflict with another user-level Chrome install.
+class UserSpecificRegistrySuffix {
+ public:
+ // All the initialization is done in the constructor to be able to build the
+ // suffix in a thread-safe manner when used in conjunction with a
+ // LazyInstance.
+ UserSpecificRegistrySuffix();
+
+ // Sets |suffix| to the pre-computed suffix cached in this object.
+ // Returns true unless the initialization originally failed.
+ bool GetSuffix(string16* suffix);
+
+ private:
+ string16 suffix_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserSpecificRegistrySuffix);
+}; // class UserSpecificRegistrySuffix
+
+UserSpecificRegistrySuffix::UserSpecificRegistrySuffix() {
+ string16 user_sid;
+ if (!base::win::GetUserSidString(&user_sid)) {
+ NOTREACHED();
+ return;
+ }
+ COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_);
+ base::MD5Digest md5_digest;
+ base::MD5Sum(user_sid.c_str(), user_sid.length(), &md5_digest);
+ const string16 base32_md5(
+ ShellUtil::ByteArrayToBase32(md5_digest.a, arraysize(md5_digest.a)));
+ // The value returned by the base32 algorithm above must never change and
+ // must always be 26 characters long (i.e. if someone ever moves this to
+ // base and implements the full base32 algorithm (i.e. with appended '='
+ // signs in the output), they must provide a flag to allow this method to
+ // still request the output with no appended '=' signs).
+ DCHECK_EQ(base32_md5.length(), 26U);
+ suffix_.reserve(base32_md5.length() + 1);
+ suffix_.assign(1, L'.');
+ suffix_.append(base32_md5);
+}
+
+bool UserSpecificRegistrySuffix::GetSuffix(string16* suffix) {
+ if (suffix_.empty()) {
+ NOTREACHED();
+ return false;
+ }
+ suffix->assign(suffix_);
+ return true;
+}
+
// This class represents a single registry entry. The objective is to
// encapsulate all the registry entries required for registering Chrome at one
// place. This class can not be instantiated outside the class and the objects
@@ -182,8 +260,7 @@ class RegistryEntry {
// File association ProgId
string16 chrome_html_prog_id(ShellUtil::kRegClasses);
chrome_html_prog_id.push_back(FilePath::kSeparators[0]);
- chrome_html_prog_id.append(ShellUtil::kChromeHTMLProgId);
- chrome_html_prog_id.append(suffix);
+ chrome_html_prog_id.append(GetBrowserProgId(suffix));
entries->push_front(new RegistryEntry(
chrome_html_prog_id, ShellUtil::kChromeHTMLProgIdDesc));
entries->push_front(new RegistryEntry(
@@ -235,7 +312,7 @@ class RegistryEntry {
std::list<RegistryEntry*>* entries) {
entries->push_front(new RegistryEntry(
GetCapabilitiesKey(dist, suffix).append(L"\\URLAssociations"),
- protocol, string16(ShellUtil::kChromeHTMLProgId).append(suffix)));
+ protocol, GetBrowserProgId(suffix)));
return true;
}
@@ -296,8 +373,7 @@ class RegistryEntry {
entries->push_front(new RegistryEntry(capabilities + L"\\Startmenu",
L"StartMenuInternet", reg_app_name));
- string16 html_prog_id(ShellUtil::kChromeHTMLProgId);
- html_prog_id.append(suffix);
+ string16 html_prog_id(GetBrowserProgId(suffix));
for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
entries->push_front(new RegistryEntry(
capabilities + L"\\FileAssociations",
@@ -366,8 +442,7 @@ class RegistryEntry {
const string16& suffix,
std::list<RegistryEntry*>* entries) {
// File extension associations.
- string16 html_prog_id(ShellUtil::kChromeHTMLProgId);
- html_prog_id.append(suffix);
+ string16 html_prog_id(GetBrowserProgId(suffix));
for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
string16 ext_key(ShellUtil::kRegClasses);
ext_key.push_back(FilePath::kSeparators[0]);
@@ -678,8 +753,7 @@ void RemoveBadWindows8RegistrationIfNeeded(
// <root hkey>\Software\Classes\ChromiumHTML[.user]\shell\open\command
key = ShellUtil::kRegClasses;
key.push_back(FilePath::kSeparators[0]);
- key.append(ShellUtil::kChromeHTMLProgId);
- key.append(installation_suffix);
+ key.append(GetBrowserProgId(installation_suffix));
key.append(ShellUtil::kRegShellOpen);
InstallUtil::DeleteRegistryValue(root_key, key,
ShellUtil::kRegDelegateExecute);
@@ -748,28 +822,13 @@ bool QuickIsChromeRegistered(BrowserDistribution* dist,
return false;
}
-// Sets |suffix| to this user's username preceded by a dot. This suffix is then
-// meant to be added to all registration that may conflict with another
-// user-level Chrome install.
-// Returns true unless the OS call to retrieve the username fails.
-bool GetUserSpecificRegistrySuffix(string16* suffix) {
- wchar_t user_name[256];
- DWORD size = arraysize(user_name);
- if (::GetUserName(user_name, &size) == 0 || size < 1) {
- PLOG(DFATAL) << "GetUserName failed";
- return false;
- }
- suffix->reserve(size);
- suffix->assign(1, L'.');
- suffix->append(user_name, size - 1);
- return true;
-}
-
-// Sets |suffix| to the current user's username, preceded by a dot, on
-// user-level installs.
-// To support old-style user-level installs however, |suffix| is cleared if
-// the user currently owns the non-suffixed HKLM registrations.
-// |suffix| is also cleared on system-level installs.
+// Sets |suffix| to a 27 character string that is specific to this user on this
+// machine (on user-level installs only).
+// To support old-style user-level installs however, |suffix| is cleared if the
+// user currently owns the non-suffixed HKLM registrations.
+// |suffix| can also be set to the user's username if the current install is
+// suffixed as per the old-style registrations.
+// |suffix| is cleared on system-level installs.
// |suffix| should then be appended to all Chrome properties that may conflict
// with other Chrome user-level installs.
// Returns true unless one of the underlying calls fails.
@@ -783,9 +842,20 @@ bool GetInstallationSpecificSuffix(BrowserDistribution* dist,
// registered with no suffix.
suffix->clear();
return true;
- } else {
- return GetUserSpecificRegistrySuffix(suffix);
}
+
+ // Get the old suffix for the check below.
+ if (!ShellUtil::GetOldUserSpecificRegistrySuffix(suffix)) {
+ NOTREACHED();
+ return false;
+ }
+ if (QuickIsChromeRegistered(dist, chrome_exe, *suffix,
+ CONFIRM_SHELL_REGISTRATION)) {
+ // Username suffix for installs that are suffixed as per the old-style.
+ return true;
+ }
+
+ return ShellUtil::GetUserSpecificRegistrySuffix(suffix);
}
// Returns the root registry key (HKLM or HKCU) into which shell integration
@@ -1062,12 +1132,29 @@ void ShellUtil::GetRegisteredBrowsers(
string16 ShellUtil::GetCurrentInstallationSuffix(BrowserDistribution* dist,
const string16& chrome_exe) {
+ // This method is somewhat the opposite of GetInstallationSpecificSuffix().
+ // In this case we are not trying to determine the current suffix for the
+ // upcoming installation (i.e. not trying to stick to a currently bad
+ // registration style if one is present).
+ // Here we want to determine which suffix we should use at run-time.
+ // In order of preference, we prefer (for user-level installs):
+ // 1) Base 32 encoding of the md5 hash of the user's sid (new-style).
+ // 2) Username (old-style).
+ // 3) Unsuffixed (even worse).
string16 tested_suffix;
- if (!InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ||
- !GetUserSpecificRegistrySuffix(&tested_suffix) ||
- !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
+ if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) &&
+ (!GetUserSpecificRegistrySuffix(&tested_suffix) ||
+ !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
+ CONFIRM_PROGID_REGISTRATION)) &&
+ (!GetOldUserSpecificRegistrySuffix(&tested_suffix) ||
+ !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix,
+ CONFIRM_PROGID_REGISTRATION)) &&
+ !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix.erase(),
CONFIRM_PROGID_REGISTRATION)) {
- return string16();
+ // If Chrome is not registered under any of the possible suffixes (e.g.
+ // tests, Canary, etc.): use the new-style suffix at run-time.
+ if (!GetUserSpecificRegistrySuffix(&tested_suffix))
+ NOTREACHED();
}
return tested_suffix;
}
@@ -1522,3 +1609,71 @@ bool ShellUtil::UpdateChromeShortcut(BrowserDistribution* dist,
app_id.c_str(),
ConvertShellUtilShortcutOptionsToFileUtil(options));
}
+
+bool ShellUtil::GetUserSpecificRegistrySuffix(string16* suffix) {
+ // Use a thread-safe cache for the user's suffix.
+ static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance =
+ LAZY_INSTANCE_INITIALIZER;
+ return suffix_instance.Get().GetSuffix(suffix);
+}
+
+bool ShellUtil::GetOldUserSpecificRegistrySuffix(string16* suffix) {
+ wchar_t user_name[256];
+ DWORD size = arraysize(user_name);
+ if (::GetUserName(user_name, &size) == 0 || size < 1) {
+ NOTREACHED();
+ return false;
+ }
+ suffix->reserve(size);
+ suffix->assign(1, L'.');
+ suffix->append(user_name, size - 1);
+ return true;
+}
+
+string16 ShellUtil::ByteArrayToBase32(const uint8* bytes, size_t size) {
+ static const char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+
+ // Eliminate special cases first.
+ if (size == 0) {
+ return string16();
+ } else if (size == 1) {
+ string16 ret;
+ ret.push_back(kEncoding[(bytes[0] & 0xf8) >> 3]);
+ ret.push_back(kEncoding[(bytes[0] & 0x07) << 2]);
+ return ret;
+ } else if (size >= std::numeric_limits<size_t>::max() / 8) {
+ // If |size| is too big, the calculation of |encoded_length| below will
+ // overflow.
+ NOTREACHED();
+ return string16();
+ }
+
+ // Overestimate the number of bits in the string by 4 so that dividing by 5
+ // is the equivalent of rounding up the actual number of bits divided by 5.
+ const size_t encoded_length = (size * 8 + 4) / 5;
+
+ string16 ret;
+ ret.reserve(encoded_length);
+
+ // A bit stream which will be read from the left and appended to from the
+ // right as it's emptied.
+ uint16 bit_stream = (bytes[0] << 8) + bytes[1];
+ size_t next_byte_index = 2;
+ int free_bits = 0;
+ while (free_bits < 16) {
+ // Extract the 5 leftmost bits in the stream
+ ret.push_back(kEncoding[(bit_stream & 0xf800) >> 11]);
+ bit_stream <<= 5;
+ free_bits += 5;
+
+ // If there is enough room in the bit stream, inject another byte (if there
+ // are any left...).
+ if (free_bits >= 8 && next_byte_index < size) {
+ free_bits -= 8;
+ bit_stream += bytes[next_byte_index++] << free_bits;
+ }
+ }
+
+ DCHECK_EQ(ret.length(), encoded_length);
+ return ret;
+}
diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h
index 51536f4..17ac257 100644
--- a/chrome/installer/util/shell_util.h
+++ b/chrome/installer/util/shell_util.h
@@ -246,8 +246,8 @@ class ShellUtil {
const string16& chrome_exe);
// Returns the AppUserModelId for |dist|. This identifier is unconditionally
- // suffixed with the user id for user-level installs (in contrast to other
- // registration entries which are suffix as described in
+ // suffixed with a unique id for this user on user-level installs (in contrast
+ // to other registration entries which are suffixed as described in
// GetCurrentInstallationSuffix() above).
static string16 GetBrowserModelId(BrowserDistribution* dist,
const string16& chrome_exe);
@@ -408,6 +408,36 @@ class ShellUtil {
int icon_index,
uint32 options);
+ // Sets |suffix| to the base 32 encoding of the md5 hash of this user's sid
+ // preceded by a dot.
+ // This is guaranteed to be unique on the machine and 27 characters long
+ // (including the '.').
+ // This suffix is then meant to be added to all registration that may conflict
+ // with another user-level Chrome install.
+ // Note that prior to Chrome 21, the suffix registered used to be the user's
+ // username (see GetOldUserSpecificRegistrySuffix() below). We still honor old
+ // installs registered that way, but it was wrong because some of the
+ // characters allowed in a username are not allowed in a ProgId.
+ // Returns true unless the OS call to retrieve the username fails.
+ // NOTE: Only the installer should use this suffix directly. Other callers
+ // should call GetCurrentInstallationSuffix().
+ static bool GetUserSpecificRegistrySuffix(string16* suffix);
+
+ // Sets |suffix| to this user's username preceded by a dot. This suffix should
+ // only be used to support legacy installs that used this suffixing
+ // style.
+ // Returns true unless the OS call to retrieve the username fails.
+ // NOTE: Only the installer should use this suffix directly. Other callers
+ // should call GetCurrentInstallationSuffix().
+ static bool GetOldUserSpecificRegistrySuffix(string16* suffix);
+
+ // Returns the base32 encoding (using the [A-Z2-7] alphabet) of |bytes|.
+ // |size| is the length of |bytes|.
+ // Note: This method does not suffix the output with '=' signs as technically
+ // required by the base32 standard for inputs that aren't a multiple of 5
+ // bytes.
+ static string16 ByteArrayToBase32(const uint8* bytes, size_t size);
+
private:
DISALLOW_COPY_AND_ASSIGN(ShellUtil);
};
diff --git a/chrome/installer/util/shell_util_unittest.cc b/chrome/installer/util/shell_util_unittest.cc
index 516155d..3e08589 100644
--- a/chrome/installer/util/shell_util_unittest.cc
+++ b/chrome/installer/util/shell_util_unittest.cc
@@ -11,8 +11,10 @@
#include "base/file_util.h"
#include "base/path_service.h"
+#include "base/md5.h"
#include "base/scoped_temp_dir.h"
#include "base/string16.h"
+#include "base/string_util.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#include "chrome/installer/util/browser_distribution.h"
@@ -88,7 +90,7 @@ bool VerifyChromeShortcut(const std::wstring& exe_path,
return true;
}
-class ShellUtilTest : public testing::Test {
+class ShellUtilTestWithDirAndDist : public testing::Test {
protected:
virtual void SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -103,7 +105,7 @@ class ShellUtilTest : public testing::Test {
};
// Test that we can open archives successfully.
-TEST_F(ShellUtilTest, UpdateChromeShortcutTest) {
+TEST_F(ShellUtilTestWithDirAndDist, UpdateChromeShortcutTest) {
// Create an executable in test path by copying ourself to it.
wchar_t exe_full_path_str[MAX_PATH];
EXPECT_FALSE(::GetModuleFileName(NULL, exe_full_path_str, MAX_PATH) == 0);
@@ -170,7 +172,7 @@ TEST_F(ShellUtilTest, UpdateChromeShortcutTest) {
description2, 1));
}
-TEST_F(ShellUtilTest, CreateChromeDesktopShortcutTest) {
+TEST_F(ShellUtilTestWithDirAndDist, CreateChromeDesktopShortcutTest) {
// Run this test on Vista+ only if we are running elevated.
if (base::win::GetVersion() > base::win::VERSION_XP && !IsUserAnAdmin()) {
LOG(ERROR) << "Must be admin to run this test on Vista+";
@@ -363,14 +365,14 @@ TEST_F(ShellUtilTest, CreateChromeDesktopShortcutTest) {
profile_names));
}
-TEST_F(ShellUtilTest, BuildAppModelIdBasic) {
+TEST_F(ShellUtilTestWithDirAndDist, BuildAppModelIdBasic) {
std::vector<string16> components;
const string16 base_app_id(dist_->GetBaseAppId());
components.push_back(base_app_id);
ASSERT_EQ(base_app_id, ShellUtil::BuildAppModelId(components));
}
-TEST_F(ShellUtilTest, BuildAppModelIdManySmall) {
+TEST_F(ShellUtilTestWithDirAndDist, BuildAppModelIdManySmall) {
std::vector<string16> components;
const string16 suffixed_app_id(dist_->GetBaseAppId().append(L".gab"));
components.push_back(suffixed_app_id);
@@ -380,7 +382,7 @@ TEST_F(ShellUtilTest, BuildAppModelIdManySmall) {
ShellUtil::BuildAppModelId(components));
}
-TEST_F(ShellUtilTest, BuildAppModelIdLongUsernameNormalProfile) {
+TEST_F(ShellUtilTestWithDirAndDist, BuildAppModelIdLongUsernameNormalProfile) {
std::vector<string16> components;
const string16 long_appname(
L"Chrome.a_user_who_has_a_crazy_long_name_with_some_weird@symbols_in_it_"
@@ -391,7 +393,7 @@ TEST_F(ShellUtilTest, BuildAppModelIdLongUsernameNormalProfile) {
ShellUtil::BuildAppModelId(components));
}
-TEST_F(ShellUtilTest, BuildAppModelIdLongEverything) {
+TEST_F(ShellUtilTestWithDirAndDist, BuildAppModelIdLongEverything) {
std::vector<string16> components;
const string16 long_appname(
L"Chrome.a_user_who_has_a_crazy_long_name_with_some_weird@symbols_in_it_"
@@ -404,3 +406,38 @@ TEST_F(ShellUtilTest, BuildAppModelIdLongEverything) {
ASSERT_EQ(L"Chrome.a_user_wer_64_characters.A_crazy_profilethat_is_possible",
constructed_app_id);
}
+
+TEST(ShellUtilTest, GetUserSpecificRegistrySuffix) {
+ string16 suffix;
+ ASSERT_TRUE(ShellUtil::GetUserSpecificRegistrySuffix(&suffix));
+ ASSERT_TRUE(StartsWith(suffix, L".", true));
+ ASSERT_EQ(27, suffix.length());
+ ASSERT_TRUE(ContainsOnlyChars(suffix.substr(1),
+ L"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"));
+}
+
+TEST(ShellUtilTest, GetOldUserSpecificRegistrySuffix) {
+ string16 suffix;
+ ASSERT_TRUE(ShellUtil::GetOldUserSpecificRegistrySuffix(&suffix));
+ ASSERT_TRUE(StartsWith(suffix, L".", true));
+
+ wchar_t user_name[256];
+ DWORD size = arraysize(user_name);
+ ASSERT_NE(0, ::GetUserName(user_name, &size));
+ ASSERT_GE(size, 1U);
+ ASSERT_STREQ(user_name, suffix.substr(1).c_str());
+}
+
+TEST(ShellUtilTest, ByteArrayToBase32) {
+ // Tests from http://tools.ietf.org/html/rfc4648#section-10.
+ const unsigned char test_array[] = { 'f', 'o', 'o', 'b', 'a', 'r' };
+
+ const string16 expected[] = { L"", L"MY", L"MZXQ", L"MZXW6", L"MZXW6YQ",
+ L"MZXW6YTB", L"MZXW6YTBOI"};
+
+ // Run the tests, with one more letter in the input every pass.
+ for (int i = 0; i < arraysize(expected); ++i) {
+ ASSERT_EQ(expected[i],
+ ShellUtil::ByteArrayToBase32(test_array, i));
+ }
+}