Simple Cache: a few tests for rare corner cases with CRC check missing.
[chromium-blink-merge.git] / chrome_frame / utils.cc
blob2b6525b03de47890da6d6a50b6177e1a43088c6a
1 // Copyright (c) 2012 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.
5 #include "chrome_frame/utils.h"
7 #include <atlsafe.h>
8 #include <atlsecurity.h>
9 #include <htiframe.h>
10 #include <mshtml.h>
11 #include <shlobj.h>
13 #include "base/file_version_info.h"
14 #include "base/lazy_instance.h"
15 #include "base/logging.h"
16 #include "base/path_service.h"
17 #include "base/string_number_conversions.h"
18 #include "base/string_util.h"
19 #include "base/stringprintf.h"
20 #include "base/strings/string_piece.h"
21 #include "base/strings/string_tokenizer.h"
22 #include "base/threading/thread_local.h"
23 #include "base/utf_string_conversions.h"
24 #include "base/win/registry.h"
25 #include "base/win/scoped_bstr.h"
26 #include "base/win/scoped_comptr.h"
27 #include "base/win/scoped_variant.h"
28 #include "chrome/common/automation_messages.h"
29 #include "chrome/common/chrome_paths_internal.h"
30 #include "chrome/common/url_constants.h"
31 #include "chrome/installer/util/chrome_frame_distribution.h"
32 #include "chrome_frame/chrome_tab.h"
33 #include "chrome_frame/extra_system_apis.h"
34 #include "chrome_frame/html_utils.h"
35 #include "chrome_frame/navigation_constraints.h"
36 #include "chrome_frame/policy_settings.h"
37 #include "chrome_frame/registry_list_preferences_holder.h"
38 #include "chrome_frame/simple_resource_loader.h"
39 #include "extensions/common/constants.h"
40 #include "googleurl/src/gurl.h"
41 #include "googleurl/src/url_canon.h"
42 #include "grit/chromium_strings.h"
43 #include "net/base/escape.h"
44 #include "net/http/http_util.h"
45 #include "ui/base/models/menu_model.h"
47 using base::win::RegKey;
49 // Note that these values are all lower case and are compared to
50 // lower-case-transformed values.
51 const char kGCFProtocol[] = "gcf";
52 const wchar_t kBodyTag[] = L"body";
53 const wchar_t kContentAttribName[] = L"content";
54 const wchar_t kChromeContentPrefix[] = L"chrome=";
55 const wchar_t kChromeMimeType[] = L"application/chromepage";
56 const wchar_t kChromeProtocolPrefix[] = L"gcf:";
57 const wchar_t kHttpEquivAttribName[] = L"http-equiv";
58 const wchar_t kIexploreProfileName[] = L"iexplore";
59 const wchar_t kMetaTag[] = L"meta";
60 const wchar_t kRundllProfileName[] = L"rundll32";
61 const wchar_t kXUACompatValue[] = L"x-ua-compatible";
63 // Registry key and value names related to Chrome Frame configuration options.
64 const wchar_t kAllowUnsafeURLs[] = L"AllowUnsafeURLs";
65 const wchar_t kChromeFrameConfigKey[] = L"Software\\Google\\ChromeFrame";
66 const wchar_t kEnableBuggyBhoIntercept[] = L"EnableBuggyBhoIntercept";
67 const wchar_t kEnableGCFRendererByDefault[] = L"IsDefaultRenderer";
68 const wchar_t kExcludeUAFromDomainList[] = L"ExcludeUAFromDomain";
69 const wchar_t kPatchProtocols[] = L"PatchProtocols";
70 const wchar_t kRenderInGCFUrlList[] = L"RenderInGcfUrls";
71 const wchar_t kRenderInHostUrlList[] = L"RenderInHostUrls";
73 static const wchar_t kChromeFramePersistNPAPIReg[] = L"PersistNPAPIReg";
75 const char kAttachExternalTabPrefix[] = "attach_external_tab";
77 // Indicates that we are running in a test environment, where execptions, etc
78 // are handled by the chrome test crash server.
79 const wchar_t kChromeFrameHeadlessMode[] = L"ChromeFrameHeadlessMode";
81 // Indicates that we are running in an environment that expects chrome renderer
82 // accessibility to be enabled for use in automation tests.
83 const wchar_t kChromeFrameAccessibleMode[] = L"ChromeFrameAccessibleMode";
85 // Indicates that we are running in an environment that wishes to avoid
86 // DLL pinning, such as the perf tests.
87 const wchar_t kChromeFrameUnpinnedMode[] = L"kChromeFrameUnpinnedMode";
89 // Controls whether we download subresources, etc on the chrome frame page in
90 // the background worker thread. Defaults to true.
91 const wchar_t kUseBackgroundThreadForSubResources[]
92 = L"BackgroundHTTPWorkerThread";
94 // {1AF32B6C-A3BA-48B9-B24E-8AA9C41F6ECD}
95 static const IID IID_IWebBrowserPriv2IE7 = { 0x1AF32B6C, 0xA3BA, 0x48B9,
96 { 0xB2, 0x4E, 0x8A, 0xA9, 0xC4, 0x1F, 0x6E, 0xCD } };
98 // {3ED72303-6FFC-4214-BA90-FAF1862DEC8A}
99 static const IID IID_IWebBrowserPriv2IE8 = { 0x3ED72303, 0x6FFC, 0x4214,
100 { 0xBA, 0x90, 0xFA, 0xF1, 0x86, 0x2D, 0xEC, 0x8A } };
102 // {486F6159-9F3F-4827-82D4-283CEF397733}
103 static const IID IID_IWebBrowserPriv2IE8XP = { 0x486F6159, 0x9F3F, 0x4827,
104 { 0x82, 0xD4, 0x28, 0x3C, 0xEF, 0x39, 0x77, 0x33 } };
106 // {38339692-0BC9-46CB-8E5C-4677A5C83DD5}
107 static const IID IID_IWebBrowserPriv2IE8XPBeta = { 0x38339692, 0x0BC9, 0x46CB,
108 { 0x8E, 0x5C, 0x46, 0x77, 0xA5, 0xC8, 0x3D, 0xD5 } };
110 namespace {
112 // A flag used to signal when an active browser instance on the current thread
113 // is loading a Chrome Frame document. There's no reference stored with the
114 // pointer so it should not be dereferenced and used for comparison against a
115 // living instance only.
116 base::LazyInstance<base::ThreadLocalPointer<IBrowserService> >
117 g_tls_browser_for_cf_navigation = LAZY_INSTANCE_INITIALIZER;
119 // Holds the cached preferences for the per-url render type settings.
120 base::LazyInstance<RegistryListPreferencesHolder>::Leaky
121 g_render_type_for_url_holder;
123 // Holds the cached preferences for the per-url user agent filter.
124 base::LazyInstance<RegistryListPreferencesHolder>::Leaky
125 g_user_agent_filter_holder;
127 } // end anonymous namespace
129 HRESULT UtilRegisterTypeLib(HINSTANCE tlb_instance,
130 LPCOLESTR index,
131 bool for_current_user_only) {
132 CComBSTR path;
133 CComPtr<ITypeLib> type_lib;
134 HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib);
135 if (SUCCEEDED(hr)) {
136 hr = UtilRegisterTypeLib(type_lib, path, NULL, for_current_user_only);
138 return hr;
141 HRESULT UtilUnRegisterTypeLib(HINSTANCE tlb_instance,
142 LPCOLESTR index,
143 bool for_current_user_only) {
144 CComBSTR path;
145 CComPtr<ITypeLib> type_lib;
146 HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib);
147 if (SUCCEEDED(hr)) {
148 hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only);
150 return hr;
153 HRESULT UtilRegisterTypeLib(LPCWSTR typelib_path,
154 bool for_current_user_only) {
155 if (NULL == typelib_path) {
156 return E_INVALIDARG;
158 CComBSTR path;
159 CComPtr<ITypeLib> type_lib;
160 HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib);
161 if (SUCCEEDED(hr)) {
162 hr = UtilRegisterTypeLib(type_lib,
163 typelib_path,
164 NULL,
165 for_current_user_only);
167 return hr;
170 HRESULT UtilUnRegisterTypeLib(LPCWSTR typelib_path,
171 bool for_current_user_only) {
172 CComPtr<ITypeLib> type_lib;
173 HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib);
174 if (SUCCEEDED(hr)) {
175 hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only);
177 return hr;
180 HRESULT UtilRegisterTypeLib(ITypeLib* typelib,
181 LPCWSTR typelib_path,
182 LPCWSTR help_dir,
183 bool for_current_user_only) {
184 typedef HRESULT(WINAPI *RegisterTypeLibPrototype)(ITypeLib FAR* type_lib,
185 OLECHAR FAR* full_path,
186 OLECHAR FAR* help_dir);
187 LPCSTR function_name =
188 for_current_user_only ? "RegisterTypeLibForUser" : "RegisterTypeLib";
189 RegisterTypeLibPrototype reg_tlb =
190 reinterpret_cast<RegisterTypeLibPrototype>(
191 GetProcAddress(GetModuleHandle(_T("oleaut32.dll")),
192 function_name));
193 if (NULL == reg_tlb) {
194 return E_FAIL;
196 return reg_tlb(typelib,
197 const_cast<OLECHAR*>(typelib_path),
198 const_cast<OLECHAR*>(help_dir));
201 HRESULT UtilUnRegisterTypeLib(ITypeLib* typelib,
202 bool for_current_user_only) {
203 if (NULL == typelib) {
204 return E_INVALIDARG;
206 typedef HRESULT(WINAPI *UnRegisterTypeLibPrototype)(
207 REFGUID libID,
208 unsigned short wVerMajor, // NOLINT
209 unsigned short wVerMinor, // NOLINT
210 LCID lcid,
211 SYSKIND syskind);
212 LPCSTR function_name =
213 for_current_user_only ? "UnRegisterTypeLibForUser" : "UnRegisterTypeLib";
215 UnRegisterTypeLibPrototype unreg_tlb =
216 reinterpret_cast<UnRegisterTypeLibPrototype>(
217 GetProcAddress(GetModuleHandle(_T("oleaut32.dll")),
218 function_name));
219 if (NULL == unreg_tlb) {
220 return E_FAIL;
222 TLIBATTR* tla = NULL;
223 HRESULT hr = typelib->GetLibAttr(&tla);
224 if (SUCCEEDED(hr)) {
225 hr = unreg_tlb(tla->guid,
226 tla->wMajorVerNum,
227 tla->wMinorVerNum,
228 tla->lcid,
229 tla->syskind);
230 typelib->ReleaseTLibAttr(tla);
232 return hr;
235 bool UtilRemovePersistentNPAPIMarker() {
236 BrowserDistribution* cf_dist = BrowserDistribution::GetDistribution();
237 std::wstring cf_state_key_path(cf_dist->GetStateKey());
238 RegKey cf_state_key;
240 LONG result = cf_state_key.Open(HKEY_LOCAL_MACHINE, cf_state_key_path.c_str(),
241 KEY_SET_VALUE);
242 if (result == ERROR_SUCCESS)
243 result = cf_state_key.DeleteValue(kChromeFramePersistNPAPIReg);
244 return (result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
247 HRESULT UtilGetXUACompatContentValue(const std::wstring& html_string,
248 std::wstring* content_value) {
249 if (!content_value) {
250 return E_POINTER;
253 // Fail fast if the string X-UA-Compatible isn't in html_string
254 if (StringToLowerASCII(html_string).find(kXUACompatValue) ==
255 std::wstring::npos) {
256 return E_FAIL;
259 HTMLScanner scanner(html_string.c_str());
261 // Build the list of meta tags that occur before the body tag is hit.
262 HTMLScanner::StringRangeList tag_list;
263 scanner.GetTagsByName(kMetaTag, &tag_list, kBodyTag);
265 // Search the list of meta tags for one with an http-equiv="X-UA-Compatible"
266 // attribute.
267 HTMLScanner::StringRange attribute;
268 std::string search_attribute_ascii(WideToASCII(kXUACompatValue));
269 HTMLScanner::StringRangeList::const_iterator tag_list_iter(tag_list.begin());
270 for (; tag_list_iter != tag_list.end(); tag_list_iter++) {
271 if (!tag_list_iter->GetTagAttribute(kHttpEquivAttribName, &attribute)) {
272 continue;
275 // We found an http-equiv meta tag, check its value using the ascii
276 // case-insensitive comparison method.
277 if (!attribute.LowerCaseEqualsASCII(search_attribute_ascii.c_str())) {
278 continue;
281 // We found our X-UA-Compatible meta tag so look for and extract
282 // the value of the content attribute.
283 if (!tag_list_iter->GetTagAttribute(kContentAttribName, &attribute)) {
284 continue;
287 // Found the content string, copy and return.
288 content_value->assign(attribute.Copy());
289 return S_OK;
292 return E_FAIL;
295 void DisplayVersionMismatchWarning(HWND parent,
296 const std::string& server_version) {
297 // Obtain the current module version.
298 scoped_ptr<FileVersionInfo> module_version_info(
299 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
300 string16 version_string(module_version_info->file_version());
301 std::wstring wide_server_version;
302 if (server_version.empty()) {
303 wide_server_version = SimpleResourceLoader::Get(IDS_VERSIONUNKNOWN);
304 } else {
305 wide_server_version = ASCIIToWide(server_version);
307 std::wstring title = SimpleResourceLoader::Get(IDS_VERSIONMISMATCH_HEADER);
308 std::wstring message;
309 base::SStringPrintf(&message,
310 SimpleResourceLoader::Get(IDS_VERSIONMISMATCH).c_str(),
311 wide_server_version.c_str(),
312 version_string.c_str());
314 ::MessageBox(parent, message.c_str(), title.c_str(), MB_OK);
317 std::string CreateJavascript(const std::string& function_name,
318 const std::string args) {
319 std::string script_string = "javascript:";
320 script_string += function_name + "(";
321 if (!args.empty()) {
322 script_string += "'";
323 script_string += args;
324 script_string += "'";
326 script_string += ")";
327 return script_string;
330 AddRefModule::AddRefModule() {
331 // TODO(tommi): Override the module's Lock/Unlock methods to call
332 // npapi::SetValue(NPPVpluginKeepLibraryInMemory) and keep the dll loaded
333 // while the module's refcount is > 0. Only do this when we're being
334 // used as an NPAPI module.
335 _pAtlModule->Lock();
339 AddRefModule::~AddRefModule() {
340 _pAtlModule->Unlock();
343 bool IsChrome(RendererType renderer_type) {
344 DCHECK_GE(renderer_type, RENDERER_TYPE_UNDETERMINED);
345 DCHECK_LE(renderer_type, RENDERER_TYPE_OTHER);
346 return renderer_type >= RENDERER_TYPE_CHROME_MIN &&
347 renderer_type <= RENDERER_TYPE_CHROME_MAX;
350 namespace {
351 const char kIEImageName[] = "iexplore.exe";
352 } // namespace
354 std::wstring GetHostProcessName(bool include_extension) {
355 base::FilePath exe;
356 if (PathService::Get(base::FILE_EXE, &exe))
357 exe = exe.BaseName();
358 if (!include_extension) {
359 exe = exe.RemoveExtension();
361 return exe.value();
364 BrowserType GetBrowserType() {
365 static BrowserType browser_type = BROWSER_INVALID;
367 if (browser_type == BROWSER_INVALID) {
368 std::wstring exe(GetHostProcessName(true));
369 if (!exe.empty()) {
370 std::wstring::const_iterator begin = exe.begin();
371 std::wstring::const_iterator end = exe.end();
372 if (LowerCaseEqualsASCII(begin, end, kIEImageName)) {
373 browser_type = BROWSER_IE;
374 } else {
375 browser_type = BROWSER_UNKNOWN;
377 } else {
378 NOTREACHED();
382 return browser_type;
385 uint32 GetIEMajorVersion() {
386 static uint32 ie_major_version = UINT_MAX;
388 if (ie_major_version == UINT_MAX) {
389 wchar_t exe_path[MAX_PATH];
390 HMODULE mod = GetModuleHandle(NULL);
391 GetModuleFileName(mod, exe_path, arraysize(exe_path) - 1);
392 std::wstring exe_name = base::FilePath(exe_path).BaseName().value();
393 if (!LowerCaseEqualsASCII(exe_name, kIEImageName)) {
394 ie_major_version = 0;
395 } else {
396 uint32 high = 0;
397 uint32 low = 0;
398 if (GetModuleVersion(mod, &high, &low)) {
399 ie_major_version = HIWORD(high);
400 } else {
401 ie_major_version = 0;
406 return ie_major_version;
409 IEVersion GetIEVersion() {
410 static IEVersion ie_version = IE_INVALID;
412 if (ie_version == IE_INVALID) {
413 uint32 major_version = GetIEMajorVersion();
414 switch (major_version) {
415 case 0:
416 ie_version = NON_IE;
417 break;
418 case 6:
419 ie_version = IE_6;
420 break;
421 case 7:
422 ie_version = IE_7;
423 break;
424 case 8:
425 ie_version = IE_8;
426 break;
427 case 9:
428 ie_version = IE_9;
429 break;
430 default:
431 ie_version = (major_version >= 10) ? IE_10 : IE_UNSUPPORTED;
432 break;
436 return ie_version;
439 base::FilePath GetIETemporaryFilesFolder() {
440 LPITEMIDLIST tif_pidl = NULL;
441 HRESULT hr = SHGetFolderLocation(NULL, CSIDL_INTERNET_CACHE, NULL,
442 SHGFP_TYPE_CURRENT, &tif_pidl);
443 if (SUCCEEDED(hr) && tif_pidl) {
444 base::win::ScopedComPtr<IShellFolder> parent_folder;
445 LPITEMIDLIST relative_pidl = NULL;
446 hr = SHBindToParent(tif_pidl, IID_IShellFolder,
447 reinterpret_cast<void**>(parent_folder.Receive()),
448 const_cast<LPCITEMIDLIST*>(&relative_pidl));
449 if (SUCCEEDED(hr) && relative_pidl) {
450 STRRET path = {0};
451 hr = parent_folder->GetDisplayNameOf(relative_pidl,
452 SHGDN_NORMAL | SHGDN_FORPARSING,
453 &path);
454 DCHECK(SUCCEEDED(hr));
455 base::win::ScopedBstr temp_internet_files_bstr;
456 StrRetToBSTR(&path, relative_pidl, temp_internet_files_bstr.Receive());
457 base::FilePath temp_internet_files(
458 static_cast<BSTR>(temp_internet_files_bstr));
459 ILFree(tif_pidl);
460 return temp_internet_files;
461 } else {
462 NOTREACHED() << "SHBindToParent failed with Error:" << hr;
463 ILFree(tif_pidl);
465 } else {
466 NOTREACHED() << "SHGetFolderLocation for internet cache failed. Error:"
467 << hr;
469 // As a last ditch effort we use the SHGetFolderPath function to retrieve the
470 // path. This function has a limitation of MAX_PATH.
471 wchar_t path[MAX_PATH + 1] = {0};
472 hr = SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_CURRENT,
473 path);
474 if (SUCCEEDED(hr)) {
475 return base::FilePath(path);
476 } else {
477 NOTREACHED() << "SHGetFolderPath for internet cache failed. Error:"
478 << hr;
480 return base::FilePath();
483 bool IsIEInPrivate() {
484 typedef BOOL (WINAPI* IEIsInPrivateBrowsingPtr)();
485 bool incognito_mode = false;
486 HMODULE h = GetModuleHandle(L"ieframe.dll");
487 if (h) {
488 IEIsInPrivateBrowsingPtr IsInPrivate =
489 reinterpret_cast<IEIsInPrivateBrowsingPtr>(GetProcAddress(h,
490 "IEIsInPrivateBrowsing"));
491 if (IsInPrivate) {
492 incognito_mode = !!IsInPrivate();
496 return incognito_mode;
499 HRESULT DoFileDownloadInIE(const wchar_t* url) {
500 DCHECK(url);
502 HMODULE mod = ::GetModuleHandleA("ieframe.dll");
503 if (!mod)
504 mod = ::GetModuleHandleA("shdocvw.dll");
506 if (!mod) {
507 NOTREACHED();
508 return E_UNEXPECTED;
511 typedef HRESULT (WINAPI* DoFileDownloadFn)(const wchar_t*);
512 DoFileDownloadFn fn = reinterpret_cast<DoFileDownloadFn>(
513 ::GetProcAddress(mod, "DoFileDownload"));
514 DCHECK(fn);
515 return fn ? fn(url) : E_UNEXPECTED;
518 bool GetModuleVersion(HMODULE module, uint32* high, uint32* low) {
519 DCHECK(module != NULL)
520 << "Please use GetModuleHandle(NULL) to get the process name";
521 DCHECK(high);
523 bool ok = false;
525 HRSRC res = FindResource(module,
526 reinterpret_cast<const wchar_t*>(VS_VERSION_INFO), RT_VERSION);
527 if (res) {
528 HGLOBAL res_data = LoadResource(module, res);
529 DWORD version_resource_size = SizeofResource(module, res);
530 const void* readonly_resource_data = LockResource(res_data);
531 if (readonly_resource_data && version_resource_size) {
532 // Copy data as VerQueryValue tries to modify the data. This causes
533 // exceptions and heap corruption errors if debugger is attached.
534 scoped_ptr<char[]> data(new char[version_resource_size]);
535 if (data.get()) {
536 memcpy(data.get(), readonly_resource_data, version_resource_size);
537 VS_FIXEDFILEINFO* ver_info = NULL;
538 UINT info_size = 0;
539 if (VerQueryValue(data.get(), L"\\",
540 reinterpret_cast<void**>(&ver_info), &info_size)) {
541 *high = ver_info->dwFileVersionMS;
542 if (low != NULL)
543 *low = ver_info->dwFileVersionLS;
544 ok = true;
547 UnlockResource(res_data);
549 FreeResource(res_data);
553 return ok;
556 namespace {
558 const int kMaxSubmenuDepth = 10;
560 // Builds a Windows menu from the menu model sent from Chrome. The
561 // caller is responsible for closing the returned HMENU. This does
562 // not currently handle bitmaps (e.g. hbmpChecked, hbmpUnchecked or
563 // hbmpItem), so checkmarks, radio buttons, and custom icons won't work.
564 // It also copies over submenus up to a maximum depth of kMaxSubMenuDepth.
565 HMENU BuildContextMenuImpl(const ContextMenuModel* menu_model, int depth) {
566 if (depth >= kMaxSubmenuDepth)
567 return NULL;
569 HMENU menu = CreatePopupMenu();
570 for (size_t i = 0; i < menu_model->items.size(); i++) {
571 const ContextMenuModel::Item& item = menu_model->items[i];
573 MENUITEMINFO item_info = { 0 };
574 item_info.cbSize = sizeof(MENUITEMINFO);
575 switch (item.type) {
576 case ui::MenuModel::TYPE_COMMAND:
577 case ui::MenuModel::TYPE_CHECK:
578 item_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
579 item_info.fType = MFT_STRING;
580 item_info.wID = item.item_id;
581 item_info.dwTypeData = const_cast<LPWSTR>(item.label.c_str());
582 break;
583 case ui::MenuModel::TYPE_RADIO:
584 item_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
585 item_info.fType = MFT_STRING | MFT_RADIOCHECK;
586 item_info.wID = item.item_id;
587 item_info.dwTypeData = const_cast<LPWSTR>(item.label.c_str());
588 break;
589 case ui::MenuModel::TYPE_SEPARATOR:
590 item_info.fMask = MIIM_FTYPE;
591 item_info.fType = MFT_SEPARATOR;
592 break;
593 case ui::MenuModel::TYPE_SUBMENU:
594 item_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_SUBMENU;
595 item_info.fType = MFT_STRING;
596 item_info.wID = item.item_id;
597 item_info.dwTypeData = const_cast<LPWSTR>(item.label.c_str());
598 item_info.hSubMenu = BuildContextMenuImpl(item.submenu, depth + 1);
599 break;
600 default:
601 NOTREACHED() << "Unsupported MenuModel::ItemType " << item.type;
602 break;
605 item_info.fMask |= MIIM_STATE;
606 item_info.fState =
607 (item.checked ? MFS_CHECKED : MFS_UNCHECKED) |
608 (item.enabled ? MFS_ENABLED : (MFS_DISABLED | MFS_GRAYED));
610 InsertMenuItem(menu, i, TRUE, &item_info);
613 return menu;
616 } // namespace
618 HMENU BuildContextMenu(const ContextMenuModel& menu_model) {
619 return BuildContextMenuImpl(&menu_model, 0);
622 std::string ResolveURL(const std::string& document,
623 const std::string& relative) {
624 if (document.empty()) {
625 return GURL(relative).spec();
626 } else {
627 return GURL(document).Resolve(relative).spec();
631 bool HaveSameOrigin(const std::string& url1, const std::string& url2) {
632 GURL a(url1), b(url2);
633 bool ret;
634 if (a.is_valid() != b.is_valid()) {
635 // Either (but not both) url is invalid, so they can't match.
636 ret = false;
637 } else if (!a.is_valid()) {
638 // Both URLs are invalid (see first check). Just check if the opaque
639 // strings match exactly.
640 ret = url1.compare(url2) == 0;
641 } else if (a.GetOrigin() != b.GetOrigin()) {
642 // The origins don't match.
643 ret = false;
644 } else {
645 // we have a match.
646 ret = true;
649 return ret;
652 int GetConfigInt(int default_value, const wchar_t* value_name) {
653 int ret = default_value;
654 RegKey config_key;
655 if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey,
656 KEY_QUERY_VALUE) == ERROR_SUCCESS) {
657 config_key.ReadValueDW(value_name, reinterpret_cast<DWORD*>(&ret));
660 return ret;
663 bool GetConfigBool(bool default_value, const wchar_t* value_name) {
664 DWORD value = GetConfigInt(default_value, value_name);
665 return (value != FALSE);
668 bool SetConfigInt(const wchar_t* value_name, int value) {
669 RegKey config_key;
670 if (config_key.Create(HKEY_CURRENT_USER, kChromeFrameConfigKey,
671 KEY_SET_VALUE) == ERROR_SUCCESS) {
672 if (config_key.WriteValue(value_name, value) == ERROR_SUCCESS) {
673 return true;
677 return false;
680 bool SetConfigBool(const wchar_t* value_name, bool value) {
681 return SetConfigInt(value_name, value);
684 bool DeleteConfigValue(const wchar_t* value_name) {
685 RegKey config_key;
686 if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey,
687 KEY_WRITE) == ERROR_SUCCESS) {
688 if (config_key.DeleteValue(value_name) == ERROR_SUCCESS) {
689 return true;
692 return false;
695 bool IsGcfDefaultRenderer() {
696 DWORD is_default = 0; // NOLINT
698 // First check policy settings
699 PolicySettings::RendererForUrl renderer =
700 PolicySettings::GetInstance()->default_renderer();
701 if (renderer != PolicySettings::RENDERER_NOT_SPECIFIED) {
702 is_default = (renderer == PolicySettings::RENDER_IN_CHROME_FRAME);
703 } else {
704 // TODO(tommi): Implement caching for this config value as it gets
705 // checked frequently.
706 RegKey config_key;
707 if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey,
708 KEY_READ) == ERROR_SUCCESS) {
709 config_key.ReadValueDW(kEnableGCFRendererByDefault, &is_default);
713 return is_default != 0;
716 RendererType RendererTypeForUrl(const std::wstring& url) {
717 // First check if the default renderer settings are specified by policy.
718 // If so, then that overrides the user settings.
719 PolicySettings::RendererForUrl renderer =
720 PolicySettings::GetInstance()->GetRendererForUrl(url.c_str());
721 if (renderer != PolicySettings::RENDERER_NOT_SPECIFIED) {
722 // We may know at this point that policy says do NOT render in Chrome Frame.
723 // To maintain consistency, we return RENDERER_TYPE_UNDETERMINED so that
724 // content sniffing, etc. still take place.
725 // TODO(tommi): Clarify the intent here.
726 return (renderer == PolicySettings::RENDER_IN_CHROME_FRAME) ?
727 RENDERER_TYPE_CHROME_OPT_IN_URL : RENDERER_TYPE_UNDETERMINED;
730 // TODO(robertshield): Move this into a holder-type class that listens
731 // for reg change events as well.
732 static int render_in_cf_by_default = FALSE;
734 RegistryListPreferencesHolder& render_type_for_url_holder =
735 g_render_type_for_url_holder.Get();
736 if (!render_type_for_url_holder.Valid()) {
737 const wchar_t* url_list_name = kRenderInGCFUrlList;
738 if (IsGcfDefaultRenderer()) {
739 url_list_name = kRenderInHostUrlList;
740 render_in_cf_by_default = TRUE;
741 } else {
742 render_in_cf_by_default = FALSE;
745 render_type_for_url_holder.Init(HKEY_CURRENT_USER,
746 kChromeFrameConfigKey,
747 url_list_name);
749 DCHECK(render_type_for_url_holder.Valid());
751 RendererType renderer_type =
752 render_in_cf_by_default ? RENDERER_TYPE_CHROME_DEFAULT_RENDERER :
753 RENDERER_TYPE_UNDETERMINED;
755 if (render_type_for_url_holder.ListMatches(url)) {
756 renderer_type = render_in_cf_by_default ?
757 RENDERER_TYPE_UNDETERMINED :
758 RENDERER_TYPE_CHROME_OPT_IN_URL;
761 return renderer_type;
764 bool ShouldRemoveUAForUrl(const string16& url) {
765 // TODO(robertshield): Wire up the stuff in PolicySettings here so the value
766 // can be specified via group policy.
767 // TODO(robertshield): Add a default list of exclusions here for site with
768 // known bad UA parsing.
769 RegistryListPreferencesHolder& user_agent_filter_holder =
770 g_user_agent_filter_holder.Get();
771 if (!user_agent_filter_holder.Valid()) {
772 user_agent_filter_holder.Init(HKEY_CURRENT_USER,
773 kChromeFrameConfigKey,
774 kExcludeUAFromDomainList);
776 DCHECK(user_agent_filter_holder.Valid());
778 return user_agent_filter_holder.ListMatches(url);
781 RegistryListPreferencesHolder& GetRendererTypePreferencesHolderForTesting() {
782 return g_render_type_for_url_holder.Get();
785 RegistryListPreferencesHolder& GetUserAgentPreferencesHolderForTesting() {
786 return g_user_agent_filter_holder.Get();
789 HRESULT NavigateBrowserToMoniker(IUnknown* browser, IMoniker* moniker,
790 const wchar_t* headers, IBindCtx* bind_ctx,
791 const wchar_t* fragment, IStream* post_data,
792 VARIANT* flags) {
793 DCHECK(browser);
794 DCHECK(moniker);
795 DCHECK(bind_ctx);
797 base::win::ScopedComPtr<IWebBrowser2> web_browser2;
798 HRESULT hr = DoQueryService(SID_SWebBrowserApp, browser,
799 web_browser2.Receive());
800 DCHECK(web_browser2);
801 DLOG_IF(WARNING, FAILED(hr)) << base::StringPrintf(L"SWebBrowserApp 0x%08X",
802 hr);
803 if (FAILED(hr))
804 return hr;
806 // If the data to be downloaded was received in response to a post request
807 // then we need to reissue the post request.
808 base::win::ScopedVariant post_data_variant;
809 if (post_data) {
810 RewindStream(post_data);
812 CComSafeArray<uint8> safe_array_post;
814 STATSTG stat;
815 post_data->Stat(&stat, STATFLAG_NONAME);
817 if (stat.cbSize.LowPart > 0) {
818 std::string data;
820 HRESULT hr = E_FAIL;
821 while ((hr = ReadStream(post_data, 0xffff, &data)) == S_OK) {
822 safe_array_post.Add(
823 data.size(),
824 reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())));
825 data.clear();
827 } else {
828 // If we get here it means that the navigation is being reissued for a
829 // POST request with no data. To ensure that the new window used as a
830 // target to handle the new navigation issues a POST request
831 // we need valid POST data. In this case we create a dummy 1 byte array.
832 // May not work as expected with some web sites.
833 DLOG(WARNING) << "Reissuing navigation with empty POST data. May not"
834 << " work as expected";
835 safe_array_post.Create(1);
837 post_data_variant.Set(safe_array_post.Detach());
839 // Create a new bind context that's not associated with our callback.
840 // Calling RevokeBindStatusCallback doesn't disassociate the callback with
841 // the bind context in IE7. The returned bind context has the same
842 // implementation of GetRunningObjectTable as the bind context we held which
843 // basically delegates to ole32's GetRunningObjectTable. The object table
844 // is then used to determine if the moniker is already running and via
845 // that mechanism is associated with the same internet request as has already
846 // been issued.
848 // TODO(tommi): See if we can get HlinkSimpleNavigateToMoniker to work
849 // instead. Looks like we'll need to support IHTMLDocument2 (get_URL in
850 // particular), access to IWebBrowser2 etc.
851 // HlinkSimpleNavigateToMoniker(moniker, url, NULL, host, bind_context,
852 // NULL, 0, 0);
854 base::win::ScopedComPtr<IUriContainer> uri_container;
855 hr = uri_container.QueryFrom(moniker);
857 base::win::ScopedVariant headers_var;
858 if (headers && headers[0])
859 headers_var.Set(headers);
861 if (uri_container) {
862 // IE7 and IE8.
863 const IID* interface_ids[] = {
864 &IID_IWebBrowserPriv2IE7,
865 &IID_IWebBrowserPriv2IE8,
866 &IID_IWebBrowserPriv2IE8XP,
867 &IID_IWebBrowserPriv2IE8XPBeta,
870 base::win::ScopedComPtr<IWebBrowserPriv2Common, NULL> browser_priv2;
871 for (int i = 0; i < arraysize(interface_ids) && browser_priv2 == NULL;
872 ++i) {
873 hr = web_browser2.QueryInterface(*interface_ids[i],
874 reinterpret_cast<void**>(browser_priv2.Receive()));
877 DCHECK(browser_priv2);
879 if (browser_priv2) {
880 base::win::ScopedComPtr<IUri> uri_obj;
881 uri_container->GetIUri(uri_obj.Receive());
882 DCHECK(uri_obj);
884 if (GetIEVersion() < IE_9) {
885 hr = browser_priv2->NavigateWithBindCtx2(
886 uri_obj, flags, NULL, post_data_variant.AsInput(),
887 headers_var.AsInput(), bind_ctx,
888 const_cast<wchar_t*>(fragment));
889 } else {
890 IWebBrowserPriv2CommonIE9* browser_priv2_ie9 =
891 reinterpret_cast<IWebBrowserPriv2CommonIE9*>(browser_priv2.get());
892 hr = browser_priv2_ie9->NavigateWithBindCtx2(
893 uri_obj, flags, NULL, post_data_variant.AsInput(),
894 headers_var.AsInput(), bind_ctx,
895 const_cast<wchar_t*>(fragment), 0);
897 DLOG_IF(WARNING, FAILED(hr))
898 << base::StringPrintf(L"NavigateWithBindCtx2 0x%08X", hr);
900 } else {
901 // IE6
902 LPOLESTR url = NULL;
903 if (SUCCEEDED(hr = moniker->GetDisplayName(bind_ctx, NULL, &url))) {
904 DVLOG(1) << __FUNCTION__ << " " << url;
905 base::win::ScopedComPtr<IWebBrowserPriv> browser_priv;
906 if (SUCCEEDED(hr = browser_priv.QueryFrom(web_browser2))) {
907 GURL target_url(url);
908 // On IE6 if the original URL has a fragment then the navigation
909 // attempt is ignored. To workaround this we strip the fragment from
910 // the url and initiate the navigation. When the active document loads
911 // we retrieve the original url with the fragment from the Navigation
912 // manager and use it.
913 if (target_url.has_ref()) {
914 url_parse::Component comp;
915 GURL::Replacements replacements;
916 replacements.SetRef("", comp);
918 target_url = target_url.ReplaceComponents(replacements);
919 fragment = NULL;
922 base::win::ScopedVariant var_url(UTF8ToWide(target_url.spec()).c_str());
923 hr = browser_priv->NavigateWithBindCtx(var_url.AsInput(), flags, NULL,
924 post_data_variant.AsInput(),
925 headers_var.AsInput(), bind_ctx,
926 const_cast<wchar_t*>(fragment));
927 DLOG_IF(WARNING, FAILED(hr))
928 << base::StringPrintf(L"NavigateWithBindCtx 0x%08X", hr);
929 } else {
930 NOTREACHED();
932 ::CoTaskMemFree(url);
933 } else {
934 DLOG(ERROR) << base::StringPrintf("GetDisplayName: 0x%08X", hr);
938 return hr;
941 void MarkBrowserOnThreadForCFNavigation(IBrowserService* browser) {
942 DCHECK(browser != NULL);
943 DCHECK(g_tls_browser_for_cf_navigation.Pointer()->Get() == NULL ||
944 g_tls_browser_for_cf_navigation.Pointer()->Get() == browser);
945 g_tls_browser_for_cf_navigation.Pointer()->Set(browser);
948 bool CheckForCFNavigation(IBrowserService* browser, bool clear_flag) {
949 DCHECK(browser);
950 bool ret = (g_tls_browser_for_cf_navigation.Pointer()->Get() == browser);
951 if (ret && clear_flag)
952 g_tls_browser_for_cf_navigation.Pointer()->Set(NULL);
953 return ret;
956 bool IsValidUrlScheme(const GURL& url, bool is_privileged) {
957 if (url.is_empty())
958 return false;
960 if (url.SchemeIs(chrome::kHttpScheme) ||
961 url.SchemeIs(chrome::kHttpsScheme) ||
962 url.SchemeIs(chrome::kAboutScheme))
963 return true;
965 // Additional checking for view-source. Allow only http and https
966 // URLs in view source.
967 if (url.SchemeIs(content::kViewSourceScheme)) {
968 GURL sub_url(url.path());
969 if (sub_url.SchemeIs(chrome::kHttpScheme) ||
970 sub_url.SchemeIs(chrome::kHttpsScheme))
971 return true;
972 else
973 return false;
976 if (is_privileged &&
977 (url.SchemeIs(chrome::kDataScheme) ||
978 url.SchemeIs(extensions::kExtensionScheme)))
979 return true;
981 return false;
984 std::string GetRawHttpHeaders(IWinInetHttpInfo* info) {
985 DCHECK(info);
987 std::string buffer;
989 DWORD size = 0;
990 DWORD flags = 0;
991 DWORD reserved = 0;
992 HRESULT hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &size,
993 &flags, &reserved);
994 if (!size) {
995 DLOG(WARNING) << "Failed to query HTTP headers size. Error: " << hr;
996 } else {
997 buffer.resize(size + 1);
998 hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, &buffer[0],
999 &size, &flags, &reserved);
1000 if (FAILED(hr)) {
1001 DLOG(WARNING) << "Failed to query HTTP headers. Error: " << hr;
1005 return buffer;
1008 bool IsSubFrameRequest(IUnknown* service_provider) {
1009 DCHECK(service_provider);
1011 // We need to be able to get at an IWebBrowser2 if we are to decide whether
1012 // this request originates from a non-top-level frame.
1013 base::win::ScopedComPtr<IWebBrowser2> web_browser;
1014 HRESULT hr = DoQueryService(IID_ITargetFrame2, service_provider,
1015 web_browser.Receive());
1017 bool is_sub_frame_request = false;
1018 if (web_browser) {
1019 // Now check to see if we are in a sub-frame.
1020 base::win::ScopedComPtr<IHTMLWindow2> current_frame, parent_frame;
1021 hr = DoQueryService(IID_IHTMLWindow2, service_provider,
1022 current_frame.Receive());
1023 if (current_frame) {
1024 // Only the top level window will return self when get_parent is called.
1025 current_frame->get_parent(parent_frame.Receive());
1026 if (parent_frame != current_frame) {
1027 DVLOG(1) << "Sub frame detected";
1028 is_sub_frame_request = true;
1031 } else {
1032 DVLOG(1) << "IsSubFrameRequest - no IWebBrowser2";
1033 is_sub_frame_request = true;
1036 return is_sub_frame_request;
1039 bool IsHeadlessMode() {
1040 bool headless = GetConfigBool(false, kChromeFrameHeadlessMode);
1041 return headless;
1044 bool IsAccessibleMode() {
1045 bool accessible = GetConfigBool(false, kChromeFrameAccessibleMode);
1046 return accessible;
1049 bool IsUnpinnedMode() {
1050 // We only check this value once and then cache it since changing the registry
1051 // once we've pinned the DLL won't have any effect.
1052 static bool unpinned = GetConfigBool(false, kChromeFrameUnpinnedMode);
1053 return unpinned;
1056 std::wstring GetActualUrlFromMoniker(IMoniker* moniker,
1057 IBindCtx* bind_context,
1058 const std::wstring& bho_url) {
1059 CComHeapPtr<WCHAR> display_name;
1060 moniker->GetDisplayName(bind_context, NULL, &display_name);
1061 std::wstring moniker_url = display_name;
1063 GURL parsed_url(WideToUTF8(bho_url));
1064 if (!parsed_url.has_ref())
1065 return moniker_url;
1067 if (StartsWith(bho_url, moniker_url, false) &&
1068 bho_url[moniker_url.length()] == L'#')
1069 return bho_url;
1071 return moniker_url;
1074 bool IsTopLevelWindow(HWND window) {
1075 long style = GetWindowLong(window, GWL_STYLE); // NOLINT
1076 if (!(style & WS_CHILD))
1077 return true;
1079 HWND parent = GetParent(window);
1080 return !parent || (parent == GetDesktopWindow());
1083 HRESULT RewindStream(IStream* stream) {
1084 HRESULT hr = E_POINTER;
1085 if (stream) {
1086 LARGE_INTEGER zero = {0};
1087 ULARGE_INTEGER new_pos = {0};
1088 hr = stream->Seek(zero, STREAM_SEEK_SET, &new_pos);
1091 return hr;
1094 std::wstring GuidToString(const GUID& guid) {
1095 std::wstring ret;
1096 ::StringFromGUID2(guid, WriteInto(&ret, 39), 39);
1097 return ret;
1100 int32 MapCookieStateToCookieAction(InternetCookieState cookie_state) {
1101 int32 cookie_action = COOKIEACTION_NONE;
1103 switch (cookie_state) {
1104 case COOKIE_STATE_UNKNOWN:
1105 cookie_action = COOKIEACTION_NONE;
1106 break;
1107 case COOKIE_STATE_ACCEPT:
1108 cookie_action = COOKIEACTION_ACCEPT;
1109 break;
1110 case COOKIE_STATE_LEASH:
1111 cookie_action = COOKIEACTION_LEASH;
1112 break;
1113 case COOKIE_STATE_DOWNGRADE:
1114 cookie_action = COOKIEACTION_DOWNGRADE;
1115 break;
1116 case COOKIE_STATE_REJECT:
1117 cookie_action = COOKIEACTION_REJECT;
1118 break;
1119 default:
1120 cookie_action = COOKIEACTION_REJECT;
1121 break;
1123 return cookie_action;
1126 GURL GetUrlWithoutFragment(const wchar_t* url) {
1127 GURL parsed_url(url);
1129 if (parsed_url.has_ref()) {
1130 url_parse::Component comp;
1131 GURL::Replacements replacements;
1132 replacements.SetRef("", comp);
1134 parsed_url = parsed_url.ReplaceComponents(replacements);
1136 return parsed_url;
1139 bool CompareUrlsWithoutFragment(const wchar_t* url1, const wchar_t* url2) {
1140 GURL parsed_url1 = GetUrlWithoutFragment(url1);
1141 GURL parsed_url2 = GetUrlWithoutFragment(url2);
1142 return parsed_url1 == parsed_url2;
1145 std::string FindReferrerFromHeaders(const wchar_t* headers,
1146 const wchar_t* additional_headers) {
1147 std::string referrer;
1149 const wchar_t* both_headers[] = { headers, additional_headers };
1150 for (int i = 0; referrer.empty() && i < arraysize(both_headers); ++i) {
1151 if (!both_headers[i])
1152 continue;
1153 std::string raw_headers_utf8 = WideToUTF8(both_headers[i]);
1154 net::HttpUtil::HeadersIterator it(raw_headers_utf8.begin(),
1155 raw_headers_utf8.end(), "\r\n");
1156 while (it.GetNext()) {
1157 if (LowerCaseEqualsASCII(it.name(), "referer")) {
1158 referrer = it.values();
1159 break;
1164 return referrer;
1167 std::string GetHttpHeadersFromBinding(IBinding* binding) {
1168 if (binding == NULL) {
1169 DLOG(WARNING) << "GetHttpResponseStatus - no binding_";
1170 return std::string();
1173 base::win::ScopedComPtr<IWinInetHttpInfo> info;
1174 if (FAILED(info.QueryFrom(binding))) {
1175 DLOG(WARNING) << "Failed to QI for IWinInetHttpInfo";
1176 return std::string();
1179 return GetRawHttpHeaders(info);
1182 int GetHttpResponseStatusFromBinding(IBinding* binding) {
1183 DVLOG(1) << __FUNCTION__;
1184 if (binding == NULL) {
1185 DLOG(WARNING) << "GetHttpResponseStatus - no binding_";
1186 return 0;
1189 int http_status = 0;
1191 base::win::ScopedComPtr<IWinInetHttpInfo> info;
1192 if (SUCCEEDED(info.QueryFrom(binding))) {
1193 char status[10] = {0};
1194 DWORD buf_size = sizeof(status);
1195 DWORD flags = 0;
1196 DWORD reserved = 0;
1197 if (SUCCEEDED(info->QueryInfo(HTTP_QUERY_STATUS_CODE, status, &buf_size,
1198 &flags, &reserved))) {
1199 base::StringToInt(status, &http_status);
1200 } else {
1201 NOTREACHED() << "Failed to get HTTP status";
1203 } else {
1204 NOTREACHED() << "failed to get IWinInetHttpInfo from binding_";
1207 return http_status;
1210 CLIPFORMAT GetTextHtmlClipboardFormat() {
1211 static const CLIPFORMAT text_html = RegisterClipboardFormat(CFSTR_MIME_HTML);
1212 return text_html;
1215 bool IsTextHtmlMimeType(const wchar_t* mime_type) {
1216 return IsTextHtmlClipFormat(RegisterClipboardFormatW(mime_type));
1219 bool IsTextHtmlClipFormat(CLIPFORMAT cf) {
1220 return cf == GetTextHtmlClipboardFormat();
1223 bool IsSystemProcess() {
1224 bool is_system = false;
1225 CAccessToken process_token;
1226 if (process_token.GetProcessToken(TOKEN_QUERY, GetCurrentProcess())) {
1227 CSid logon_sid;
1228 if (process_token.GetUser(&logon_sid)) {
1229 is_system = logon_sid == Sids::System();
1232 return is_system;
1236 std::string BindStatus2Str(ULONG bind_status) {
1237 std::string s;
1238 static const char* const bindstatus_txt[] = {
1239 "BINDSTATUS_FINDINGRESOURCE",
1240 "BINDSTATUS_CONNECTING",
1241 "BINDSTATUS_REDIRECTING",
1242 "BINDSTATUS_BEGINDOWNLOADDATA",
1243 "BINDSTATUS_DOWNLOADINGDATA",
1244 "BINDSTATUS_ENDDOWNLOADDATA",
1245 "BINDSTATUS_BEGINDOWNLOADCOMPONENTS",
1246 "BINDSTATUS_INSTALLINGCOMPONENTS",
1247 "BINDSTATUS_ENDDOWNLOADCOMPONENTS",
1248 "BINDSTATUS_USINGCACHEDCOPY",
1249 "BINDSTATUS_SENDINGREQUEST",
1250 "BINDSTATUS_CLASSIDAVAILABLE",
1251 "BINDSTATUS_MIMETYPEAVAILABLE",
1252 "BINDSTATUS_CACHEFILENAMEAVAILABLE",
1253 "BINDSTATUS_BEGINSYNCOPERATION",
1254 "BINDSTATUS_ENDSYNCOPERATION",
1255 "BINDSTATUS_BEGINUPLOADDATA",
1256 "BINDSTATUS_UPLOADINGDATA",
1257 "BINDSTATUS_ENDUPLOADINGDATA",
1258 "BINDSTATUS_PROTOCOLCLASSID",
1259 "BINDSTATUS_ENCODING",
1260 "BINDSTATUS_VERFIEDMIMETYPEAVAILABLE",
1261 "BINDSTATUS_CLASSINSTALLLOCATION",
1262 "BINDSTATUS_DECODING",
1263 "BINDSTATUS_LOADINGMIMEHANDLER",
1264 "BINDSTATUS_CONTENTDISPOSITIONATTACH",
1265 "BINDSTATUS_FILTERREPORTMIMETYPE",
1266 "BINDSTATUS_CLSIDCANINSTANTIATE",
1267 "BINDSTATUS_IUNKNOWNAVAILABLE",
1268 "BINDSTATUS_DIRECTBIND",
1269 "BINDSTATUS_RAWMIMETYPE",
1270 "BINDSTATUS_PROXYDETECTING",
1271 "BINDSTATUS_ACCEPTRANGES",
1272 "BINDSTATUS_COOKIE_SENT",
1273 "BINDSTATUS_COMPACT_POLICY_RECEIVED",
1274 "BINDSTATUS_COOKIE_SUPPRESSED",
1275 "BINDSTATUS_COOKIE_STATE_UNKNOWN",
1276 "BINDSTATUS_COOKIE_STATE_ACCEPT",
1277 "BINDSTATUS_COOKIE_STATE_REJECT",
1278 "BINDSTATUS_COOKIE_STATE_PROMPT",
1279 "BINDSTATUS_COOKIE_STATE_LEASH",
1280 "BINDSTATUS_COOKIE_STATE_DOWNGRADE",
1281 "BINDSTATUS_POLICY_HREF",
1282 "BINDSTATUS_P3P_HEADER",
1283 "BINDSTATUS_SESSION_COOKIE_RECEIVED",
1284 "BINDSTATUS_PERSISTENT_COOKIE_RECEIVED",
1285 "BINDSTATUS_SESSION_COOKIES_ALLOWED",
1286 "BINDSTATUS_CACHECONTROL",
1287 "BINDSTATUS_CONTENTDISPOSITIONFILENAME",
1288 "BINDSTATUS_MIMETEXTPLAINMISMATCH",
1289 "BINDSTATUS_PUBLISHERAVAILABLE",
1290 "BINDSTATUS_DISPLAYNAMEAVAILABLE",
1291 "BINDSTATUS_SSLUX_NAVBLOCKED",
1292 "BINDSTATUS_SERVER_MIMETYPEAVAILABLE",
1293 "BINDSTATUS_SNIFFED_CLASSIDAVAILABLE",
1294 "BINDSTATUS_64BIT_PROGRESS"
1296 if (bind_status >= 1 && bind_status <= BINDSTATUS_64BIT_PROGRESS)
1297 s = bindstatus_txt[bind_status - 1];
1298 else
1299 s = base::StringPrintf("UnDoc[%#x]", bind_status);
1300 return s;
1303 std::string PiFlags2Str(DWORD flags) {
1304 #define ADD_PI_FLAG(x) \
1305 if (flags & x) { \
1306 s.append(#x ## " "); \
1307 flags &= ~x; \
1310 std::string s = " flags ";
1311 ADD_PI_FLAG(PI_PARSE_URL);
1312 ADD_PI_FLAG(PI_FILTER_MODE);
1313 ADD_PI_FLAG(PI_FORCE_ASYNC);
1314 ADD_PI_FLAG(PI_USE_WORKERTHREAD);
1315 ADD_PI_FLAG(PI_MIMEVERIFICATION);
1316 ADD_PI_FLAG(PI_CLSIDLOOKUP);
1317 ADD_PI_FLAG(PI_DATAPROGRESS);
1318 ADD_PI_FLAG(PI_SYNCHRONOUS);
1319 ADD_PI_FLAG(PI_APARTMENTTHREADED);
1320 ADD_PI_FLAG(PI_CLASSINSTALL);
1321 ADD_PI_FLAG(PI_PASSONBINDCTX);
1322 ADD_PI_FLAG(PI_NOMIMEHANDLER);
1323 ADD_PI_FLAG(PI_LOADAPPDIRECT);
1324 ADD_PI_FLAG(PD_FORCE_SWITCH);
1325 ADD_PI_FLAG(PI_PREFERDEFAULTHANDLER);
1327 if (flags)
1328 s += base::StringPrintf("+UnDoc[%#x]", flags);
1329 return s;
1330 #undef ADD_PI_FLAG
1333 std::string Bscf2Str(DWORD flags) {
1334 #define ADD_BSCF_FLAG(x) \
1335 if (flags & x) {\
1336 s.append(#x ## " "); \
1337 flags &= ~x; \
1340 std::string s = " flags ";
1341 ADD_BSCF_FLAG(BSCF_FIRSTDATANOTIFICATION)
1342 ADD_BSCF_FLAG(BSCF_INTERMEDIATEDATANOTIFICATION)
1343 ADD_BSCF_FLAG(BSCF_LASTDATANOTIFICATION)
1344 ADD_BSCF_FLAG(BSCF_DATAFULLYAVAILABLE)
1345 ADD_BSCF_FLAG(BSCF_AVAILABLEDATASIZEUNKNOWN)
1346 ADD_BSCF_FLAG(BSCF_SKIPDRAINDATAFORFILEURLS)
1347 ADD_BSCF_FLAG(BSCF_64BITLENGTHDOWNLOAD)
1349 if (flags)
1350 s += base::StringPrintf("+UnDoc[%#x]", flags);
1351 return s;
1352 #undef ADD_BSCF_FLAG
1355 // Reads data from a stream into a string.
1356 HRESULT ReadStream(IStream* stream, size_t size, std::string* data) {
1357 DCHECK(stream);
1358 DCHECK_GT(size, 0u);
1359 DCHECK(data);
1361 DWORD read = 0;
1362 HRESULT hr = stream->Read(WriteInto(data, size + 1), size, &read);
1363 DCHECK(hr == S_OK || hr == S_FALSE || hr == E_PENDING);
1364 if (read) {
1365 data->erase(read);
1366 DCHECK_EQ(read, data->length());
1367 } else {
1368 data->clear();
1369 // Return S_FALSE if the underlying stream returned S_OK and zero bytes.
1370 if (hr == S_OK)
1371 hr = S_FALSE;
1374 return hr;
1377 ChromeFrameUrl::ChromeFrameUrl() {
1378 Reset();
1381 bool ChromeFrameUrl::Parse(const std::wstring& url) {
1382 Reset();
1383 parsed_url_ = GURL(url);
1385 if (parsed_url_.is_empty())
1386 return false;
1388 is_chrome_protocol_ = parsed_url_.SchemeIs(kGCFProtocol);
1389 if (is_chrome_protocol_) {
1390 parsed_url_ = GURL(url.c_str() + lstrlen(kChromeProtocolPrefix));
1391 return true;
1394 return ParseAttachExternalTabUrl();
1397 bool ChromeFrameUrl::ParseAttachExternalTabUrl() {
1398 std::string query = parsed_url_.query();
1399 if (!StartsWithASCII(query, kAttachExternalTabPrefix, false)) {
1400 return parsed_url_.is_valid();
1403 attach_to_external_tab_ = true;
1404 base::StringTokenizer tokenizer(query, "&");
1405 // Skip over kChromeAttachExternalTabPrefix
1406 tokenizer.GetNext();
1407 // Read the following items in order.
1408 // 1. cookie
1409 // 2. disposition
1410 // 3. dimension.x
1411 // 4. dimension.y
1412 // 5. dimension.width
1413 // 6. dimension.height.
1414 if (tokenizer.GetNext()) {
1415 char* end_ptr = 0;
1416 cookie_ = _strtoui64(tokenizer.token().c_str(), &end_ptr, 10);
1417 } else {
1418 return false;
1421 if (tokenizer.GetNext()) {
1422 disposition_ = atoi(tokenizer.token().c_str());
1423 } else {
1424 return false;
1427 if (tokenizer.GetNext()) {
1428 dimensions_.set_x(atoi(tokenizer.token().c_str()));
1429 } else {
1430 return false;
1433 if (tokenizer.GetNext()) {
1434 dimensions_.set_y(atoi(tokenizer.token().c_str()));
1435 } else {
1436 return false;
1439 if (tokenizer.GetNext()) {
1440 dimensions_.set_width(atoi(tokenizer.token().c_str()));
1441 } else {
1442 return false;
1445 if (tokenizer.GetNext()) {
1446 dimensions_.set_height(atoi(tokenizer.token().c_str()));
1447 } else {
1448 return false;
1451 if (tokenizer.GetNext()) {
1452 profile_name_ = tokenizer.token();
1453 // Escape out special characters like %20, etc.
1454 profile_name_ = net::UnescapeURLComponent(profile_name_,
1455 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS);
1456 } else {
1457 return false;
1460 return true;
1463 void ChromeFrameUrl::Reset() {
1464 attach_to_external_tab_ = false;
1465 is_chrome_protocol_ = false;
1466 cookie_ = 0;
1467 dimensions_.SetRect(0, 0, 0, 0);
1468 disposition_ = 0;
1469 profile_name_.clear();
1472 bool CanNavigate(const GURL& url,
1473 NavigationConstraints* navigation_constraints) {
1474 if (!url.is_valid()) {
1475 DLOG(ERROR) << "Invalid URL passed to InitiateNavigation: " << url;
1476 return false;
1479 if (!navigation_constraints) {
1480 NOTREACHED() << "Invalid NavigationConstraints passed in";
1481 return false;
1484 // No sanity checks if unsafe URLs are allowed
1485 if (navigation_constraints->AllowUnsafeUrls())
1486 return true;
1488 if (!navigation_constraints->IsSchemeAllowed(url)) {
1489 DLOG(WARNING) << __FUNCTION__ << " Disallowing navigation to url: " << url;
1490 return false;
1493 if (!navigation_constraints->IsZoneAllowed(url)) {
1494 DLOG(WARNING) << __FUNCTION__
1495 << " Disallowing navigation to restricted url: " << url;
1496 return false;
1498 return true;
1501 void WaitWithMessageLoop(HANDLE* handles, int count, DWORD timeout) {
1502 base::Time now = base::Time::Now();
1503 base::Time wait_until = now + base::TimeDelta::FromMilliseconds(timeout);
1505 while (wait_until >= now) {
1506 base::TimeDelta wait_time = wait_until - now;
1507 DWORD wait = MsgWaitForMultipleObjects(
1508 count, handles, FALSE, static_cast<DWORD>(wait_time.InMilliseconds()),
1509 QS_ALLINPUT);
1510 switch (wait) {
1511 case WAIT_OBJECT_0:
1512 case WAIT_TIMEOUT:
1513 return;
1515 case WAIT_OBJECT_0 + 1: {
1516 MSG msg = {0};
1517 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1518 TranslateMessage(&msg);
1519 DispatchMessage(&msg);
1521 break;
1524 default: {
1525 NOTREACHED() << "Unexpected return from MsgWaitForMultipleObjects :"
1526 << wait;
1527 return;
1530 now = base::Time::Now();
1534 // Returns -1 if no directive is found, std::numeric_limits<int>::max() if the
1535 // directive matches all IE versions ('Chrome=1') or the maximum IE version
1536 // matched ('Chrome=IE7' => 7)
1537 int GetXUaCompatibleDirective(const std::string& directive, char delimiter) {
1538 net::HttpUtil::NameValuePairsIterator name_value_pairs(directive.begin(),
1539 directive.end(),
1540 delimiter);
1542 // Loop through the values until a valid 'Chrome=<FILTER>' entry is found
1543 while (name_value_pairs.GetNext()) {
1544 if (!LowerCaseEqualsASCII(name_value_pairs.name_begin(),
1545 name_value_pairs.name_end(),
1546 "chrome")) {
1547 continue;
1549 std::string::const_iterator filter_begin = name_value_pairs.value_begin();
1550 std::string::const_iterator filter_end = name_value_pairs.value_end();
1552 size_t filter_length = filter_end - filter_begin;
1554 if (filter_length == 1 && *filter_begin == '1') {
1555 return std::numeric_limits<int>::max();
1558 if (filter_length < 3 ||
1559 !LowerCaseEqualsASCII(filter_begin, filter_begin + 2, "ie") ||
1560 !isdigit(*(filter_begin + 2))) { // ensure no leading +/-
1561 continue;
1564 int header_ie_version = 0;
1565 if (!base::StringToInt(base::StringPiece(filter_begin + 2,
1566 filter_end),
1567 &header_ie_version) ||
1568 header_ie_version == 0) { // ensure it's not a sequence of 0's
1569 continue;
1572 // The first valid directive we find wins, whether it matches or not
1573 return header_ie_version;
1575 return -1;
1578 bool CheckXUaCompatibleDirective(const std::string& directive,
1579 int ie_major_version) {
1580 int header_ie_version = GetXUaCompatibleDirective(directive, ';');
1581 if (header_ie_version == -1) {
1582 header_ie_version = GetXUaCompatibleDirective(directive, ',');
1584 return header_ie_version >= ie_major_version;
1587 void EnumerateKeyValues(HKEY parent_key, const wchar_t* sub_key_name,
1588 std::vector<std::wstring>* values) {
1589 DCHECK(values);
1590 base::win::RegistryValueIterator url_list(parent_key, sub_key_name);
1591 while (url_list.Valid()) {
1592 values->push_back(url_list.Value());
1593 ++url_list;
1597 std::wstring GetCurrentModuleVersion() {
1598 scoped_ptr<FileVersionInfo> module_version_info(
1599 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
1600 DCHECK(module_version_info.get() != NULL);
1601 return module_version_info->file_version();
1604 bool IsChromeFrameDocument(IWebBrowser2* web_browser) {
1605 if (!web_browser)
1606 return false;
1608 base::win::ScopedComPtr<IDispatch> doc;
1609 web_browser->get_Document(doc.Receive());
1610 if (doc) {
1611 // Detect if CF is rendering based on whether the document is a
1612 // ChromeActiveDocument. Detecting based on hwnd is problematic as
1613 // the CF Active Document window may not have been created yet.
1614 base::win::ScopedComPtr<IChromeFrame> chrome_frame;
1615 chrome_frame.QueryFrom(doc);
1616 return chrome_frame.get() != NULL;
1618 return false;
1621 bool IncreaseWinInetConnections(DWORD connections) {
1622 static bool wininet_connection_count_updated = false;
1623 if (wininet_connection_count_updated) {
1624 return true;
1627 static int connection_options[] = {
1628 INTERNET_OPTION_MAX_CONNS_PER_SERVER,
1629 INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER,
1632 BOOL ret = FALSE;
1634 for (int option_index = 0; option_index < arraysize(connection_options);
1635 ++option_index) {
1636 DWORD connection_value_size = sizeof(DWORD);
1637 DWORD current_connection_limit = 0;
1638 InternetQueryOption(NULL, connection_options[option_index],
1639 &current_connection_limit, &connection_value_size);
1640 if (current_connection_limit > connections) {
1641 continue;
1644 ret = InternetSetOption(NULL, connection_options[option_index],
1645 &connections, connection_value_size);
1646 if (!ret) {
1647 return false;
1650 wininet_connection_count_updated = true;
1651 return true;
1654 void GetChromeFrameProfilePath(const string16& profile_name,
1655 base::FilePath* profile_path) {
1656 chrome::GetChromeFrameUserDataDirectory(profile_path);
1657 *profile_path = profile_path->Append(profile_name);
1658 DVLOG(1) << __FUNCTION__ << ": " << profile_path->value();