Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(910)

Side by Side Diff: chrome_frame/utils.cc

Issue 218019: Initial import of the Chrome Frame codebase. Integration in chrome.gyp coming... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome_frame/utils.h ('k') | chrome_frame/vectored_handler.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <shlobj.h>
6
7 #include "chrome_frame/html_utils.h"
8 #include "chrome_frame/utils.h"
9
10 #include "base/file_util.h"
11 #include "base/file_version_info.h"
12 #include "base/logging.h"
13 #include "base/path_service.h"
14 #include "base/registry.h"
15 #include "base/scoped_comptr_win.h"
16 #include "base/string_util.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/installer/util/google_update_constants.h"
19 #include "googleurl/src/gurl.h"
20 #include "grit/chrome_frame_resources.h"
21 #include "chrome_frame/resource.h"
22
23 // Note that these values are all lower case and are compared to
24 // lower-case-transformed values.
25 const wchar_t kMetaTag[] = L"meta";
26 const wchar_t kHttpEquivAttribName[] = L"http-equiv";
27 const wchar_t kContentAttribName[] = L"content";
28 const wchar_t kXUACompatValue[] = L"x-ua-compatible";
29 const wchar_t kBodyTag[] = L"body";
30 const wchar_t kChromeContentPrefix[] = L"chrome=";
31 const wchar_t kChromeProtocolPrefix[] = L"cf:";
32
33 static const wchar_t kChromeFrameConfigKey[] =
34 L"Software\\Google\\ChromeFrame";
35 static const wchar_t kChromeFrameOptinUrlsKey[] = L"OptinUrls";
36
37 // Used to isolate chrome frame builds from google chrome release channels.
38 const wchar_t kChromeFrameOmahaSuffix[] = L"-cf";
39 const wchar_t kDevChannelName[] = L"-dev";
40
41 const wchar_t kChromeAttachExternalTabPrefix[] = L"attach_external_tab";
42
43 HRESULT UtilRegisterTypeLib(HINSTANCE tlb_instance,
44 LPCOLESTR index,
45 bool for_current_user_only) {
46 CComBSTR path;
47 CComPtr<ITypeLib> type_lib;
48 HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib);
49 if (SUCCEEDED(hr)) {
50 hr = UtilRegisterTypeLib(type_lib, path, NULL, for_current_user_only);
51 }
52 return hr;
53 }
54
55 HRESULT UtilUnRegisterTypeLib(HINSTANCE tlb_instance,
56 LPCOLESTR index,
57 bool for_current_user_only) {
58 CComBSTR path;
59 CComPtr<ITypeLib> type_lib;
60 HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib);
61 if (SUCCEEDED(hr)) {
62 hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only);
63 }
64 return hr;
65 }
66
67 HRESULT UtilRegisterTypeLib(LPCWSTR typelib_path,
68 bool for_current_user_only) {
69 if (NULL == typelib_path) {
70 return E_INVALIDARG;
71 }
72 CComBSTR path;
73 CComPtr<ITypeLib> type_lib;
74 HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib);
75 if (SUCCEEDED(hr)) {
76 hr = UtilRegisterTypeLib(type_lib,
77 typelib_path,
78 NULL,
79 for_current_user_only);
80 }
81 return hr;
82 }
83
84 HRESULT UtilUnRegisterTypeLib(LPCWSTR typelib_path,
85 bool for_current_user_only) {
86 CComPtr<ITypeLib> type_lib;
87 HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib);
88 if (SUCCEEDED(hr)) {
89 hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only);
90 }
91 return hr;
92 }
93
94 HRESULT UtilRegisterTypeLib(ITypeLib* typelib,
95 LPCWSTR typelib_path,
96 LPCWSTR help_dir,
97 bool for_current_user_only) {
98 typedef HRESULT(WINAPI *RegisterTypeLibPrototype)(ITypeLib FAR* type_lib,
99 OLECHAR FAR* full_path,
100 OLECHAR FAR* help_dir);
101 LPCSTR function_name =
102 for_current_user_only ? "RegisterTypeLibForUser" : "RegisterTypeLib";
103 RegisterTypeLibPrototype reg_tlb =
104 reinterpret_cast<RegisterTypeLibPrototype>(
105 GetProcAddress(GetModuleHandle(_T("oleaut32.dll")),
106 function_name));
107 if (NULL == reg_tlb) {
108 return E_FAIL;
109 }
110 return reg_tlb(typelib,
111 const_cast<OLECHAR*>(typelib_path),
112 const_cast<OLECHAR*>(help_dir));
113 }
114
115 HRESULT UtilUnRegisterTypeLib(ITypeLib* typelib,
116 bool for_current_user_only) {
117 if (NULL == typelib) {
118 return E_INVALIDARG;
119 }
120 typedef HRESULT(WINAPI *UnRegisterTypeLibPrototype)(
121 REFGUID libID,
122 unsigned short wVerMajor, // NOLINT
123 unsigned short wVerMinor, // NOLINT
124 LCID lcid,
125 SYSKIND syskind);
126 LPCSTR function_name =
127 for_current_user_only ? "UnRegisterTypeLibForUser" : "UnRegisterTypeLib";
128
129 UnRegisterTypeLibPrototype unreg_tlb =
130 reinterpret_cast<UnRegisterTypeLibPrototype>(
131 GetProcAddress(GetModuleHandle(_T("oleaut32.dll")),
132 function_name));
133 if (NULL == unreg_tlb) {
134 return E_FAIL;
135 }
136 TLIBATTR* tla = NULL;
137 HRESULT hr = typelib->GetLibAttr(&tla);
138 if (SUCCEEDED(hr)) {
139 hr = unreg_tlb(tla->guid,
140 tla->wMajorVerNum,
141 tla->wMinorVerNum,
142 tla->lcid,
143 tla->syskind);
144 typelib->ReleaseTLibAttr(tla);
145 }
146 return hr;
147 }
148
149 HRESULT UtilGetXUACompatContentValue(const std::wstring& html_string,
150 std::wstring* content_value) {
151 if (!content_value) {
152 return E_POINTER;
153 }
154
155 // Fail fast if the string X-UA-Compatible isn't in html_string
156 if (StringToLowerASCII(html_string).find(kXUACompatValue) ==
157 std::wstring::npos) {
158 return E_FAIL;
159 }
160
161 HTMLScanner scanner(html_string.c_str());
162
163 // Build the list of meta tags that occur before the body tag is hit.
164 HTMLScanner::StringRangeList tag_list;
165 scanner.GetTagsByName(kMetaTag, &tag_list, kBodyTag);
166
167 // Search the list of meta tags for one with an http-equiv="X-UA-Compatible"
168 // attribute.
169 HTMLScanner::StringRange attribute;
170 std::string search_attribute_ascii(WideToASCII(kXUACompatValue));
171 HTMLScanner::StringRangeList::const_iterator tag_list_iter(tag_list.begin());
172 for (; tag_list_iter != tag_list.end(); tag_list_iter++) {
173 if (!tag_list_iter->GetTagAttribute(kHttpEquivAttribName, &attribute)) {
174 continue;
175 }
176
177 // We found an http-equiv meta tag, check its value using the ascii
178 // case-insensitive comparison method.
179 if (!attribute.LowerCaseEqualsASCII(search_attribute_ascii.c_str())) {
180 continue;
181 }
182
183 // We found our X-UA-Compatible meta tag so look for and extract
184 // the value of the content attribute.
185 if (!tag_list_iter->GetTagAttribute(kContentAttribName, &attribute)) {
186 continue;
187 }
188
189 // Found the content string, copy and return.
190 content_value->assign(attribute.Copy());
191 return S_OK;
192 }
193
194 return E_FAIL;
195 }
196
197 bool AppendSuffixToChannelName(std::wstring* string,
198 const std::wstring& channel_name,
199 const std::wstring& suffix) {
200 size_t pos = string->find(channel_name);
201 // Append the suffix only if we find the channel name.
202 if (pos != std::wstring::npos) {
203 pos += channel_name.size();
204 // Append the suffix only to the channel name only if the name is not
205 // already followed by suffix.
206 if (string->find(suffix, pos) != pos) {
207 string->insert(pos, suffix);
208 return true;
209 }
210 }
211 return false;
212 }
213
214 bool RemoveSuffixFromChannelName(std::wstring* string,
215 const std::wstring& channel_name,
216 const std::wstring& suffix) {
217 std::wstring decorated_channel(channel_name + suffix);
218 size_t pos = string->find(decorated_channel);
219 // TODO(robertshield): Remove the suffix iff the suffix is the last thing in
220 // the string or is followed by another suffix that starts with '-'.
221 if (pos != std::wstring::npos) {
222 pos += channel_name.size();
223 string->erase(pos, suffix.size());
224 return true;
225 }
226 return false;
227 }
228
229 HRESULT UtilUpdateOmahaConfig(bool add_cf_suffix) {
230 HKEY reg_root = HKEY_LOCAL_MACHINE;
231
232 RegKey key;
233 std::wstring ap_key_value;
234 std::wstring reg_key(google_update::kRegPathClientState);
235 reg_key.append(L"\\");
236 reg_key.append(google_update::kChromeGuid);
237 if (!key.Open(reg_root, reg_key.c_str(), KEY_READ | KEY_WRITE) ||
238 !key.ReadValue(google_update::kRegApField, &ap_key_value)) {
239 // Can't read the Omaha config.
240 return REGDB_E_READREGDB;
241 }
242
243 HRESULT result = S_OK;
244 // We've read the key in, try and modify it then write it back.
245 if (add_cf_suffix && AppendSuffixToChannelName(&ap_key_value,
246 kDevChannelName,
247 kChromeFrameOmahaSuffix)) {
248 if (!key.WriteValue(google_update::kRegApField, ap_key_value.c_str())) {
249 DLOG(ERROR) << "Failed to add suffix to omaha ap key value.";
250 result = REGDB_E_WRITEREGDB;
251 }
252 } else if (!add_cf_suffix &&
253 RemoveSuffixFromChannelName(&ap_key_value,
254 kDevChannelName,
255 kChromeFrameOmahaSuffix)) {
256 if (!key.WriteValue(google_update::kRegApField, ap_key_value.c_str())) {
257 DLOG(ERROR) << "Failed to remove suffix from omaha ap key value.";
258 result = REGDB_E_WRITEREGDB;
259 }
260 } else {
261 // Getting here means that no modifications needed to be made.
262 result = S_FALSE;
263 }
264
265 return result;
266 }
267
268 std::wstring GetResourceString(int resource_id) {
269 std::wstring resource_string;
270 HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
271 const ATLSTRINGRESOURCEIMAGE* image = AtlGetStringResourceImage(
272 this_module, resource_id);
273 if (image) {
274 resource_string.assign(image->achString, image->nLength);
275 } else {
276 NOTREACHED() << "Unable to find resource id " << resource_id;
277 }
278 return resource_string;
279 }
280
281 void DisplayVersionMismatchWarning(HWND parent,
282 const std::string& server_version) {
283 // Obtain the current module version.
284 FileVersionInfo* file_version_info =
285 FileVersionInfo::CreateFileVersionInfoForCurrentModule();
286 DCHECK(file_version_info);
287 std::wstring version_string(file_version_info->file_version());
288 std::wstring wide_server_version;
289 if (server_version.empty()) {
290 wide_server_version = GetResourceString(IDS_VERSIONUNKNOWN);
291 } else {
292 wide_server_version = ASCIIToWide(server_version);
293 }
294 std::wstring title = GetResourceString(IDS_VERSIONMISMATCH_HEADER);
295 std::wstring message;
296 SStringPrintf(&message, GetResourceString(IDS_VERSIONMISMATCH).c_str(),
297 wide_server_version.c_str(), version_string.c_str());
298
299 ::MessageBox(parent, message.c_str(), title.c_str(), MB_OK);
300 }
301
302 std::string CreateJavascript(const std::string& function_name,
303 const std::string args) {
304 std::string script_string = "javascript:";
305 script_string += function_name + "(";
306 if (!args.empty()) {
307 script_string += "'";
308 script_string += args;
309 script_string += "'";
310 }
311 script_string += ")";
312 return script_string;
313 }
314
315 AddRefModule::AddRefModule() {
316 // TODO(tommi): Override the module's Lock/Unlock methods to call
317 // npapi::SetValue(NPPVpluginKeepLibraryInMemory) and keep the dll loaded
318 // while the module's refcount is > 0. Only do this when we're being
319 // used as an NPAPI module.
320 _pAtlModule->Lock();
321 }
322
323
324 AddRefModule::~AddRefModule() {
325 _pAtlModule->Unlock();
326 }
327
328 namespace {
329 const char kIEImageName[] = "iexplore.exe";
330 const char kFirefoxImageName[] = "firefox.exe";
331 const char kOperaImageName[] = "opera.exe";
332 } // namespace
333
334 std::wstring GetHostProcessName(bool include_extension) {
335 FilePath exe;
336 if (PathService::Get(base::FILE_EXE, &exe))
337 exe = exe.BaseName();
338 if (!include_extension) {
339 exe = exe.RemoveExtension();
340 }
341 return exe.ToWStringHack();
342 }
343
344 BrowserType GetBrowserType() {
345 static BrowserType browser_type = BROWSER_INVALID;
346
347 if (browser_type == BROWSER_INVALID) {
348 std::wstring exe(GetHostProcessName(true));
349 if (!exe.empty()) {
350 std::wstring::const_iterator begin = exe.begin();
351 std::wstring::const_iterator end = exe.end();
352 if (LowerCaseEqualsASCII(begin, end, kIEImageName)) {
353 browser_type = BROWSER_IE;
354 } else if (LowerCaseEqualsASCII(begin, end, kFirefoxImageName)) {
355 browser_type = BROWSER_FIREFOX;
356 } else if (LowerCaseEqualsASCII(begin, end, kOperaImageName)) {
357 browser_type = BROWSER_OPERA;
358 } else {
359 browser_type = BROWSER_UNKNOWN;
360 }
361 } else {
362 NOTREACHED();
363 }
364 }
365
366 return browser_type;
367 }
368
369 IEVersion GetIEVersion() {
370 static IEVersion ie_version = IE_INVALID;
371
372 if (ie_version == IE_INVALID) {
373 wchar_t exe_path[MAX_PATH];
374 HMODULE mod = GetModuleHandle(NULL);
375 GetModuleFileName(mod, exe_path, arraysize(exe_path) - 1);
376 std::wstring exe_name(file_util::GetFilenameFromPath(exe_path));
377 if (!LowerCaseEqualsASCII(exe_name, kIEImageName)) {
378 ie_version = NON_IE;
379 } else {
380 uint32 high = 0;
381 uint32 low = 0;
382 if (GetModuleVersion(mod, &high, &low)) {
383 switch (HIWORD(high)) {
384 case 6:
385 ie_version = IE_6;
386 break;
387 case 7:
388 ie_version = IE_7;
389 break;
390 default:
391 ie_version = HIWORD(high) >= 8 ? IE_8 : IE_UNSUPPORTED;
392 break;
393 }
394 } else {
395 NOTREACHED() << "Can't get IE version";
396 }
397 }
398 }
399
400 return ie_version;
401 }
402
403 bool IsIEInPrivate() {
404 typedef BOOL (WINAPI* IEIsInPrivateBrowsingPtr)();
405 bool incognito_mode = false;
406 HMODULE h = GetModuleHandle(L"ieframe.dll");
407 if (h) {
408 IEIsInPrivateBrowsingPtr IsInPrivate =
409 reinterpret_cast<IEIsInPrivateBrowsingPtr>(GetProcAddress(h,
410 "IEIsInPrivateBrowsing"));
411 if (IsInPrivate) {
412 incognito_mode = !!IsInPrivate();
413 }
414 }
415
416 return incognito_mode;
417 }
418
419 bool GetModuleVersion(HMODULE module, uint32* high, uint32* low) {
420 DCHECK(module != NULL)
421 << "Please use GetModuleHandle(NULL) to get the process name";
422 DCHECK(high);
423
424 bool ok = false;
425
426 HRSRC res = FindResource(module,
427 reinterpret_cast<const wchar_t*>(VS_VERSION_INFO), RT_VERSION);
428 if (res) {
429 HGLOBAL res_data = LoadResource(module, res);
430 DWORD version_resource_size = SizeofResource(module, res);
431 const void* readonly_resource_data = LockResource(res_data);
432 if (readonly_resource_data && version_resource_size) {
433 // Copy data as VerQueryValue tries to modify the data. This causes
434 // exceptions and heap corruption errors if debugger is attached.
435 scoped_ptr<char> data(new char[version_resource_size]);
436 memcpy(data.get(), readonly_resource_data, version_resource_size);
437 if (data.get()) {
438 VS_FIXEDFILEINFO* ver_info = NULL;
439 UINT info_size = 0;
440 if (VerQueryValue(data.get(), L"\\",
441 reinterpret_cast<void**>(&ver_info), &info_size)) {
442 *high = ver_info->dwFileVersionMS;
443 if (low != NULL)
444 *low = ver_info->dwFileVersionLS;
445 ok = true;
446 }
447
448 UnlockResource(res_data);
449 }
450 FreeResource(res_data);
451 }
452 }
453
454 return ok;
455 }
456
457 namespace {
458
459 const int kMaxSubmenuDepth = 10;
460
461 // Copies original_menu and returns the copy. The caller is responsible for
462 // closing the returned HMENU. This does not currently copy over bitmaps
463 // (e.g. hbmpChecked, hbmpUnchecked or hbmpItem), so checkmarks, radio buttons,
464 // and custom icons won't work.
465 // It also copies over submenus up to a maximum depth of kMaxSubMenuDepth.
466 //
467 // TODO(robertshield): Add support for the bitmap fields if need be.
468 HMENU UtilCloneContextMenuImpl(HMENU original_menu, int depth) {
469 DCHECK(IsMenu(original_menu));
470
471 if (depth >= kMaxSubmenuDepth)
472 return NULL;
473
474 HMENU new_menu = CreatePopupMenu();
475 int item_count = GetMenuItemCount(original_menu);
476 if (item_count <= 0) {
477 NOTREACHED();
478 } else {
479 for (int i = 0; i < item_count; i++) {
480 MENUITEMINFO item_info = { 0 };
481 item_info.cbSize = sizeof(MENUITEMINFO);
482 item_info.fMask = MIIM_ID | MIIM_STRING | MIIM_FTYPE |
483 MIIM_STATE | MIIM_DATA | MIIM_SUBMENU |
484 MIIM_CHECKMARKS | MIIM_BITMAP;
485
486 // Call GetMenuItemInfo a first time to obtain the buffer size for
487 // the label.
488 if (GetMenuItemInfo(original_menu, i, TRUE, &item_info)) {
489 item_info.cch++; // Increment this as per MSDN
490 std::vector<wchar_t> buffer(item_info.cch, 0);
491 item_info.dwTypeData = &buffer[0];
492
493 // Call GetMenuItemInfo a second time with dwTypeData set to a buffer
494 // of a correct size to get the label.
495 GetMenuItemInfo(original_menu, i, TRUE, &item_info);
496
497 // Clone any submenus. Within reason.
498 if (item_info.hSubMenu) {
499 HMENU new_submenu = UtilCloneContextMenuImpl(item_info.hSubMenu,
500 depth + 1);
501 item_info.hSubMenu = new_submenu;
502 }
503
504 // Now insert the item into the new menu.
505 InsertMenuItem(new_menu, i, TRUE, &item_info);
506 }
507 }
508 }
509 return new_menu;
510 }
511
512 } // namespace
513
514 HMENU UtilCloneContextMenu(HMENU original_menu) {
515 return UtilCloneContextMenuImpl(original_menu, 0);
516 }
517
518 std::string ResolveURL(const std::string& document,
519 const std::string& relative) {
520 if (document.empty()) {
521 return GURL(relative).spec();
522 } else {
523 return GURL(document).Resolve(relative).spec();
524 }
525 }
526
527 bool HaveSameOrigin(const std::string& url1, const std::string& url2) {
528 GURL a(url1), b(url2);
529 bool ret;
530 if (a.is_valid() != b.is_valid()) {
531 // Either (but not both) url is invalid, so they can't match.
532 ret = false;
533 } else if (!a.is_valid()) {
534 // Both URLs are invalid (see first check). Just check if the opaque
535 // strings match exactly.
536 ret = url1.compare(url2) == 0;
537 } else if (a.GetOrigin() != b.GetOrigin()) {
538 // The origins don't match.
539 ret = false;
540 } else {
541 // we have a match.
542 ret = true;
543 }
544
545 return ret;
546 }
547
548 int GetConfigInt(int default_value, const wchar_t* value_name) {
549 int ret = default_value;
550 RegKey config_key;
551 if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey,
552 KEY_QUERY_VALUE)) {
553 int value = FALSE;
554 if (config_key.ReadValueDW(value_name, reinterpret_cast<DWORD*>(&value))) {
555 ret = value;
556 }
557 }
558
559 return ret;
560 }
561
562 bool GetConfigBool(bool default_value, const wchar_t* value_name) {
563 DWORD value = GetConfigInt(default_value, value_name);
564 return (value != FALSE);
565 }
566
567 bool IsOptInUrl(const wchar_t* url) {
568 RegKey config_key;
569 if (!config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, KEY_READ))
570 return false;
571
572 RegistryValueIterator optin_urls_list(config_key.Handle(),
573 kChromeFrameOptinUrlsKey);
574 while (optin_urls_list.Valid()) {
575 if (MatchPattern(url, optin_urls_list.Name()))
576 return true;
577 ++optin_urls_list;
578 }
579
580 return false;
581 }
582
583 HRESULT GetUrlFromMoniker(IMoniker* moniker, IBindCtx* bind_context,
584 std::wstring* url) {
585 if (!moniker || !url) {
586 NOTREACHED();
587 return E_INVALIDARG;
588 }
589
590 ScopedComPtr<IBindCtx> temp_bind_context;
591 if (!bind_context) {
592 CreateBindCtx(0, temp_bind_context.Receive());
593 bind_context = temp_bind_context;
594 }
595
596 CComHeapPtr<WCHAR> display_name;
597 HRESULT hr = moniker->GetDisplayName(bind_context, NULL, &display_name);
598 if (display_name)
599 *url = display_name;
600
601 return hr;
602 }
603
604 bool IsValidUrlScheme(const std::wstring& url) {
605 if (url.empty())
606 return false;
607
608 GURL crack_url(url);
609
610 if (crack_url.SchemeIs("http") || crack_url.SchemeIs("https") ||
611 crack_url.SchemeIs("about") || crack_url.SchemeIs("view-source"))
612 return true;
613
614 if (StartsWith(url, kChromeAttachExternalTabPrefix, false))
615 return true;
616
617 return false;
618 }
619
620 // TODO(robertshield): Register and use Chrome's PathProviders.
621 // - Note that this function is used by unit tests as well to override
622 // PathService paths, so please test when addressing todo.
623 bool GetUserProfileBaseDirectory(std::wstring* path) {
624 DCHECK(path);
625 wchar_t path_buffer[MAX_PATH * 4];
626 path_buffer[0] = 0;
627 // TODO(robertshield): Ideally we should use SHGetFolderLocation and then
628 // get a path via PIDL.
629 HRESULT hr = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL,
630 SHGFP_TYPE_CURRENT, path_buffer);
631
632 if (SUCCEEDED(hr)) {
633 *path = path_buffer;
634 #if defined(GOOGLE_CHROME_BUILD)
635 file_util::AppendToPath(path, FILE_PATH_LITERAL("Google"));
636 #endif
637 file_util::AppendToPath(path, chrome::kBrowserAppName);
638 file_util::AppendToPath(path, chrome::kUserDataDirname);
639 return true;
640 }
641
642 return false;
643 }
OLDNEW
« no previous file with comments | « chrome_frame/utils.h ('k') | chrome_frame/vectored_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698