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