| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome_frame/utils.h" |
| |
| #include <atlsafe.h> |
| #include <atlsecurity.h> |
| #include <htiframe.h> |
| #include <mshtml.h> |
| #include <shlobj.h> |
| |
| #include "base/file_version_info.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_tokenizer.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_local.h" |
| #include "base/win/registry.h" |
| #include "base/win/scoped_bstr.h" |
| #include "base/win/scoped_comptr.h" |
| #include "base/win/scoped_variant.h" |
| #include "chrome/common/automation_messages.h" |
| #include "chrome/common/chrome_paths_internal.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/installer/util/chrome_frame_distribution.h" |
| #include "chrome_frame/chrome_tab.h" |
| #include "chrome_frame/extra_system_apis.h" |
| #include "chrome_frame/html_utils.h" |
| #include "chrome_frame/navigation_constraints.h" |
| #include "chrome_frame/policy_settings.h" |
| #include "chrome_frame/registry_list_preferences_holder.h" |
| #include "chrome_frame/simple_resource_loader.h" |
| #include "extensions/common/constants.h" |
| #include "grit/chromium_strings.h" |
| #include "net/base/escape.h" |
| #include "net/http/http_util.h" |
| #include "ui/base/models/menu_model.h" |
| #include "url/gurl.h" |
| #include "url/url_canon.h" |
| |
| using base::win::RegKey; |
| |
| // Note that these values are all lower case and are compared to |
| // lower-case-transformed values. |
| const char kGCFProtocol[] = "gcf"; |
| const wchar_t kBodyTag[] = L"body"; |
| const wchar_t kContentAttribName[] = L"content"; |
| const wchar_t kChromeContentPrefix[] = L"chrome="; |
| const wchar_t kChromeMimeType[] = L"application/chromepage"; |
| const wchar_t kChromeProtocolPrefix[] = L"gcf:"; |
| const wchar_t kHttpEquivAttribName[] = L"http-equiv"; |
| const wchar_t kIexploreProfileName[] = L"iexplore"; |
| const wchar_t kMetaTag[] = L"meta"; |
| const wchar_t kRundllProfileName[] = L"rundll32"; |
| const wchar_t kXUACompatValue[] = L"x-ua-compatible"; |
| |
| // Registry key and value names related to Chrome Frame configuration options. |
| const wchar_t kAllowUnsafeURLs[] = L"AllowUnsafeURLs"; |
| const wchar_t kChromeFrameConfigKey[] = L"Software\\Google\\ChromeFrame"; |
| const wchar_t kEnableBuggyBhoIntercept[] = L"EnableBuggyBhoIntercept"; |
| const wchar_t kEnableGCFRendererByDefault[] = L"IsDefaultRenderer"; |
| const wchar_t kExcludeUAFromDomainList[] = L"ExcludeUAFromDomain"; |
| const wchar_t kPatchProtocols[] = L"PatchProtocols"; |
| const wchar_t kRenderInGCFUrlList[] = L"RenderInGcfUrls"; |
| const wchar_t kRenderInHostUrlList[] = L"RenderInHostUrls"; |
| |
| static const wchar_t kChromeFramePersistNPAPIReg[] = L"PersistNPAPIReg"; |
| |
| const char kAttachExternalTabPrefix[] = "attach_external_tab"; |
| |
| // Indicates that we are running in a test environment, where execptions, etc |
| // are handled by the chrome test crash server. |
| const wchar_t kChromeFrameHeadlessMode[] = L"ChromeFrameHeadlessMode"; |
| |
| // Indicates that we are running in an environment that expects chrome renderer |
| // accessibility to be enabled for use in automation tests. |
| const wchar_t kChromeFrameAccessibleMode[] = L"ChromeFrameAccessibleMode"; |
| |
| // Indicates that we are running in an environment that wishes to avoid |
| // DLL pinning, such as the perf tests. |
| const wchar_t kChromeFrameUnpinnedMode[] = L"kChromeFrameUnpinnedMode"; |
| |
| // Controls whether we download subresources, etc on the chrome frame page in |
| // the background worker thread. Defaults to true. |
| const wchar_t kUseBackgroundThreadForSubResources[] |
| = L"BackgroundHTTPWorkerThread"; |
| |
| // {1AF32B6C-A3BA-48B9-B24E-8AA9C41F6ECD} |
| static const IID IID_IWebBrowserPriv2IE7 = { 0x1AF32B6C, 0xA3BA, 0x48B9, |
| { 0xB2, 0x4E, 0x8A, 0xA9, 0xC4, 0x1F, 0x6E, 0xCD } }; |
| |
| // {3ED72303-6FFC-4214-BA90-FAF1862DEC8A} |
| static const IID IID_IWebBrowserPriv2IE8 = { 0x3ED72303, 0x6FFC, 0x4214, |
| { 0xBA, 0x90, 0xFA, 0xF1, 0x86, 0x2D, 0xEC, 0x8A } }; |
| |
| // {486F6159-9F3F-4827-82D4-283CEF397733} |
| static const IID IID_IWebBrowserPriv2IE8XP = { 0x486F6159, 0x9F3F, 0x4827, |
| { 0x82, 0xD4, 0x28, 0x3C, 0xEF, 0x39, 0x77, 0x33 } }; |
| |
| // {38339692-0BC9-46CB-8E5C-4677A5C83DD5} |
| static const IID IID_IWebBrowserPriv2IE8XPBeta = { 0x38339692, 0x0BC9, 0x46CB, |
| { 0x8E, 0x5C, 0x46, 0x77, 0xA5, 0xC8, 0x3D, 0xD5 } }; |
| |
| namespace { |
| |
| // A flag used to signal when an active browser instance on the current thread |
| // is loading a Chrome Frame document. There's no reference stored with the |
| // pointer so it should not be dereferenced and used for comparison against a |
| // living instance only. |
| base::LazyInstance<base::ThreadLocalPointer<IBrowserService> > |
| g_tls_browser_for_cf_navigation = LAZY_INSTANCE_INITIALIZER; |
| |
| // Holds the cached preferences for the per-url render type settings. |
| base::LazyInstance<RegistryListPreferencesHolder>::Leaky |
| g_render_type_for_url_holder; |
| |
| // Holds the cached preferences for the per-url user agent filter. |
| base::LazyInstance<RegistryListPreferencesHolder>::Leaky |
| g_user_agent_filter_holder; |
| |
| } // end anonymous namespace |
| |
| HRESULT UtilRegisterTypeLib(HINSTANCE tlb_instance, |
| LPCOLESTR index, |
| bool for_current_user_only) { |
| CComBSTR path; |
| CComPtr<ITypeLib> type_lib; |
| HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib); |
| if (SUCCEEDED(hr)) { |
| hr = UtilRegisterTypeLib(type_lib, path, NULL, for_current_user_only); |
| } |
| return hr; |
| } |
| |
| HRESULT UtilUnRegisterTypeLib(HINSTANCE tlb_instance, |
| LPCOLESTR index, |
| bool for_current_user_only) { |
| CComBSTR path; |
| CComPtr<ITypeLib> type_lib; |
| HRESULT hr = AtlLoadTypeLib(tlb_instance, index, &path, &type_lib); |
| if (SUCCEEDED(hr)) { |
| hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only); |
| } |
| return hr; |
| } |
| |
| HRESULT UtilRegisterTypeLib(LPCWSTR typelib_path, |
| bool for_current_user_only) { |
| if (NULL == typelib_path) { |
| return E_INVALIDARG; |
| } |
| CComBSTR path; |
| CComPtr<ITypeLib> type_lib; |
| HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib); |
| if (SUCCEEDED(hr)) { |
| hr = UtilRegisterTypeLib(type_lib, |
| typelib_path, |
| NULL, |
| for_current_user_only); |
| } |
| return hr; |
| } |
| |
| HRESULT UtilUnRegisterTypeLib(LPCWSTR typelib_path, |
| bool for_current_user_only) { |
| CComPtr<ITypeLib> type_lib; |
| HRESULT hr = ::LoadTypeLib(typelib_path, &type_lib); |
| if (SUCCEEDED(hr)) { |
| hr = UtilUnRegisterTypeLib(type_lib, for_current_user_only); |
| } |
| return hr; |
| } |
| |
| HRESULT UtilRegisterTypeLib(ITypeLib* typelib, |
| LPCWSTR typelib_path, |
| LPCWSTR help_dir, |
| bool for_current_user_only) { |
| typedef HRESULT(WINAPI *RegisterTypeLibPrototype)(ITypeLib FAR* type_lib, |
| OLECHAR FAR* full_path, |
| OLECHAR FAR* help_dir); |
| LPCSTR function_name = |
| for_current_user_only ? "RegisterTypeLibForUser" : "RegisterTypeLib"; |
| RegisterTypeLibPrototype reg_tlb = |
| reinterpret_cast<RegisterTypeLibPrototype>( |
| GetProcAddress(GetModuleHandle(_T("oleaut32.dll")), |
| function_name)); |
| if (NULL == reg_tlb) { |
| return E_FAIL; |
| } |
| return reg_tlb(typelib, |
| const_cast<OLECHAR*>(typelib_path), |
| const_cast<OLECHAR*>(help_dir)); |
| } |
| |
| HRESULT UtilUnRegisterTypeLib(ITypeLib* typelib, |
| bool for_current_user_only) { |
| if (NULL == typelib) { |
| return E_INVALIDARG; |
| } |
| typedef HRESULT(WINAPI *UnRegisterTypeLibPrototype)( |
| REFGUID libID, |
| unsigned short wVerMajor, // NOLINT |
| unsigned short wVerMinor, // NOLINT |
| LCID lcid, |
| SYSKIND syskind); |
| LPCSTR function_name = |
| for_current_user_only ? "UnRegisterTypeLibForUser" : "UnRegisterTypeLib"; |
| |
| UnRegisterTypeLibPrototype unreg_tlb = |
| reinterpret_cast<UnRegisterTypeLibPrototype>( |
| GetProcAddress(GetModuleHandle(_T("oleaut32.dll")), |
| function_name)); |
| if (NULL == unreg_tlb) { |
| return E_FAIL; |
| } |
| TLIBATTR* tla = NULL; |
| HRESULT hr = typelib->GetLibAttr(&tla); |
| if (SUCCEEDED(hr)) { |
| hr = unreg_tlb(tla->guid, |
| tla->wMajorVerNum, |
| tla->wMinorVerNum, |
| tla->lcid, |
| tla->syskind); |
| typelib->ReleaseTLibAttr(tla); |
| } |
| return hr; |
| } |
| |
| bool UtilRemovePersistentNPAPIMarker() { |
| BrowserDistribution* cf_dist = BrowserDistribution::GetDistribution(); |
| std::wstring cf_state_key_path(cf_dist->GetStateKey()); |
| RegKey cf_state_key; |
| |
| LONG result = cf_state_key.Open(HKEY_LOCAL_MACHINE, cf_state_key_path.c_str(), |
| KEY_SET_VALUE); |
| if (result == ERROR_SUCCESS) |
| result = cf_state_key.DeleteValue(kChromeFramePersistNPAPIReg); |
| return (result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); |
| } |
| |
| HRESULT UtilGetXUACompatContentValue(const std::wstring& html_string, |
| std::wstring* content_value) { |
| if (!content_value) { |
| return E_POINTER; |
| } |
| |
| // Fail fast if the string X-UA-Compatible isn't in html_string |
| if (StringToLowerASCII(html_string).find(kXUACompatValue) == |
| std::wstring::npos) { |
| return E_FAIL; |
| } |
| |
| HTMLScanner scanner(html_string.c_str()); |
| |
| // Build the list of meta tags that occur before the body tag is hit. |
| HTMLScanner::StringRangeList tag_list; |
| scanner.GetTagsByName(kMetaTag, &tag_list, kBodyTag); |
| |
| // Search the list of meta tags for one with an http-equiv="X-UA-Compatible" |
| // attribute. |
| HTMLScanner::StringRange attribute; |
| std::string search_attribute_ascii(WideToASCII(kXUACompatValue)); |
| HTMLScanner::StringRangeList::const_iterator tag_list_iter(tag_list.begin()); |
| for (; tag_list_iter != tag_list.end(); tag_list_iter++) { |
| if (!tag_list_iter->GetTagAttribute(kHttpEquivAttribName, &attribute)) { |
| continue; |
| } |
| |
| // We found an http-equiv meta tag, check its value using the ascii |
| // case-insensitive comparison method. |
| if (!attribute.LowerCaseEqualsASCII(search_attribute_ascii.c_str())) { |
| continue; |
| } |
| |
| // We found our X-UA-Compatible meta tag so look for and extract |
| // the value of the content attribute. |
| if (!tag_list_iter->GetTagAttribute(kContentAttribName, &attribute)) { |
| continue; |
| } |
| |
| // Found the content string, copy and return. |
| content_value->assign(attribute.Copy()); |
| return S_OK; |
| } |
| |
| return E_FAIL; |
| } |
| |
| void DisplayVersionMismatchWarning(HWND parent, |
| const std::string& server_version) { |
| // Obtain the current module version. |
| scoped_ptr<FileVersionInfo> module_version_info( |
| FileVersionInfo::CreateFileVersionInfoForCurrentModule()); |
| string16 version_string(module_version_info->file_version()); |
| std::wstring wide_server_version; |
| if (server_version.empty()) { |
| wide_server_version = SimpleResourceLoader::Get(IDS_VERSIONUNKNOWN); |
| } else { |
| wide_server_version = ASCIIToWide(server_version); |
| } |
| std::wstring title = SimpleResourceLoader::Get(IDS_VERSIONMISMATCH_HEADER); |
| std::wstring message; |
| base::SStringPrintf(&message, |
| SimpleResourceLoader::Get(IDS_VERSIONMISMATCH).c_str(), |
| wide_server_version.c_str(), |
| version_string.c_str()); |
| |
| ::MessageBox(parent, message.c_str(), title.c_str(), MB_OK); |
| } |
| |
| std::string CreateJavascript(const std::string& function_name, |
| const std::string args) { |
| std::string script_string = "javascript:"; |
| script_string += function_name + "("; |
| if (!args.empty()) { |
| script_string += "'"; |
| script_string += args; |
| script_string += "'"; |
| } |
| script_string += ")"; |
| return script_string; |
| } |
| |
| AddRefModule::AddRefModule() { |
| // TODO(tommi): Override the module's Lock/Unlock methods to call |
| // npapi::SetValue(NPPVpluginKeepLibraryInMemory) and keep the dll loaded |
| // while the module's refcount is > 0. Only do this when we're being |
| // used as an NPAPI module. |
| _pAtlModule->Lock(); |
| } |
| |
| |
| AddRefModule::~AddRefModule() { |
| _pAtlModule->Unlock(); |
| } |
| |
| bool IsChrome(RendererType renderer_type) { |
| DCHECK_GE(renderer_type, RENDERER_TYPE_UNDETERMINED); |
| DCHECK_LE(renderer_type, RENDERER_TYPE_OTHER); |
| return renderer_type >= RENDERER_TYPE_CHROME_MIN && |
| renderer_type <= RENDERER_TYPE_CHROME_MAX; |
| } |
| |
| namespace { |
| const char kIEImageName[] = "iexplore.exe"; |
| } // namespace |
| |
| std::wstring GetHostProcessName(bool include_extension) { |
| base::FilePath exe; |
| if (PathService::Get(base::FILE_EXE, &exe)) |
| exe = exe.BaseName(); |
| if (!include_extension) { |
| exe = exe.RemoveExtension(); |
| } |
| return exe.value(); |
| } |
| |
| BrowserType GetBrowserType() { |
| static BrowserType browser_type = BROWSER_INVALID; |
| |
| if (browser_type == BROWSER_INVALID) { |
| std::wstring exe(GetHostProcessName(true)); |
| if (!exe.empty()) { |
| std::wstring::const_iterator begin = exe.begin(); |
| std::wstring::const_iterator end = exe.end(); |
| if (LowerCaseEqualsASCII(begin, end, kIEImageName)) { |
| browser_type = BROWSER_IE; |
| } else { |
| browser_type = BROWSER_UNKNOWN; |
| } |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| return browser_type; |
| } |
| |
| uint32 GetIEMajorVersion() { |
| static uint32 ie_major_version = UINT_MAX; |
| |
| if (ie_major_version == UINT_MAX) { |
| wchar_t exe_path[MAX_PATH]; |
| HMODULE mod = GetModuleHandle(NULL); |
| GetModuleFileName(mod, exe_path, arraysize(exe_path) - 1); |
| std::wstring exe_name = base::FilePath(exe_path).BaseName().value(); |
| if (!LowerCaseEqualsASCII(exe_name, kIEImageName)) { |
| ie_major_version = 0; |
| } else { |
| uint32 high = 0; |
| uint32 low = 0; |
| if (GetModuleVersion(mod, &high, &low)) { |
| ie_major_version = HIWORD(high); |
| } else { |
| ie_major_version = 0; |
| } |
| } |
| } |
| |
| return ie_major_version; |
| } |
| |
| IEVersion GetIEVersion() { |
| static IEVersion ie_version = IE_INVALID; |
| |
| if (ie_version == IE_INVALID) { |
| uint32 major_version = GetIEMajorVersion(); |
| switch (major_version) { |
| case 0: |
| ie_version = NON_IE; |
| break; |
| case 6: |
| ie_version = IE_6; |
| break; |
| case 7: |
| ie_version = IE_7; |
| break; |
| case 8: |
| ie_version = IE_8; |
| break; |
| case 9: |
| ie_version = IE_9; |
| break; |
| default: |
| ie_version = (major_version >= 10) ? IE_10 : IE_UNSUPPORTED; |
| break; |
| } |
| } |
| |
| return ie_version; |
| } |
| |
| base::FilePath GetIETemporaryFilesFolder() { |
| LPITEMIDLIST tif_pidl = NULL; |
| HRESULT hr = SHGetFolderLocation(NULL, CSIDL_INTERNET_CACHE, NULL, |
| SHGFP_TYPE_CURRENT, &tif_pidl); |
| if (SUCCEEDED(hr) && tif_pidl) { |
| base::win::ScopedComPtr<IShellFolder> parent_folder; |
| LPITEMIDLIST relative_pidl = NULL; |
| hr = SHBindToParent(tif_pidl, IID_IShellFolder, |
| reinterpret_cast<void**>(parent_folder.Receive()), |
| const_cast<LPCITEMIDLIST*>(&relative_pidl)); |
| if (SUCCEEDED(hr) && relative_pidl) { |
| STRRET path = {0}; |
| hr = parent_folder->GetDisplayNameOf(relative_pidl, |
| SHGDN_NORMAL | SHGDN_FORPARSING, |
| &path); |
| DCHECK(SUCCEEDED(hr)); |
| base::win::ScopedBstr temp_internet_files_bstr; |
| StrRetToBSTR(&path, relative_pidl, temp_internet_files_bstr.Receive()); |
| base::FilePath temp_internet_files( |
| static_cast<BSTR>(temp_internet_files_bstr)); |
| ILFree(tif_pidl); |
| return temp_internet_files; |
| } else { |
| NOTREACHED() << "SHBindToParent failed with Error:" << hr; |
| ILFree(tif_pidl); |
| } |
| } else { |
| NOTREACHED() << "SHGetFolderLocation for internet cache failed. Error:" |
| << hr; |
| } |
| // As a last ditch effort we use the SHGetFolderPath function to retrieve the |
| // path. This function has a limitation of MAX_PATH. |
| wchar_t path[MAX_PATH + 1] = {0}; |
| hr = SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_CURRENT, |
| path); |
| if (SUCCEEDED(hr)) { |
| return base::FilePath(path); |
| } else { |
| NOTREACHED() << "SHGetFolderPath for internet cache failed. Error:" |
| << hr; |
| } |
| return base::FilePath(); |
| } |
| |
| bool IsIEInPrivate() { |
| typedef BOOL (WINAPI* IEIsInPrivateBrowsingPtr)(); |
| bool incognito_mode = false; |
| HMODULE h = GetModuleHandle(L"ieframe.dll"); |
| if (h) { |
| IEIsInPrivateBrowsingPtr IsInPrivate = |
| reinterpret_cast<IEIsInPrivateBrowsingPtr>(GetProcAddress(h, |
| "IEIsInPrivateBrowsing")); |
| if (IsInPrivate) { |
| incognito_mode = !!IsInPrivate(); |
| } |
| } |
| |
| return incognito_mode; |
| } |
| |
| HRESULT DoFileDownloadInIE(const wchar_t* url) { |
| DCHECK(url); |
| |
| HMODULE mod = ::GetModuleHandleA("ieframe.dll"); |
| if (!mod) |
| mod = ::GetModuleHandleA("shdocvw.dll"); |
| |
| if (!mod) { |
| NOTREACHED(); |
| return E_UNEXPECTED; |
| } |
| |
| typedef HRESULT (WINAPI* DoFileDownloadFn)(const wchar_t*); |
| DoFileDownloadFn fn = reinterpret_cast<DoFileDownloadFn>( |
| ::GetProcAddress(mod, "DoFileDownload")); |
| DCHECK(fn); |
| return fn ? fn(url) : E_UNEXPECTED; |
| } |
| |
| bool GetModuleVersion(HMODULE module, uint32* high, uint32* low) { |
| DCHECK(module != NULL) |
| << "Please use GetModuleHandle(NULL) to get the process name"; |
| DCHECK(high); |
| |
| bool ok = false; |
| |
| HRSRC res = FindResource(module, |
| reinterpret_cast<const wchar_t*>(VS_VERSION_INFO), RT_VERSION); |
| if (res) { |
| HGLOBAL res_data = LoadResource(module, res); |
| DWORD version_resource_size = SizeofResource(module, res); |
| const void* readonly_resource_data = LockResource(res_data); |
| if (readonly_resource_data && version_resource_size) { |
| // Copy data as VerQueryValue tries to modify the data. This causes |
| // exceptions and heap corruption errors if debugger is attached. |
| scoped_ptr<char[]> data(new char[version_resource_size]); |
| if (data.get()) { |
| memcpy(data.get(), readonly_resource_data, version_resource_size); |
| VS_FIXEDFILEINFO* ver_info = NULL; |
| UINT info_size = 0; |
| if (VerQueryValue(data.get(), L"\\", |
| reinterpret_cast<void**>(&ver_info), &info_size)) { |
| *high = ver_info->dwFileVersionMS; |
| if (low != NULL) |
| *low = ver_info->dwFileVersionLS; |
| ok = true; |
| } |
| |
| UnlockResource(res_data); |
| } |
| FreeResource(res_data); |
| } |
| } |
| |
| return ok; |
| } |
| |
| namespace { |
| |
| const int kMaxSubmenuDepth = 10; |
| |
| // Builds a Windows menu from the menu model sent from Chrome. The |
| // caller is responsible for closing the returned HMENU. This does |
| // not currently handle bitmaps (e.g. hbmpChecked, hbmpUnchecked or |
| // hbmpItem), so checkmarks, radio buttons, and custom icons won't work. |
| // It also copies over submenus up to a maximum depth of kMaxSubMenuDepth. |
| HMENU BuildContextMenuImpl(const ContextMenuModel* menu_model, int depth) { |
| if (depth >= kMaxSubmenuDepth) |
| return NULL; |
| |
| HMENU menu = CreatePopupMenu(); |
| for (size_t i = 0; i < menu_model->items.size(); i++) { |
| const ContextMenuModel::Item& item = menu_model->items[i]; |
| |
| MENUITEMINFO item_info = { 0 }; |
| item_info.cbSize = sizeof(MENUITEMINFO); |
| switch (item.type) { |
| case ui::MenuModel::TYPE_COMMAND: |
| case ui::MenuModel::TYPE_CHECK: |
| item_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING; |
| item_info.fType = MFT_STRING; |
| item_info.wID = item.item_id; |
| item_info.dwTypeData = const_cast<LPWSTR>(item.label.c_str()); |
| break; |
| case ui::MenuModel::TYPE_RADIO: |
| item_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING; |
| item_info.fType = MFT_STRING | MFT_RADIOCHECK; |
| item_info.wID = item.item_id; |
| item_info.dwTypeData = const_cast<LPWSTR>(item.label.c_str()); |
| break; |
| case ui::MenuModel::TYPE_SEPARATOR: |
| item_info.fMask = MIIM_FTYPE; |
| item_info.fType = MFT_SEPARATOR; |
| break; |
| case ui::MenuModel::TYPE_SUBMENU: |
| item_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_SUBMENU; |
| item_info.fType = MFT_STRING; |
| item_info.wID = item.item_id; |
| item_info.dwTypeData = const_cast<LPWSTR>(item.label.c_str()); |
| item_info.hSubMenu = BuildContextMenuImpl(item.submenu, depth + 1); |
| break; |
| default: |
| NOTREACHED() << "Unsupported MenuModel::ItemType " << item.type; |
| break; |
| } |
| |
| item_info.fMask |= MIIM_STATE; |
| item_info.fState = |
| (item.checked ? MFS_CHECKED : MFS_UNCHECKED) | |
| (item.enabled ? MFS_ENABLED : (MFS_DISABLED | MFS_GRAYED)); |
| |
| InsertMenuItem(menu, i, TRUE, &item_info); |
| } |
| |
| return menu; |
| } |
| |
| } // namespace |
| |
| HMENU BuildContextMenu(const ContextMenuModel& menu_model) { |
| return BuildContextMenuImpl(&menu_model, 0); |
| } |
| |
| std::string ResolveURL(const std::string& document, |
| const std::string& relative) { |
| if (document.empty()) { |
| return GURL(relative).spec(); |
| } else { |
| return GURL(document).Resolve(relative).spec(); |
| } |
| } |
| |
| bool HaveSameOrigin(const std::string& url1, const std::string& url2) { |
| GURL a(url1), b(url2); |
| bool ret; |
| if (a.is_valid() != b.is_valid()) { |
| // Either (but not both) url is invalid, so they can't match. |
| ret = false; |
| } else if (!a.is_valid()) { |
| // Both URLs are invalid (see first check). Just check if the opaque |
| // strings match exactly. |
| ret = url1.compare(url2) == 0; |
| } else if (a.GetOrigin() != b.GetOrigin()) { |
| // The origins don't match. |
| ret = false; |
| } else { |
| // we have a match. |
| ret = true; |
| } |
| |
| return ret; |
| } |
| |
| int GetConfigInt(int default_value, const wchar_t* value_name) { |
| int ret = default_value; |
| RegKey config_key; |
| if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, |
| KEY_QUERY_VALUE) == ERROR_SUCCESS) { |
| config_key.ReadValueDW(value_name, reinterpret_cast<DWORD*>(&ret)); |
| } |
| |
| return ret; |
| } |
| |
| int64 GetConfigInt64(int64 default_value, const wchar_t* value_name) { |
| int64 ret = default_value; |
| RegKey config_key; |
| if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, |
| KEY_QUERY_VALUE) == ERROR_SUCCESS) { |
| config_key.ReadInt64(value_name, &ret); |
| } |
| |
| return ret; |
| } |
| |
| bool GetConfigBool(bool default_value, const wchar_t* value_name) { |
| DWORD value = GetConfigInt(default_value, value_name); |
| return (value != FALSE); |
| } |
| |
| bool SetConfigInt(const wchar_t* value_name, int value) { |
| RegKey config_key; |
| if (config_key.Create(HKEY_CURRENT_USER, kChromeFrameConfigKey, |
| KEY_SET_VALUE) == ERROR_SUCCESS) { |
| if (config_key.WriteValue(value_name, value) == ERROR_SUCCESS) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool SetConfigBool(const wchar_t* value_name, bool value) { |
| return SetConfigInt(value_name, value); |
| } |
| |
| bool SetConfigInt64(const wchar_t* value_name, int64 value) { |
| RegKey config_key; |
| if (config_key.Create(HKEY_CURRENT_USER, kChromeFrameConfigKey, |
| KEY_SET_VALUE) == ERROR_SUCCESS) { |
| if (config_key.WriteValue(value_name, &value, sizeof(value), |
| REG_QWORD) == ERROR_SUCCESS) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool DeleteConfigValue(const wchar_t* value_name) { |
| RegKey config_key; |
| if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, |
| KEY_WRITE) == ERROR_SUCCESS) { |
| if (config_key.DeleteValue(value_name) == ERROR_SUCCESS) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool IsGcfDefaultRenderer() { |
| DWORD is_default = 0; // NOLINT |
| |
| // First check policy settings |
| PolicySettings::RendererForUrl renderer = |
| PolicySettings::GetInstance()->default_renderer(); |
| if (renderer != PolicySettings::RENDERER_NOT_SPECIFIED) { |
| is_default = (renderer == PolicySettings::RENDER_IN_CHROME_FRAME); |
| } else { |
| // TODO(tommi): Implement caching for this config value as it gets |
| // checked frequently. |
| RegKey config_key; |
| if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, |
| KEY_READ) == ERROR_SUCCESS) { |
| config_key.ReadValueDW(kEnableGCFRendererByDefault, &is_default); |
| } |
| } |
| |
| return is_default != 0; |
| } |
| |
| RendererType RendererTypeForUrl(const std::wstring& url) { |
| // First check if the default renderer settings are specified by policy. |
| // If so, then that overrides the user settings. |
| PolicySettings::RendererForUrl renderer = |
| PolicySettings::GetInstance()->GetRendererForUrl(url.c_str()); |
| if (renderer != PolicySettings::RENDERER_NOT_SPECIFIED) { |
| // We may know at this point that policy says do NOT render in Chrome Frame. |
| // To maintain consistency, we return RENDERER_TYPE_UNDETERMINED so that |
| // content sniffing, etc. still take place. |
| // TODO(tommi): Clarify the intent here. |
| return (renderer == PolicySettings::RENDER_IN_CHROME_FRAME) ? |
| RENDERER_TYPE_CHROME_OPT_IN_URL : RENDERER_TYPE_UNDETERMINED; |
| } |
| |
| // TODO(robertshield): Move this into a holder-type class that listens |
| // for reg change events as well. |
| static int render_in_cf_by_default = FALSE; |
| |
| RegistryListPreferencesHolder& render_type_for_url_holder = |
| g_render_type_for_url_holder.Get(); |
| if (!render_type_for_url_holder.Valid()) { |
| const wchar_t* url_list_name = kRenderInGCFUrlList; |
| if (IsGcfDefaultRenderer()) { |
| url_list_name = kRenderInHostUrlList; |
| render_in_cf_by_default = TRUE; |
| } else { |
| render_in_cf_by_default = FALSE; |
| } |
| |
| render_type_for_url_holder.Init(HKEY_CURRENT_USER, |
| kChromeFrameConfigKey, |
| url_list_name); |
| } |
| DCHECK(render_type_for_url_holder.Valid()); |
| |
| RendererType renderer_type = |
| render_in_cf_by_default ? RENDERER_TYPE_CHROME_DEFAULT_RENDERER : |
| RENDERER_TYPE_UNDETERMINED; |
| |
| if (render_type_for_url_holder.ListMatches(url)) { |
| renderer_type = render_in_cf_by_default ? |
| RENDERER_TYPE_UNDETERMINED : |
| RENDERER_TYPE_CHROME_OPT_IN_URL; |
| } |
| |
| return renderer_type; |
| } |
| |
| bool ShouldRemoveUAForUrl(const string16& url) { |
| // TODO(robertshield): Wire up the stuff in PolicySettings here so the value |
| // can be specified via group policy. |
| // TODO(robertshield): Add a default list of exclusions here for site with |
| // known bad UA parsing. |
| RegistryListPreferencesHolder& user_agent_filter_holder = |
| g_user_agent_filter_holder.Get(); |
| if (!user_agent_filter_holder.Valid()) { |
| user_agent_filter_holder.Init(HKEY_CURRENT_USER, |
| kChromeFrameConfigKey, |
| kExcludeUAFromDomainList); |
| } |
| DCHECK(user_agent_filter_holder.Valid()); |
| |
| return user_agent_filter_holder.ListMatches(url); |
| } |
| |
| RegistryListPreferencesHolder& GetRendererTypePreferencesHolderForTesting() { |
| return g_render_type_for_url_holder.Get(); |
| } |
| |
| RegistryListPreferencesHolder& GetUserAgentPreferencesHolderForTesting() { |
| return g_user_agent_filter_holder.Get(); |
| } |
| |
| HRESULT NavigateBrowserToMoniker(IUnknown* browser, IMoniker* moniker, |
| const wchar_t* headers, IBindCtx* bind_ctx, |
| const wchar_t* fragment, IStream* post_data, |
| VARIANT* flags) { |
| DCHECK(browser); |
| DCHECK(moniker); |
| DCHECK(bind_ctx); |
| |
| base::win::ScopedComPtr<IWebBrowser2> web_browser2; |
| HRESULT hr = DoQueryService(SID_SWebBrowserApp, browser, |
| web_browser2.Receive()); |
| DCHECK(web_browser2); |
| DLOG_IF(WARNING, FAILED(hr)) << base::StringPrintf(L"SWebBrowserApp 0x%08X", |
| hr); |
| if (FAILED(hr)) |
| return hr; |
| |
| // If the data to be downloaded was received in response to a post request |
| // then we need to reissue the post request. |
| base::win::ScopedVariant post_data_variant; |
| if (post_data) { |
| RewindStream(post_data); |
| |
| CComSafeArray<uint8> safe_array_post; |
| |
| STATSTG stat; |
| post_data->Stat(&stat, STATFLAG_NONAME); |
| |
| if (stat.cbSize.LowPart > 0) { |
| std::string data; |
| |
| HRESULT hr = E_FAIL; |
| while ((hr = ReadStream(post_data, 0xffff, &data)) == S_OK) { |
| safe_array_post.Add( |
| data.size(), |
| reinterpret_cast<unsigned char*>(const_cast<char*>(data.data()))); |
| data.clear(); |
| } |
| } else { |
| // If we get here it means that the navigation is being reissued for a |
| // POST request with no data. To ensure that the new window used as a |
| // target to handle the new navigation issues a POST request |
| // we need valid POST data. In this case we create a dummy 1 byte array. |
| // May not work as expected with some web sites. |
| DLOG(WARNING) << "Reissuing navigation with empty POST data. May not" |
| << " work as expected"; |
| safe_array_post.Create(1); |
| } |
| post_data_variant.Set(safe_array_post.Detach()); |
| } |
| // Create a new bind context that's not associated with our callback. |
| // Calling RevokeBindStatusCallback doesn't disassociate the callback with |
| // the bind context in IE7. The returned bind context has the same |
| // implementation of GetRunningObjectTable as the bind context we held which |
| // basically delegates to ole32's GetRunningObjectTable. The object table |
| // is then used to determine if the moniker is already running and via |
| // that mechanism is associated with the same internet request as has already |
| // been issued. |
| |
| // TODO(tommi): See if we can get HlinkSimpleNavigateToMoniker to work |
| // instead. Looks like we'll need to support IHTMLDocument2 (get_URL in |
| // particular), access to IWebBrowser2 etc. |
| // HlinkSimpleNavigateToMoniker(moniker, url, NULL, host, bind_context, |
| // NULL, 0, 0); |
| |
| base::win::ScopedComPtr<IUriContainer> uri_container; |
| hr = uri_container.QueryFrom(moniker); |
| |
| base::win::ScopedVariant headers_var; |
| if (headers && headers[0]) |
| headers_var.Set(headers); |
| |
| if (uri_container) { |
| // IE7 and IE8. |
| const IID* interface_ids[] = { |
| &IID_IWebBrowserPriv2IE7, |
| &IID_IWebBrowserPriv2IE8, |
| &IID_IWebBrowserPriv2IE8XP, |
| &IID_IWebBrowserPriv2IE8XPBeta, |
| }; |
| |
| base::win::ScopedComPtr<IWebBrowserPriv2Common, NULL> browser_priv2; |
| for (int i = 0; i < arraysize(interface_ids) && browser_priv2 == NULL; |
| ++i) { |
| hr = web_browser2.QueryInterface(*interface_ids[i], |
| reinterpret_cast<void**>(browser_priv2.Receive())); |
| } |
| |
| DCHECK(browser_priv2); |
| |
| if (browser_priv2) { |
| base::win::ScopedComPtr<IUri> uri_obj; |
| uri_container->GetIUri(uri_obj.Receive()); |
| DCHECK(uri_obj); |
| |
| if (GetIEVersion() < IE_9) { |
| hr = browser_priv2->NavigateWithBindCtx2( |
| uri_obj, flags, NULL, post_data_variant.AsInput(), |
| headers_var.AsInput(), bind_ctx, |
| const_cast<wchar_t*>(fragment)); |
| } else { |
| IWebBrowserPriv2CommonIE9* browser_priv2_ie9 = |
| reinterpret_cast<IWebBrowserPriv2CommonIE9*>(browser_priv2.get()); |
| hr = browser_priv2_ie9->NavigateWithBindCtx2( |
| uri_obj, flags, NULL, post_data_variant.AsInput(), |
| headers_var.AsInput(), bind_ctx, |
| const_cast<wchar_t*>(fragment), 0); |
| } |
| DLOG_IF(WARNING, FAILED(hr)) |
| << base::StringPrintf(L"NavigateWithBindCtx2 0x%08X", hr); |
| } |
| } else { |
| // IE6 |
| LPOLESTR url = NULL; |
| if (SUCCEEDED(hr = moniker->GetDisplayName(bind_ctx, NULL, &url))) { |
| DVLOG(1) << __FUNCTION__ << " " << url; |
| base::win::ScopedComPtr<IWebBrowserPriv> browser_priv; |
| if (SUCCEEDED(hr = browser_priv.QueryFrom(web_browser2))) { |
| GURL target_url(url); |
| // On IE6 if the original URL has a fragment then the navigation |
| // attempt is ignored. To workaround this we strip the fragment from |
| // the url and initiate the navigation. When the active document loads |
| // we retrieve the original url with the fragment from the Navigation |
| // manager and use it. |
| if (target_url.has_ref()) { |
| url_parse::Component comp; |
| GURL::Replacements replacements; |
| replacements.SetRef("", comp); |
| |
| target_url = target_url.ReplaceComponents(replacements); |
| fragment = NULL; |
| } |
| |
| base::win::ScopedVariant var_url(UTF8ToWide(target_url.spec()).c_str()); |
| hr = browser_priv->NavigateWithBindCtx(var_url.AsInput(), flags, NULL, |
| post_data_variant.AsInput(), |
| headers_var.AsInput(), bind_ctx, |
| const_cast<wchar_t*>(fragment)); |
| DLOG_IF(WARNING, FAILED(hr)) |
| << base::StringPrintf(L"NavigateWithBindCtx 0x%08X", hr); |
| } else { |
| NOTREACHED(); |
| } |
| ::CoTaskMemFree(url); |
| } else { |
| DLOG(ERROR) << base::StringPrintf("GetDisplayName: 0x%08X", hr); |
| } |
| } |
| |
| return hr; |
| } |
| |
| void MarkBrowserOnThreadForCFNavigation(IBrowserService* browser) { |
| DCHECK(browser != NULL); |
| DCHECK(g_tls_browser_for_cf_navigation.Pointer()->Get() == NULL || |
| g_tls_browser_for_cf_navigation.Pointer()->Get() == browser); |
| g_tls_browser_for_cf_navigation.Pointer()->Set(browser); |
| } |
| |
| bool CheckForCFNavigation(IBrowserService* browser, bool clear_flag) { |
| DCHECK(browser); |
| bool ret = (g_tls_browser_for_cf_navigation.Pointer()->Get() == browser); |
| if (ret && clear_flag) |
| g_tls_browser_for_cf_navigation.Pointer()->Set(NULL); |
| return ret; |
| } |
| |
| bool IsValidUrlScheme(const GURL& url, bool is_privileged) { |
| if (url.is_empty()) |
| return false; |
| |
| if (url.SchemeIs(chrome::kHttpScheme) || |
| url.SchemeIs(chrome::kHttpsScheme) || |
| url.SchemeIs(chrome::kAboutScheme)) |
| return true; |
| |
| // Additional checking for view-source. Allow only http and https |
| // URLs in view source. |
| if (url.SchemeIs(content::kViewSourceScheme)) { |
| GURL sub_url(url.path()); |
| if (sub_url.SchemeIs(chrome::kHttpScheme) || |
| sub_url.SchemeIs(chrome::kHttpsScheme)) |
| return true; |
| else |
| return false; |
| } |
| |
| if (is_privileged && |
| (url.SchemeIs(chrome::kDataScheme) || |
| url.SchemeIs(extensions::kExtensionScheme))) |
| return true; |
| |
| return false; |
| } |
| |
| std::string GetRawHttpHeaders(IWinInetHttpInfo* info) { |
| DCHECK(info); |
| |
| std::string buffer; |
| |
| DWORD size = 0; |
| DWORD flags = 0; |
| DWORD reserved = 0; |
| HRESULT hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &size, |
| &flags, &reserved); |
| if (!size) { |
| DLOG(WARNING) << "Failed to query HTTP headers size. Error: " << hr; |
| } else { |
| buffer.resize(size + 1); |
| hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, &buffer[0], |
| &size, &flags, &reserved); |
| if (FAILED(hr)) { |
| DLOG(WARNING) << "Failed to query HTTP headers. Error: " << hr; |
| } |
| } |
| |
| return buffer; |
| } |
| |
| bool IsSubFrameRequest(IUnknown* service_provider) { |
| DCHECK(service_provider); |
| |
| // We need to be able to get at an IWebBrowser2 if we are to decide whether |
| // this request originates from a non-top-level frame. |
| base::win::ScopedComPtr<IWebBrowser2> web_browser; |
| HRESULT hr = DoQueryService(IID_ITargetFrame2, service_provider, |
| web_browser.Receive()); |
| |
| bool is_sub_frame_request = false; |
| if (web_browser) { |
| // Now check to see if we are in a sub-frame. |
| base::win::ScopedComPtr<IHTMLWindow2> current_frame, parent_frame; |
| hr = DoQueryService(IID_IHTMLWindow2, service_provider, |
| current_frame.Receive()); |
| if (current_frame) { |
| // Only the top level window will return self when get_parent is called. |
| current_frame->get_parent(parent_frame.Receive()); |
| if (parent_frame != current_frame) { |
| DVLOG(1) << "Sub frame detected"; |
| is_sub_frame_request = true; |
| } |
| } |
| } else { |
| DVLOG(1) << "IsSubFrameRequest - no IWebBrowser2"; |
| is_sub_frame_request = true; |
| } |
| |
| return is_sub_frame_request; |
| } |
| |
| bool IsHeadlessMode() { |
| bool headless = GetConfigBool(false, kChromeFrameHeadlessMode); |
| return headless; |
| } |
| |
| bool IsAccessibleMode() { |
| bool accessible = GetConfigBool(false, kChromeFrameAccessibleMode); |
| return accessible; |
| } |
| |
| bool IsUnpinnedMode() { |
| // We only check this value once and then cache it since changing the registry |
| // once we've pinned the DLL won't have any effect. |
| static bool unpinned = GetConfigBool(false, kChromeFrameUnpinnedMode); |
| return unpinned; |
| } |
| |
| std::wstring GetActualUrlFromMoniker(IMoniker* moniker, |
| IBindCtx* bind_context, |
| const std::wstring& bho_url) { |
| CComHeapPtr<WCHAR> display_name; |
| moniker->GetDisplayName(bind_context, NULL, &display_name); |
| std::wstring moniker_url = display_name; |
| |
| GURL parsed_url(WideToUTF8(bho_url)); |
| if (!parsed_url.has_ref()) |
| return moniker_url; |
| |
| if (StartsWith(bho_url, moniker_url, false) && |
| bho_url[moniker_url.length()] == L'#') |
| return bho_url; |
| |
| return moniker_url; |
| } |
| |
| bool IsTopLevelWindow(HWND window) { |
| long style = GetWindowLong(window, GWL_STYLE); // NOLINT |
| if (!(style & WS_CHILD)) |
| return true; |
| |
| HWND parent = GetParent(window); |
| return !parent || (parent == GetDesktopWindow()); |
| } |
| |
| HRESULT RewindStream(IStream* stream) { |
| HRESULT hr = E_POINTER; |
| if (stream) { |
| LARGE_INTEGER zero = {0}; |
| ULARGE_INTEGER new_pos = {0}; |
| hr = stream->Seek(zero, STREAM_SEEK_SET, &new_pos); |
| } |
| |
| return hr; |
| } |
| |
| std::wstring GuidToString(const GUID& guid) { |
| std::wstring ret; |
| ::StringFromGUID2(guid, WriteInto(&ret, 39), 39); |
| return ret; |
| } |
| |
| int32 MapCookieStateToCookieAction(InternetCookieState cookie_state) { |
| int32 cookie_action = COOKIEACTION_NONE; |
| |
| switch (cookie_state) { |
| case COOKIE_STATE_UNKNOWN: |
| cookie_action = COOKIEACTION_NONE; |
| break; |
| case COOKIE_STATE_ACCEPT: |
| cookie_action = COOKIEACTION_ACCEPT; |
| break; |
| case COOKIE_STATE_LEASH: |
| cookie_action = COOKIEACTION_LEASH; |
| break; |
| case COOKIE_STATE_DOWNGRADE: |
| cookie_action = COOKIEACTION_DOWNGRADE; |
| break; |
| case COOKIE_STATE_REJECT: |
| cookie_action = COOKIEACTION_REJECT; |
| break; |
| default: |
| cookie_action = COOKIEACTION_REJECT; |
| break; |
| } |
| return cookie_action; |
| } |
| |
| GURL GetUrlWithoutFragment(const wchar_t* url) { |
| GURL parsed_url(url); |
| |
| if (parsed_url.has_ref()) { |
| url_parse::Component comp; |
| GURL::Replacements replacements; |
| replacements.SetRef("", comp); |
| |
| parsed_url = parsed_url.ReplaceComponents(replacements); |
| } |
| return parsed_url; |
| } |
| |
| bool CompareUrlsWithoutFragment(const wchar_t* url1, const wchar_t* url2) { |
| GURL parsed_url1 = GetUrlWithoutFragment(url1); |
| GURL parsed_url2 = GetUrlWithoutFragment(url2); |
| return parsed_url1 == parsed_url2; |
| } |
| |
| std::string FindReferrerFromHeaders(const wchar_t* headers, |
| const wchar_t* additional_headers) { |
| std::string referrer; |
| |
| const wchar_t* both_headers[] = { headers, additional_headers }; |
| for (int i = 0; referrer.empty() && i < arraysize(both_headers); ++i) { |
| if (!both_headers[i]) |
| continue; |
| std::string raw_headers_utf8 = WideToUTF8(both_headers[i]); |
| net::HttpUtil::HeadersIterator it(raw_headers_utf8.begin(), |
| raw_headers_utf8.end(), "\r\n"); |
| while (it.GetNext()) { |
| if (LowerCaseEqualsASCII(it.name(), "referer")) { |
| referrer = it.values(); |
| break; |
| } |
| } |
| } |
| |
| return referrer; |
| } |
| |
| std::string GetHttpHeadersFromBinding(IBinding* binding) { |
| if (binding == NULL) { |
| DLOG(WARNING) << "GetHttpResponseStatus - no binding_"; |
| return std::string(); |
| } |
| |
| base::win::ScopedComPtr<IWinInetHttpInfo> info; |
| if (FAILED(info.QueryFrom(binding))) { |
| DLOG(WARNING) << "Failed to QI for IWinInetHttpInfo"; |
| return std::string(); |
| } |
| |
| return GetRawHttpHeaders(info); |
| } |
| |
| int GetHttpResponseStatusFromBinding(IBinding* binding) { |
| DVLOG(1) << __FUNCTION__; |
| if (binding == NULL) { |
| DLOG(WARNING) << "GetHttpResponseStatus - no binding_"; |
| return 0; |
| } |
| |
| int http_status = 0; |
| |
| base::win::ScopedComPtr<IWinInetHttpInfo> info; |
| if (SUCCEEDED(info.QueryFrom(binding))) { |
| char status[10] = {0}; |
| DWORD buf_size = sizeof(status); |
| DWORD flags = 0; |
| DWORD reserved = 0; |
| if (SUCCEEDED(info->QueryInfo(HTTP_QUERY_STATUS_CODE, status, &buf_size, |
| &flags, &reserved))) { |
| base::StringToInt(status, &http_status); |
| } else { |
| NOTREACHED() << "Failed to get HTTP status"; |
| } |
| } else { |
| NOTREACHED() << "failed to get IWinInetHttpInfo from binding_"; |
| } |
| |
| return http_status; |
| } |
| |
| CLIPFORMAT GetTextHtmlClipboardFormat() { |
| static const CLIPFORMAT text_html = RegisterClipboardFormat(CFSTR_MIME_HTML); |
| return text_html; |
| } |
| |
| bool IsTextHtmlMimeType(const wchar_t* mime_type) { |
| return IsTextHtmlClipFormat(RegisterClipboardFormatW(mime_type)); |
| } |
| |
| bool IsTextHtmlClipFormat(CLIPFORMAT cf) { |
| return cf == GetTextHtmlClipboardFormat(); |
| } |
| |
| bool IsSystemProcess() { |
| bool is_system = false; |
| CAccessToken process_token; |
| if (process_token.GetProcessToken(TOKEN_QUERY, GetCurrentProcess())) { |
| CSid logon_sid; |
| if (process_token.GetUser(&logon_sid)) { |
| is_system = logon_sid == Sids::System(); |
| } |
| } |
| return is_system; |
| } |
| |
| |
| std::string BindStatus2Str(ULONG bind_status) { |
| std::string s; |
| static const char* const bindstatus_txt[] = { |
| "BINDSTATUS_FINDINGRESOURCE", |
| "BINDSTATUS_CONNECTING", |
| "BINDSTATUS_REDIRECTING", |
| "BINDSTATUS_BEGINDOWNLOADDATA", |
| "BINDSTATUS_DOWNLOADINGDATA", |
| "BINDSTATUS_ENDDOWNLOADDATA", |
| "BINDSTATUS_BEGINDOWNLOADCOMPONENTS", |
| "BINDSTATUS_INSTALLINGCOMPONENTS", |
| "BINDSTATUS_ENDDOWNLOADCOMPONENTS", |
| "BINDSTATUS_USINGCACHEDCOPY", |
| "BINDSTATUS_SENDINGREQUEST", |
| "BINDSTATUS_CLASSIDAVAILABLE", |
| "BINDSTATUS_MIMETYPEAVAILABLE", |
| "BINDSTATUS_CACHEFILENAMEAVAILABLE", |
| "BINDSTATUS_BEGINSYNCOPERATION", |
| "BINDSTATUS_ENDSYNCOPERATION", |
| "BINDSTATUS_BEGINUPLOADDATA", |
| "BINDSTATUS_UPLOADINGDATA", |
| "BINDSTATUS_ENDUPLOADINGDATA", |
| "BINDSTATUS_PROTOCOLCLASSID", |
| "BINDSTATUS_ENCODING", |
| "BINDSTATUS_VERFIEDMIMETYPEAVAILABLE", |
| "BINDSTATUS_CLASSINSTALLLOCATION", |
| "BINDSTATUS_DECODING", |
| "BINDSTATUS_LOADINGMIMEHANDLER", |
| "BINDSTATUS_CONTENTDISPOSITIONATTACH", |
| "BINDSTATUS_FILTERREPORTMIMETYPE", |
| "BINDSTATUS_CLSIDCANINSTANTIATE", |
| "BINDSTATUS_IUNKNOWNAVAILABLE", |
| "BINDSTATUS_DIRECTBIND", |
| "BINDSTATUS_RAWMIMETYPE", |
| "BINDSTATUS_PROXYDETECTING", |
| "BINDSTATUS_ACCEPTRANGES", |
| "BINDSTATUS_COOKIE_SENT", |
| "BINDSTATUS_COMPACT_POLICY_RECEIVED", |
| "BINDSTATUS_COOKIE_SUPPRESSED", |
| "BINDSTATUS_COOKIE_STATE_UNKNOWN", |
| "BINDSTATUS_COOKIE_STATE_ACCEPT", |
| "BINDSTATUS_COOKIE_STATE_REJECT", |
| "BINDSTATUS_COOKIE_STATE_PROMPT", |
| "BINDSTATUS_COOKIE_STATE_LEASH", |
| "BINDSTATUS_COOKIE_STATE_DOWNGRADE", |
| "BINDSTATUS_POLICY_HREF", |
| "BINDSTATUS_P3P_HEADER", |
| "BINDSTATUS_SESSION_COOKIE_RECEIVED", |
| "BINDSTATUS_PERSISTENT_COOKIE_RECEIVED", |
| "BINDSTATUS_SESSION_COOKIES_ALLOWED", |
| "BINDSTATUS_CACHECONTROL", |
| "BINDSTATUS_CONTENTDISPOSITIONFILENAME", |
| "BINDSTATUS_MIMETEXTPLAINMISMATCH", |
| "BINDSTATUS_PUBLISHERAVAILABLE", |
| "BINDSTATUS_DISPLAYNAMEAVAILABLE", |
| "BINDSTATUS_SSLUX_NAVBLOCKED", |
| "BINDSTATUS_SERVER_MIMETYPEAVAILABLE", |
| "BINDSTATUS_SNIFFED_CLASSIDAVAILABLE", |
| "BINDSTATUS_64BIT_PROGRESS" |
| }; |
| if (bind_status >= 1 && bind_status <= BINDSTATUS_64BIT_PROGRESS) |
| s = bindstatus_txt[bind_status - 1]; |
| else |
| s = base::StringPrintf("UnDoc[%#x]", bind_status); |
| return s; |
| } |
| |
| std::string PiFlags2Str(DWORD flags) { |
| #define ADD_PI_FLAG(x) \ |
| if (flags & x) { \ |
| s.append(#x ## " "); \ |
| flags &= ~x; \ |
| } |
| |
| std::string s = " flags "; |
| ADD_PI_FLAG(PI_PARSE_URL); |
| ADD_PI_FLAG(PI_FILTER_MODE); |
| ADD_PI_FLAG(PI_FORCE_ASYNC); |
| ADD_PI_FLAG(PI_USE_WORKERTHREAD); |
| ADD_PI_FLAG(PI_MIMEVERIFICATION); |
| ADD_PI_FLAG(PI_CLSIDLOOKUP); |
| ADD_PI_FLAG(PI_DATAPROGRESS); |
| ADD_PI_FLAG(PI_SYNCHRONOUS); |
| ADD_PI_FLAG(PI_APARTMENTTHREADED); |
| ADD_PI_FLAG(PI_CLASSINSTALL); |
| ADD_PI_FLAG(PI_PASSONBINDCTX); |
| ADD_PI_FLAG(PI_NOMIMEHANDLER); |
| ADD_PI_FLAG(PI_LOADAPPDIRECT); |
| ADD_PI_FLAG(PD_FORCE_SWITCH); |
| ADD_PI_FLAG(PI_PREFERDEFAULTHANDLER); |
| |
| if (flags) |
| s += base::StringPrintf("+UnDoc[%#x]", flags); |
| return s; |
| #undef ADD_PI_FLAG |
| } |
| |
| std::string Bscf2Str(DWORD flags) { |
| #define ADD_BSCF_FLAG(x) \ |
| if (flags & x) {\ |
| s.append(#x ## " "); \ |
| flags &= ~x; \ |
| } |
| |
| std::string s = " flags "; |
| ADD_BSCF_FLAG(BSCF_FIRSTDATANOTIFICATION) |
| ADD_BSCF_FLAG(BSCF_INTERMEDIATEDATANOTIFICATION) |
| ADD_BSCF_FLAG(BSCF_LASTDATANOTIFICATION) |
| ADD_BSCF_FLAG(BSCF_DATAFULLYAVAILABLE) |
| ADD_BSCF_FLAG(BSCF_AVAILABLEDATASIZEUNKNOWN) |
| ADD_BSCF_FLAG(BSCF_SKIPDRAINDATAFORFILEURLS) |
| ADD_BSCF_FLAG(BSCF_64BITLENGTHDOWNLOAD) |
| |
| if (flags) |
| s += base::StringPrintf("+UnDoc[%#x]", flags); |
| return s; |
| #undef ADD_BSCF_FLAG |
| } |
| |
| // Reads data from a stream into a string. |
| HRESULT ReadStream(IStream* stream, size_t size, std::string* data) { |
| DCHECK(stream); |
| DCHECK_GT(size, 0u); |
| DCHECK(data); |
| |
| DWORD read = 0; |
| HRESULT hr = stream->Read(WriteInto(data, size + 1), size, &read); |
| DCHECK(hr == S_OK || hr == S_FALSE || hr == E_PENDING); |
| if (read) { |
| data->erase(read); |
| DCHECK_EQ(read, data->length()); |
| } else { |
| data->clear(); |
| // Return S_FALSE if the underlying stream returned S_OK and zero bytes. |
| if (hr == S_OK) |
| hr = S_FALSE; |
| } |
| |
| return hr; |
| } |
| |
| ChromeFrameUrl::ChromeFrameUrl() { |
| Reset(); |
| } |
| |
| bool ChromeFrameUrl::Parse(const std::wstring& url) { |
| Reset(); |
| parsed_url_ = GURL(url); |
| |
| if (parsed_url_.is_empty()) |
| return false; |
| |
| is_chrome_protocol_ = parsed_url_.SchemeIs(kGCFProtocol); |
| if (is_chrome_protocol_) { |
| parsed_url_ = GURL(url.c_str() + lstrlen(kChromeProtocolPrefix)); |
| return true; |
| } |
| |
| return ParseAttachExternalTabUrl(); |
| } |
| |
| bool ChromeFrameUrl::ParseAttachExternalTabUrl() { |
| std::string query = parsed_url_.query(); |
| if (!StartsWithASCII(query, kAttachExternalTabPrefix, false)) { |
| return parsed_url_.is_valid(); |
| } |
| |
| attach_to_external_tab_ = true; |
| base::StringTokenizer tokenizer(query, "&"); |
| // Skip over kChromeAttachExternalTabPrefix |
| tokenizer.GetNext(); |
| // Read the following items in order. |
| // 1. cookie |
| // 2. disposition |
| // 3. dimension.x |
| // 4. dimension.y |
| // 5. dimension.width |
| // 6. dimension.height. |
| if (tokenizer.GetNext()) { |
| char* end_ptr = 0; |
| cookie_ = _strtoui64(tokenizer.token().c_str(), &end_ptr, 10); |
| } else { |
| return false; |
| } |
| |
| if (tokenizer.GetNext()) { |
| disposition_ = atoi(tokenizer.token().c_str()); |
| } else { |
| return false; |
| } |
| |
| if (tokenizer.GetNext()) { |
| dimensions_.set_x(atoi(tokenizer.token().c_str())); |
| } else { |
| return false; |
| } |
| |
| if (tokenizer.GetNext()) { |
| dimensions_.set_y(atoi(tokenizer.token().c_str())); |
| } else { |
| return false; |
| } |
| |
| if (tokenizer.GetNext()) { |
| dimensions_.set_width(atoi(tokenizer.token().c_str())); |
| } else { |
| return false; |
| } |
| |
| if (tokenizer.GetNext()) { |
| dimensions_.set_height(atoi(tokenizer.token().c_str())); |
| } else { |
| return false; |
| } |
| |
| if (tokenizer.GetNext()) { |
| profile_name_ = tokenizer.token(); |
| // Escape out special characters like %20, etc. |
| profile_name_ = net::UnescapeURLComponent(profile_name_, |
| net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS); |
| } else { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void ChromeFrameUrl::Reset() { |
| attach_to_external_tab_ = false; |
| is_chrome_protocol_ = false; |
| cookie_ = 0; |
| dimensions_.SetRect(0, 0, 0, 0); |
| disposition_ = 0; |
| profile_name_.clear(); |
| } |
| |
| bool CanNavigate(const GURL& url, |
| NavigationConstraints* navigation_constraints) { |
| if (!url.is_valid()) { |
| DLOG(ERROR) << "Invalid URL passed to InitiateNavigation: " << url; |
| return false; |
| } |
| |
| if (!navigation_constraints) { |
| NOTREACHED() << "Invalid NavigationConstraints passed in"; |
| return false; |
| } |
| |
| // No sanity checks if unsafe URLs are allowed |
| if (navigation_constraints->AllowUnsafeUrls()) |
| return true; |
| |
| if (!navigation_constraints->IsSchemeAllowed(url)) { |
| DLOG(WARNING) << __FUNCTION__ << " Disallowing navigation to url: " << url; |
| return false; |
| } |
| |
| if (!navigation_constraints->IsZoneAllowed(url)) { |
| DLOG(WARNING) << __FUNCTION__ |
| << " Disallowing navigation to restricted url: " << url; |
| return false; |
| } |
| return true; |
| } |
| |
| void WaitWithMessageLoop(HANDLE* handles, int count, DWORD timeout) { |
| base::Time now = base::Time::Now(); |
| base::Time wait_until = now + base::TimeDelta::FromMilliseconds(timeout); |
| |
| while (wait_until >= now) { |
| base::TimeDelta wait_time = wait_until - now; |
| DWORD wait = MsgWaitForMultipleObjects( |
| count, handles, FALSE, static_cast<DWORD>(wait_time.InMilliseconds()), |
| QS_ALLINPUT); |
| switch (wait) { |
| case WAIT_OBJECT_0: |
| case WAIT_TIMEOUT: |
| return; |
| |
| case WAIT_OBJECT_0 + 1: { |
| MSG msg = {0}; |
| while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { |
| TranslateMessage(&msg); |
| DispatchMessage(&msg); |
| } |
| break; |
| } |
| |
| default: { |
| NOTREACHED() << "Unexpected return from MsgWaitForMultipleObjects :" |
| << wait; |
| return; |
| } |
| } |
| now = base::Time::Now(); |
| } |
| } |
| |
| // Returns -1 if no directive is found, std::numeric_limits<int>::max() if the |
| // directive matches all IE versions ('Chrome=1') or the maximum IE version |
| // matched ('Chrome=IE7' => 7) |
| int GetXUaCompatibleDirective(const std::string& directive, char delimiter) { |
| net::HttpUtil::NameValuePairsIterator name_value_pairs(directive.begin(), |
| directive.end(), |
| delimiter); |
| |
| // Loop through the values until a valid 'Chrome=<FILTER>' entry is found |
| while (name_value_pairs.GetNext()) { |
| if (!LowerCaseEqualsASCII(name_value_pairs.name_begin(), |
| name_value_pairs.name_end(), |
| "chrome")) { |
| continue; |
| } |
| std::string::const_iterator filter_begin = name_value_pairs.value_begin(); |
| std::string::const_iterator filter_end = name_value_pairs.value_end(); |
| |
| size_t filter_length = filter_end - filter_begin; |
| |
| if (filter_length == 1 && *filter_begin == '1') { |
| return std::numeric_limits<int>::max(); |
| } |
| |
| if (filter_length < 3 || |
| !LowerCaseEqualsASCII(filter_begin, filter_begin + 2, "ie") || |
| !isdigit(*(filter_begin + 2))) { // ensure no leading +/- |
| continue; |
| } |
| |
| int header_ie_version = 0; |
| if (!base::StringToInt(base::StringPiece(filter_begin + 2, |
| filter_end), |
| &header_ie_version) || |
| header_ie_version == 0) { // ensure it's not a sequence of 0's |
| continue; |
| } |
| |
| // The first valid directive we find wins, whether it matches or not |
| return header_ie_version; |
| } |
| return -1; |
| } |
| |
| bool CheckXUaCompatibleDirective(const std::string& directive, |
| int ie_major_version) { |
| int header_ie_version = GetXUaCompatibleDirective(directive, ';'); |
| if (header_ie_version == -1) { |
| header_ie_version = GetXUaCompatibleDirective(directive, ','); |
| } |
| return header_ie_version >= ie_major_version; |
| } |
| |
| void EnumerateKeyValues(HKEY parent_key, const wchar_t* sub_key_name, |
| std::vector<std::wstring>* values) { |
| DCHECK(values); |
| base::win::RegistryValueIterator url_list(parent_key, sub_key_name); |
| while (url_list.Valid()) { |
| values->push_back(url_list.Value()); |
| ++url_list; |
| } |
| } |
| |
| std::wstring GetCurrentModuleVersion() { |
| scoped_ptr<FileVersionInfo> module_version_info( |
| FileVersionInfo::CreateFileVersionInfoForCurrentModule()); |
| DCHECK(module_version_info.get() != NULL); |
| return module_version_info->file_version(); |
| } |
| |
| bool IsChromeFrameDocument(IWebBrowser2* web_browser) { |
| if (!web_browser) |
| return false; |
| |
| base::win::ScopedComPtr<IDispatch> doc; |
| web_browser->get_Document(doc.Receive()); |
| if (doc) { |
| // Detect if CF is rendering based on whether the document is a |
| // ChromeActiveDocument. Detecting based on hwnd is problematic as |
| // the CF Active Document window may not have been created yet. |
| base::win::ScopedComPtr<IChromeFrame> chrome_frame; |
| chrome_frame.QueryFrom(doc); |
| return chrome_frame.get() != NULL; |
| } |
| return false; |
| } |
| |
| bool IncreaseWinInetConnections(DWORD connections) { |
| static bool wininet_connection_count_updated = false; |
| if (wininet_connection_count_updated) { |
| return true; |
| } |
| |
| static int connection_options[] = { |
| INTERNET_OPTION_MAX_CONNS_PER_SERVER, |
| INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER, |
| }; |
| |
| BOOL ret = FALSE; |
| |
| for (int option_index = 0; option_index < arraysize(connection_options); |
| ++option_index) { |
| DWORD connection_value_size = sizeof(DWORD); |
| DWORD current_connection_limit = 0; |
| InternetQueryOption(NULL, connection_options[option_index], |
| ¤t_connection_limit, &connection_value_size); |
| if (current_connection_limit > connections) { |
| continue; |
| } |
| |
| ret = InternetSetOption(NULL, connection_options[option_index], |
| &connections, connection_value_size); |
| if (!ret) { |
| return false; |
| } |
| } |
| wininet_connection_count_updated = true; |
| return true; |
| } |
| |
| void GetChromeFrameProfilePath(const string16& profile_name, |
| base::FilePath* profile_path) { |
| chrome::GetChromeFrameUserDataDirectory(profile_path); |
| *profile_path = profile_path->Append(profile_name); |
| DVLOG(1) << __FUNCTION__ << ": " << profile_path->value(); |
| } |