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