summaryrefslogtreecommitdiffstats
path: root/chrome_frame/utils.cc
diff options
context:
space:
mode:
authorslightlyoff@chromium.org <slightlyoff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 05:11:58 +0000
committerslightlyoff@chromium.org <slightlyoff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 05:11:58 +0000
commitf781782dd67077478e117c61dca4ea5eefce3544 (patch)
tree4801f724123cfdcbb69c4e7fe40a565b331723ae /chrome_frame/utils.cc
parent63cf4759efa2373e33436fb5df6849f930081226 (diff)
downloadchromium_src-f781782dd67077478e117c61dca4ea5eefce3544.zip
chromium_src-f781782dd67077478e117c61dca4ea5eefce3544.tar.gz
chromium_src-f781782dd67077478e117c61dca4ea5eefce3544.tar.bz2
Initial import of the Chrome Frame codebase. Integration in chrome.gyp coming in a separate CL.
BUG=None TEST=None Review URL: http://codereview.chromium.org/218019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27042 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/utils.cc')
-rw-r--r--chrome_frame/utils.cc643
1 files changed, 643 insertions, 0 deletions
diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc
new file mode 100644
index 0000000..b445c26
--- /dev/null
+++ b/chrome_frame/utils.cc
@@ -0,0 +1,643 @@
+// Copyright (c) 2009 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 <shlobj.h>
+
+#include "chrome_frame/html_utils.h"
+#include "chrome_frame/utils.h"
+
+#include "base/file_util.h"
+#include "base/file_version_info.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/registry.h"
+#include "base/scoped_comptr_win.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/installer/util/google_update_constants.h"
+#include "googleurl/src/gurl.h"
+#include "grit/chrome_frame_resources.h"
+#include "chrome_frame/resource.h"
+
+// Note that these values are all lower case and are compared to
+// lower-case-transformed values.
+const wchar_t kMetaTag[] = L"meta";
+const wchar_t kHttpEquivAttribName[] = L"http-equiv";
+const wchar_t kContentAttribName[] = L"content";
+const wchar_t kXUACompatValue[] = L"x-ua-compatible";
+const wchar_t kBodyTag[] = L"body";
+const wchar_t kChromeContentPrefix[] = L"chrome=";
+const wchar_t kChromeProtocolPrefix[] = L"cf:";
+
+static const wchar_t kChromeFrameConfigKey[] =
+ L"Software\\Google\\ChromeFrame";
+static const wchar_t kChromeFrameOptinUrlsKey[] = L"OptinUrls";
+
+// Used to isolate chrome frame builds from google chrome release channels.
+const wchar_t kChromeFrameOmahaSuffix[] = L"-cf";
+const wchar_t kDevChannelName[] = L"-dev";
+
+const wchar_t kChromeAttachExternalTabPrefix[] = L"attach_external_tab";
+
+HRESULT UtilRegisterTypeLib(HINSTANCE tlb_instance,
+ LPCOLESTR index,
+ bool for_current_user_only) {
+ CComBSTR path;
+ CComPtr<ITypeLib> type_lib;
+ HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib);
+ if (SUCCEEDED(hr)) {
+ hr = UtilRegisterTypeLib(type_lib, path, NULL, for_current_user_only);
+ }
+ return hr;
+}
+
+HRESULT UtilUnRegisterTypeLib(HINSTANCE tlb_instance,
+ LPCOLESTR index,
+ bool for_current_user_only) {
+ CComBSTR path;
+ CComPtr<ITypeLib> type_lib;
+ HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib);
+ if (SUCCEEDED(hr)) {
+ hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only);
+ }
+ return hr;
+}
+
+HRESULT UtilRegisterTypeLib(LPCWSTR typelib_path,
+ bool for_current_user_only) {
+ if (NULL == typelib_path) {
+ return E_INVALIDARG;
+ }
+ CComBSTR path;
+ CComPtr<ITypeLib> type_lib;
+ HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib);
+ if (SUCCEEDED(hr)) {
+ hr = UtilRegisterTypeLib(type_lib,
+ typelib_path,
+ NULL,
+ for_current_user_only);
+ }
+ return hr;
+}
+
+HRESULT UtilUnRegisterTypeLib(LPCWSTR typelib_path,
+ bool for_current_user_only) {
+ CComPtr<ITypeLib> type_lib;
+ HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib);
+ if (SUCCEEDED(hr)) {
+ hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only);
+ }
+ return hr;
+}
+
+HRESULT UtilRegisterTypeLib(ITypeLib* typelib,
+ LPCWSTR typelib_path,
+ LPCWSTR help_dir,
+ bool for_current_user_only) {
+ typedef HRESULT(WINAPI *RegisterTypeLibPrototype)(ITypeLib FAR* type_lib,
+ OLECHAR FAR* full_path,
+ OLECHAR FAR* help_dir);
+ LPCSTR function_name =
+ for_current_user_only ? "RegisterTypeLibForUser" : "RegisterTypeLib";
+ RegisterTypeLibPrototype reg_tlb =
+ reinterpret_cast<RegisterTypeLibPrototype>(
+ GetProcAddress(GetModuleHandle(_T("oleaut32.dll")),
+ function_name));
+ if (NULL == reg_tlb) {
+ return E_FAIL;
+ }
+ return reg_tlb(typelib,
+ const_cast<OLECHAR*>(typelib_path),
+ const_cast<OLECHAR*>(help_dir));
+}
+
+HRESULT UtilUnRegisterTypeLib(ITypeLib* typelib,
+ bool for_current_user_only) {
+ if (NULL == typelib) {
+ return E_INVALIDARG;
+ }
+ typedef HRESULT(WINAPI *UnRegisterTypeLibPrototype)(
+ REFGUID libID,
+ unsigned short wVerMajor, // NOLINT
+ unsigned short wVerMinor, // NOLINT
+ LCID lcid,
+ SYSKIND syskind);
+ LPCSTR function_name =
+ for_current_user_only ? "UnRegisterTypeLibForUser" : "UnRegisterTypeLib";
+
+ UnRegisterTypeLibPrototype unreg_tlb =
+ reinterpret_cast<UnRegisterTypeLibPrototype>(
+ GetProcAddress(GetModuleHandle(_T("oleaut32.dll")),
+ function_name));
+ if (NULL == unreg_tlb) {
+ return E_FAIL;
+ }
+ TLIBATTR* tla = NULL;
+ HRESULT hr = typelib->GetLibAttr(&tla);
+ if (SUCCEEDED(hr)) {
+ hr = unreg_tlb(tla->guid,
+ tla->wMajorVerNum,
+ tla->wMinorVerNum,
+ tla->lcid,
+ tla->syskind);
+ typelib->ReleaseTLibAttr(tla);
+ }
+ return hr;
+}
+
+HRESULT UtilGetXUACompatContentValue(const std::wstring& html_string,
+ std::wstring* content_value) {
+ if (!content_value) {
+ return E_POINTER;
+ }
+
+ // Fail fast if the string X-UA-Compatible isn't in html_string
+ if (StringToLowerASCII(html_string).find(kXUACompatValue) ==
+ std::wstring::npos) {
+ return E_FAIL;
+ }
+
+ HTMLScanner scanner(html_string.c_str());
+
+ // Build the list of meta tags that occur before the body tag is hit.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(kMetaTag, &tag_list, kBodyTag);
+
+ // Search the list of meta tags for one with an http-equiv="X-UA-Compatible"
+ // attribute.
+ HTMLScanner::StringRange attribute;
+ std::string search_attribute_ascii(WideToASCII(kXUACompatValue));
+ HTMLScanner::StringRangeList::const_iterator tag_list_iter(tag_list.begin());
+ for (; tag_list_iter != tag_list.end(); tag_list_iter++) {
+ if (!tag_list_iter->GetTagAttribute(kHttpEquivAttribName, &attribute)) {
+ continue;
+ }
+
+ // We found an http-equiv meta tag, check its value using the ascii
+ // case-insensitive comparison method.
+ if (!attribute.LowerCaseEqualsASCII(search_attribute_ascii.c_str())) {
+ continue;
+ }
+
+ // We found our X-UA-Compatible meta tag so look for and extract
+ // the value of the content attribute.
+ if (!tag_list_iter->GetTagAttribute(kContentAttribName, &attribute)) {
+ continue;
+ }
+
+ // Found the content string, copy and return.
+ content_value->assign(attribute.Copy());
+ return S_OK;
+ }
+
+ return E_FAIL;
+}
+
+bool AppendSuffixToChannelName(std::wstring* string,
+ const std::wstring& channel_name,
+ const std::wstring& suffix) {
+ size_t pos = string->find(channel_name);
+ // Append the suffix only if we find the channel name.
+ if (pos != std::wstring::npos) {
+ pos += channel_name.size();
+ // Append the suffix only to the channel name only if the name is not
+ // already followed by suffix.
+ if (string->find(suffix, pos) != pos) {
+ string->insert(pos, suffix);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RemoveSuffixFromChannelName(std::wstring* string,
+ const std::wstring& channel_name,
+ const std::wstring& suffix) {
+ std::wstring decorated_channel(channel_name + suffix);
+ size_t pos = string->find(decorated_channel);
+ // TODO(robertshield): Remove the suffix iff the suffix is the last thing in
+ // the string or is followed by another suffix that starts with '-'.
+ if (pos != std::wstring::npos) {
+ pos += channel_name.size();
+ string->erase(pos, suffix.size());
+ return true;
+ }
+ return false;
+}
+
+HRESULT UtilUpdateOmahaConfig(bool add_cf_suffix) {
+ HKEY reg_root = HKEY_LOCAL_MACHINE;
+
+ RegKey key;
+ std::wstring ap_key_value;
+ std::wstring reg_key(google_update::kRegPathClientState);
+ reg_key.append(L"\\");
+ reg_key.append(google_update::kChromeGuid);
+ if (!key.Open(reg_root, reg_key.c_str(), KEY_READ | KEY_WRITE) ||
+ !key.ReadValue(google_update::kRegApField, &ap_key_value)) {
+ // Can't read the Omaha config.
+ return REGDB_E_READREGDB;
+ }
+
+ HRESULT result = S_OK;
+ // We've read the key in, try and modify it then write it back.
+ if (add_cf_suffix && AppendSuffixToChannelName(&ap_key_value,
+ kDevChannelName,
+ kChromeFrameOmahaSuffix)) {
+ if (!key.WriteValue(google_update::kRegApField, ap_key_value.c_str())) {
+ DLOG(ERROR) << "Failed to add suffix to omaha ap key value.";
+ result = REGDB_E_WRITEREGDB;
+ }
+ } else if (!add_cf_suffix &&
+ RemoveSuffixFromChannelName(&ap_key_value,
+ kDevChannelName,
+ kChromeFrameOmahaSuffix)) {
+ if (!key.WriteValue(google_update::kRegApField, ap_key_value.c_str())) {
+ DLOG(ERROR) << "Failed to remove suffix from omaha ap key value.";
+ result = REGDB_E_WRITEREGDB;
+ }
+ } else {
+ // Getting here means that no modifications needed to be made.
+ result = S_FALSE;
+ }
+
+ return result;
+}
+
+std::wstring GetResourceString(int resource_id) {
+ std::wstring resource_string;
+ HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
+ const ATLSTRINGRESOURCEIMAGE* image = AtlGetStringResourceImage(
+ this_module, resource_id);
+ if (image) {
+ resource_string.assign(image->achString, image->nLength);
+ } else {
+ NOTREACHED() << "Unable to find resource id " << resource_id;
+ }
+ return resource_string;
+}
+
+void DisplayVersionMismatchWarning(HWND parent,
+ const std::string& server_version) {
+ // Obtain the current module version.
+ FileVersionInfo* file_version_info =
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule();
+ DCHECK(file_version_info);
+ std::wstring version_string(file_version_info->file_version());
+ std::wstring wide_server_version;
+ if (server_version.empty()) {
+ wide_server_version = GetResourceString(IDS_VERSIONUNKNOWN);
+ } else {
+ wide_server_version = ASCIIToWide(server_version);
+ }
+ std::wstring title = GetResourceString(IDS_VERSIONMISMATCH_HEADER);
+ std::wstring message;
+ SStringPrintf(&message, GetResourceString(IDS_VERSIONMISMATCH).c_str(),
+ wide_server_version.c_str(), version_string.c_str());
+
+ ::MessageBox(parent, message.c_str(), title.c_str(), MB_OK);
+}
+
+std::string CreateJavascript(const std::string& function_name,
+ const std::string args) {
+ std::string script_string = "javascript:";
+ script_string += function_name + "(";
+ if (!args.empty()) {
+ script_string += "'";
+ script_string += args;
+ script_string += "'";
+ }
+ script_string += ")";
+ return script_string;
+}
+
+AddRefModule::AddRefModule() {
+ // TODO(tommi): Override the module's Lock/Unlock methods to call
+ // npapi::SetValue(NPPVpluginKeepLibraryInMemory) and keep the dll loaded
+ // while the module's refcount is > 0. Only do this when we're being
+ // used as an NPAPI module.
+ _pAtlModule->Lock();
+}
+
+
+AddRefModule::~AddRefModule() {
+ _pAtlModule->Unlock();
+}
+
+namespace {
+const char kIEImageName[] = "iexplore.exe";
+const char kFirefoxImageName[] = "firefox.exe";
+const char kOperaImageName[] = "opera.exe";
+} // namespace
+
+std::wstring GetHostProcessName(bool include_extension) {
+ FilePath exe;
+ if (PathService::Get(base::FILE_EXE, &exe))
+ exe = exe.BaseName();
+ if (!include_extension) {
+ exe = exe.RemoveExtension();
+ }
+ return exe.ToWStringHack();
+}
+
+BrowserType GetBrowserType() {
+ static BrowserType browser_type = BROWSER_INVALID;
+
+ if (browser_type == BROWSER_INVALID) {
+ std::wstring exe(GetHostProcessName(true));
+ if (!exe.empty()) {
+ std::wstring::const_iterator begin = exe.begin();
+ std::wstring::const_iterator end = exe.end();
+ if (LowerCaseEqualsASCII(begin, end, kIEImageName)) {
+ browser_type = BROWSER_IE;
+ } else if (LowerCaseEqualsASCII(begin, end, kFirefoxImageName)) {
+ browser_type = BROWSER_FIREFOX;
+ } else if (LowerCaseEqualsASCII(begin, end, kOperaImageName)) {
+ browser_type = BROWSER_OPERA;
+ } else {
+ browser_type = BROWSER_UNKNOWN;
+ }
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ return browser_type;
+}
+
+IEVersion GetIEVersion() {
+ static IEVersion ie_version = IE_INVALID;
+
+ if (ie_version == IE_INVALID) {
+ wchar_t exe_path[MAX_PATH];
+ HMODULE mod = GetModuleHandle(NULL);
+ GetModuleFileName(mod, exe_path, arraysize(exe_path) - 1);
+ std::wstring exe_name(file_util::GetFilenameFromPath(exe_path));
+ if (!LowerCaseEqualsASCII(exe_name, kIEImageName)) {
+ ie_version = NON_IE;
+ } else {
+ uint32 high = 0;
+ uint32 low = 0;
+ if (GetModuleVersion(mod, &high, &low)) {
+ switch (HIWORD(high)) {
+ case 6:
+ ie_version = IE_6;
+ break;
+ case 7:
+ ie_version = IE_7;
+ break;
+ default:
+ ie_version = HIWORD(high) >= 8 ? IE_8 : IE_UNSUPPORTED;
+ break;
+ }
+ } else {
+ NOTREACHED() << "Can't get IE version";
+ }
+ }
+ }
+
+ return ie_version;
+}
+
+bool IsIEInPrivate() {
+ typedef BOOL (WINAPI* IEIsInPrivateBrowsingPtr)();
+ bool incognito_mode = false;
+ HMODULE h = GetModuleHandle(L"ieframe.dll");
+ if (h) {
+ IEIsInPrivateBrowsingPtr IsInPrivate =
+ reinterpret_cast<IEIsInPrivateBrowsingPtr>(GetProcAddress(h,
+ "IEIsInPrivateBrowsing"));
+ if (IsInPrivate) {
+ incognito_mode = !!IsInPrivate();
+ }
+ }
+
+ return incognito_mode;
+}
+
+bool GetModuleVersion(HMODULE module, uint32* high, uint32* low) {
+ DCHECK(module != NULL)
+ << "Please use GetModuleHandle(NULL) to get the process name";
+ DCHECK(high);
+
+ bool ok = false;
+
+ HRSRC res = FindResource(module,
+ reinterpret_cast<const wchar_t*>(VS_VERSION_INFO), RT_VERSION);
+ if (res) {
+ HGLOBAL res_data = LoadResource(module, res);
+ DWORD version_resource_size = SizeofResource(module, res);
+ const void* readonly_resource_data = LockResource(res_data);
+ if (readonly_resource_data && version_resource_size) {
+ // Copy data as VerQueryValue tries to modify the data. This causes
+ // exceptions and heap corruption errors if debugger is attached.
+ scoped_ptr<char> data(new char[version_resource_size]);
+ memcpy(data.get(), readonly_resource_data, version_resource_size);
+ if (data.get()) {
+ VS_FIXEDFILEINFO* ver_info = NULL;
+ UINT info_size = 0;
+ if (VerQueryValue(data.get(), L"\\",
+ reinterpret_cast<void**>(&ver_info), &info_size)) {
+ *high = ver_info->dwFileVersionMS;
+ if (low != NULL)
+ *low = ver_info->dwFileVersionLS;
+ ok = true;
+ }
+
+ UnlockResource(res_data);
+ }
+ FreeResource(res_data);
+ }
+ }
+
+ return ok;
+}
+
+namespace {
+
+const int kMaxSubmenuDepth = 10;
+
+// Copies original_menu and returns the copy. The caller is responsible for
+// closing the returned HMENU. This does not currently copy over bitmaps
+// (e.g. hbmpChecked, hbmpUnchecked or hbmpItem), so checkmarks, radio buttons,
+// and custom icons won't work.
+// It also copies over submenus up to a maximum depth of kMaxSubMenuDepth.
+//
+// TODO(robertshield): Add support for the bitmap fields if need be.
+HMENU UtilCloneContextMenuImpl(HMENU original_menu, int depth) {
+ DCHECK(IsMenu(original_menu));
+
+ if (depth >= kMaxSubmenuDepth)
+ return NULL;
+
+ HMENU new_menu = CreatePopupMenu();
+ int item_count = GetMenuItemCount(original_menu);
+ if (item_count <= 0) {
+ NOTREACHED();
+ } else {
+ for (int i = 0; i < item_count; i++) {
+ MENUITEMINFO item_info = { 0 };
+ item_info.cbSize = sizeof(MENUITEMINFO);
+ item_info.fMask = MIIM_ID | MIIM_STRING | MIIM_FTYPE |
+ MIIM_STATE | MIIM_DATA | MIIM_SUBMENU |
+ MIIM_CHECKMARKS | MIIM_BITMAP;
+
+ // Call GetMenuItemInfo a first time to obtain the buffer size for
+ // the label.
+ if (GetMenuItemInfo(original_menu, i, TRUE, &item_info)) {
+ item_info.cch++; // Increment this as per MSDN
+ std::vector<wchar_t> buffer(item_info.cch, 0);
+ item_info.dwTypeData = &buffer[0];
+
+ // Call GetMenuItemInfo a second time with dwTypeData set to a buffer
+ // of a correct size to get the label.
+ GetMenuItemInfo(original_menu, i, TRUE, &item_info);
+
+ // Clone any submenus. Within reason.
+ if (item_info.hSubMenu) {
+ HMENU new_submenu = UtilCloneContextMenuImpl(item_info.hSubMenu,
+ depth + 1);
+ item_info.hSubMenu = new_submenu;
+ }
+
+ // Now insert the item into the new menu.
+ InsertMenuItem(new_menu, i, TRUE, &item_info);
+ }
+ }
+ }
+ return new_menu;
+}
+
+} // namespace
+
+HMENU UtilCloneContextMenu(HMENU original_menu) {
+ return UtilCloneContextMenuImpl(original_menu, 0);
+}
+
+std::string ResolveURL(const std::string& document,
+ const std::string& relative) {
+ if (document.empty()) {
+ return GURL(relative).spec();
+ } else {
+ return GURL(document).Resolve(relative).spec();
+ }
+}
+
+bool HaveSameOrigin(const std::string& url1, const std::string& url2) {
+ GURL a(url1), b(url2);
+ bool ret;
+ if (a.is_valid() != b.is_valid()) {
+ // Either (but not both) url is invalid, so they can't match.
+ ret = false;
+ } else if (!a.is_valid()) {
+ // Both URLs are invalid (see first check). Just check if the opaque
+ // strings match exactly.
+ ret = url1.compare(url2) == 0;
+ } else if (a.GetOrigin() != b.GetOrigin()) {
+ // The origins don't match.
+ ret = false;
+ } else {
+ // we have a match.
+ ret = true;
+ }
+
+ return ret;
+}
+
+int GetConfigInt(int default_value, const wchar_t* value_name) {
+ int ret = default_value;
+ RegKey config_key;
+ if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey,
+ KEY_QUERY_VALUE)) {
+ int value = FALSE;
+ if (config_key.ReadValueDW(value_name, reinterpret_cast<DWORD*>(&value))) {
+ ret = value;
+ }
+ }
+
+ return ret;
+}
+
+bool GetConfigBool(bool default_value, const wchar_t* value_name) {
+ DWORD value = GetConfigInt(default_value, value_name);
+ return (value != FALSE);
+}
+
+bool IsOptInUrl(const wchar_t* url) {
+ RegKey config_key;
+ if (!config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, KEY_READ))
+ return false;
+
+ RegistryValueIterator optin_urls_list(config_key.Handle(),
+ kChromeFrameOptinUrlsKey);
+ while (optin_urls_list.Valid()) {
+ if (MatchPattern(url, optin_urls_list.Name()))
+ return true;
+ ++optin_urls_list;
+ }
+
+ return false;
+}
+
+HRESULT GetUrlFromMoniker(IMoniker* moniker, IBindCtx* bind_context,
+ std::wstring* url) {
+ if (!moniker || !url) {
+ NOTREACHED();
+ return E_INVALIDARG;
+ }
+
+ ScopedComPtr<IBindCtx> temp_bind_context;
+ if (!bind_context) {
+ CreateBindCtx(0, temp_bind_context.Receive());
+ bind_context = temp_bind_context;
+ }
+
+ CComHeapPtr<WCHAR> display_name;
+ HRESULT hr = moniker->GetDisplayName(bind_context, NULL, &display_name);
+ if (display_name)
+ *url = display_name;
+
+ return hr;
+}
+
+bool IsValidUrlScheme(const std::wstring& url) {
+ if (url.empty())
+ return false;
+
+ GURL crack_url(url);
+
+ if (crack_url.SchemeIs("http") || crack_url.SchemeIs("https") ||
+ crack_url.SchemeIs("about") || crack_url.SchemeIs("view-source"))
+ return true;
+
+ if (StartsWith(url, kChromeAttachExternalTabPrefix, false))
+ return true;
+
+ return false;
+}
+
+// TODO(robertshield): Register and use Chrome's PathProviders.
+// - Note that this function is used by unit tests as well to override
+// PathService paths, so please test when addressing todo.
+bool GetUserProfileBaseDirectory(std::wstring* path) {
+ DCHECK(path);
+ wchar_t path_buffer[MAX_PATH * 4];
+ path_buffer[0] = 0;
+ // TODO(robertshield): Ideally we should use SHGetFolderLocation and then
+ // get a path via PIDL.
+ HRESULT hr = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL,
+ SHGFP_TYPE_CURRENT, path_buffer);
+
+ if (SUCCEEDED(hr)) {
+ *path = path_buffer;
+#if defined(GOOGLE_CHROME_BUILD)
+ file_util::AppendToPath(path, FILE_PATH_LITERAL("Google"));
+#endif
+ file_util::AppendToPath(path, chrome::kBrowserAppName);
+ file_util::AppendToPath(path, chrome::kUserDataDirname);
+ return true;
+ }
+
+ return false;
+}