Index: win8/delegate_execute/chrome_util.cc |
diff --git a/win8/delegate_execute/chrome_util.cc b/win8/delegate_execute/chrome_util.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..701f2e84dd82f76a4de0f1525acbafc205201ba0 |
--- /dev/null |
+++ b/win8/delegate_execute/chrome_util.cc |
@@ -0,0 +1,267 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "win8/delegate_execute/chrome_util.h" |
+ |
+#include <atlbase.h> |
+#include <shlobj.h> |
+#include <windows.h> |
+ |
+#include <algorithm> |
+#include <limits> |
+#include <string> |
+ |
+#include "base/file_path.h" |
+#include "base/file_util.h" |
+#include "base/md5.h" |
+#include "base/process_util.h" |
+#include "base/string_util.h" |
+#include "base/utf_string_conversions.h" |
+#include "base/win/registry.h" |
+#include "base/win/scoped_comptr.h" |
+#include "base/win/scoped_handle.h" |
+#include "base/win/win_util.h" |
+#include "google_update/google_update_idl.h" |
+ |
+namespace { |
+ |
+#if defined(GOOGLE_CHROME_BUILD) |
+const wchar_t kAppUserModelId[] = L"Chrome"; |
+#else // GOOGLE_CHROME_BUILD |
+const wchar_t kAppUserModelId[] = L"Chromium"; |
+#endif // GOOGLE_CHROME_BUILD |
+ |
+#if defined(GOOGLE_CHROME_BUILD) |
+ |
+// TODO(grt): These constants live in installer_util. Consider moving them |
+// into common_constants to allow for reuse. |
+const FilePath::CharType kNewChromeExe[] = FILE_PATH_LITERAL("new_chrome.exe"); |
+const wchar_t kRenameCommandValue[] = L"cmd"; |
+const wchar_t kChromeAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; |
+const wchar_t kRegPathChromeClient[] = |
+ L"Software\\Google\\Update\\Clients\\" |
+ L"{8A69D345-D564-463c-AFF1-A69D9E530F96}"; |
+const int kExitCodeRenameSuccessful = 23; |
+ |
+// Returns the name of the global event used to detect if |chrome_exe| is in |
+// use by a browser process. |
+// TODO(grt): Move this somewhere central so it can be used by both this |
+// IsBrowserRunning (below) and IsBrowserAlreadyRunning (browser_util_win.cc). |
+string16 GetEventName(const FilePath& chrome_exe) { |
+ static wchar_t const kEventPrefix[] = L"Global\\"; |
+ const size_t prefix_len = arraysize(kEventPrefix) - 1; |
+ string16 name; |
+ name.reserve(prefix_len + chrome_exe.value().size()); |
+ name.assign(kEventPrefix, prefix_len); |
+ name.append(chrome_exe.value()); |
+ std::replace(name.begin() + prefix_len, name.end(), '\\', '!'); |
+ std::transform(name.begin() + prefix_len, name.end(), |
+ name.begin() + prefix_len, tolower); |
+ return name; |
+} |
+ |
+// Returns true if |chrome_exe| is in use by a browser process. In this case, |
+// "in use" means past ChromeBrowserMainParts::PreMainMessageLoopRunImpl. |
+bool IsBrowserRunning(const FilePath& chrome_exe) { |
+ base::win::ScopedHandle handle(::OpenEvent( |
+ SYNCHRONIZE, FALSE, GetEventName(chrome_exe).c_str())); |
+ if (handle.IsValid()) |
+ return true; |
+ DWORD last_error = ::GetLastError(); |
+ if (last_error != ERROR_FILE_NOT_FOUND) { |
+ AtlTrace("%hs. Failed to open browser event; error %u.\n", __FUNCTION__, |
+ last_error); |
+ } |
+ return false; |
+} |
+ |
+// Returns true if the file new_chrome.exe exists in the same directory as |
+// |chrome_exe|. |
+bool NewChromeExeExists(const FilePath& chrome_exe) { |
+ FilePath new_chrome_exe(chrome_exe.DirName().Append(kNewChromeExe)); |
+ return file_util::PathExists(new_chrome_exe); |
+} |
+ |
+bool GetUpdateCommand(bool is_per_user, string16* update_command) { |
+ const HKEY root = is_per_user ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; |
+ base::win::RegKey key(root, kRegPathChromeClient, KEY_QUERY_VALUE); |
+ |
+ return key.ReadValue(kRenameCommandValue, update_command) == ERROR_SUCCESS; |
+} |
+ |
+#endif // GOOGLE_CHROME_BUILD |
+ |
+// TODO(grt): This code also lives in installer_util. Refactor for reuse. |
+bool IsPerUserInstall(const FilePath& chrome_exe) { |
+ wchar_t program_files_path[MAX_PATH] = {0}; |
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, |
+ SHGFP_TYPE_CURRENT, program_files_path))) { |
+ return !StartsWith(chrome_exe.value().c_str(), program_files_path, false); |
+ } else { |
+ NOTREACHED(); |
+ } |
+ return true; |
+} |
+ |
+// TODO(gab): This code also lives in shell_util. Refactor for reuse. |
+string16 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. |
+ AtlTrace("%hs. Byte array is too long.\n", __FUNCTION__); |
+ 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; |
+ } |
+ } |
+ |
+ if (ret.length() != encoded_length) { |
+ AtlTrace("%hs. Encoding doesn't match expected length.\n", __FUNCTION__); |
+ return string16(); |
+ } |
+ return ret; |
+} |
+ |
+// TODO(gab): This code also lives in shell_util. Refactor for reuse. |
+bool GetUserSpecificRegistrySuffix(string16* suffix) { |
+ string16 user_sid; |
+ if (!base::win::GetUserSidString(&user_sid)) { |
+ AtlTrace("%hs. GetUserSidString failed.\n", __FUNCTION__); |
+ return false; |
+ } |
+ COMPILE_ASSERT(sizeof(base::MD5Digest) == 16, size_of_MD5_not_as_expected_); |
+ base::MD5Digest md5_digest; |
+ std::string user_sid_ascii(UTF16ToASCII(user_sid)); |
+ base::MD5Sum(user_sid_ascii.c_str(), user_sid_ascii.length(), &md5_digest); |
+ const string16 base32_md5( |
+ 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). |
+ if (base32_md5.length() != 26U) { |
+ AtlTrace("%hs. Base32 encoding of md5 hash is incorrect.\n", __FUNCTION__); |
+ return false; |
+ } |
+ suffix->reserve(base32_md5.length() + 1); |
+ suffix->assign(1, L'.'); |
+ suffix->append(base32_md5); |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+namespace delegate_execute { |
+ |
+void UpdateChromeIfNeeded(const FilePath& chrome_exe) { |
+#if defined(GOOGLE_CHROME_BUILD) |
+ // Nothing to do if a browser is already running or if there's no |
+ // new_chrome.exe. |
+ if (IsBrowserRunning(chrome_exe) || !NewChromeExeExists(chrome_exe)) |
+ return; |
+ |
+ base::ProcessHandle process_handle = base::kNullProcessHandle; |
+ |
+ if (IsPerUserInstall(chrome_exe)) { |
+ // Read the update command from the registry. |
+ string16 update_command; |
+ if (!GetUpdateCommand(true, &update_command)) { |
+ AtlTrace("%hs. Failed to read update command from registry.\n", |
+ __FUNCTION__); |
+ } else { |
+ // Run the update command. |
+ base::LaunchOptions launch_options; |
+ launch_options.start_hidden = true; |
+ if (!base::LaunchProcess(update_command, launch_options, |
+ &process_handle)) { |
+ AtlTrace("%hs. Failed to launch command to finalize update; " |
+ "error %u.\n", __FUNCTION__, ::GetLastError()); |
+ process_handle = base::kNullProcessHandle; |
+ } |
+ } |
+ } else { |
+ // Run the update command via Google Update. |
+ HRESULT hr = S_OK; |
+ base::win::ScopedComPtr<IProcessLauncher> process_launcher; |
+ hr = process_launcher.CreateInstance(__uuidof(ProcessLauncherClass)); |
+ if (FAILED(hr)) { |
+ AtlTrace("%hs. Failed to Create ProcessLauncher; hr=0x%X.\n", |
+ __FUNCTION__, hr); |
+ } else { |
+ ULONG_PTR handle = 0; |
+ hr = process_launcher->LaunchCmdElevated( |
+ kChromeAppGuid, kRenameCommandValue, GetCurrentProcessId(), &handle); |
+ if (FAILED(hr)) { |
+ AtlTrace("%hs. Failed to launch command to finalize update; " |
+ "hr=0x%X.\n", __FUNCTION__, hr); |
+ } else { |
+ process_handle = reinterpret_cast<base::ProcessHandle>(handle); |
+ } |
+ } |
+ } |
+ |
+ // Wait for the update to complete and report the results. |
+ if (process_handle != base::kNullProcessHandle) { |
+ int exit_code = 0; |
+ // WaitForExitCode will close the handle in all cases. |
+ if (!base::WaitForExitCode(process_handle, &exit_code)) { |
+ AtlTrace("%hs. Failed to get result when finalizing update.\n", |
+ __FUNCTION__); |
+ } else if (exit_code != kExitCodeRenameSuccessful) { |
+ AtlTrace("%hs. Failed to finalize update with exit code %d.\n", |
+ __FUNCTION__, exit_code); |
+ } else { |
+ AtlTrace("%hs. Finalized pending update.\n", __FUNCTION__); |
+ } |
+ } |
+#endif |
+} |
+ |
+// TODO(gab): This code also lives in shell_util. Refactor for reuse. |
+string16 GetAppId(const FilePath& chrome_exe) { |
+ string16 app_id(kAppUserModelId); |
+ string16 suffix; |
+ if (IsPerUserInstall(chrome_exe) && |
+ !GetUserSpecificRegistrySuffix(&suffix)) { |
+ AtlTrace("%hs. GetUserSpecificRegistrySuffix failed.\n", |
+ __FUNCTION__); |
+ } |
+ return app_id.append(suffix); |
+} |
+ |
+} // delegate_execute |