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