blob: cea60f83a76593543b6d7ff308ed3fde34523476 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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// chrome_tab.cc : Implementation of DLL Exports.
6
7// Need to include this before the ATL headers below.
8#include "chrome_frame/chrome_tab.h"
9
10#include <atlsecurity.h>
11#include <objbase.h>
12
13#include "base/at_exit.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000014#include "base/basictypes.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015#include "base/command_line.h"
16#include "base/file_util.h"
17#include "base/file_version_info.h"
18#include "base/logging.h"
19#include "base/logging_win.h"
20#include "base/metrics/field_trial.h"
21#include "base/path_service.h"
22#include "base/string16.h"
23#include "base/string_number_conversions.h"
24#include "base/string_piece.h"
25#include "base/string_util.h"
26#include "base/sys_string_conversions.h"
27#include "base/utf_string_conversions.h"
28#include "base/win/registry.h"
29#include "base/win/windows_version.h"
30#include "chrome/common/chrome_constants.h"
31#include "chrome/common/chrome_switches.h"
32#include "chrome/common/metrics/entropy_provider.h"
33#include "chrome/installer/util/google_update_settings.h"
34#include "chrome_frame/bho.h"
35#include "chrome_frame/chrome_active_document.h"
36#include "chrome_frame/chrome_frame_activex.h"
37#include "chrome_frame/chrome_frame_automation.h"
38#include "chrome_frame/chrome_frame_reporting.h"
39#include "chrome_frame/chrome_launcher_utils.h"
40#include "chrome_frame/chrome_protocol.h"
41#include "chrome_frame/dll_redirector.h"
42#include "chrome_frame/exception_barrier.h"
43#include "chrome_frame/metrics_service.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000044#include "chrome_frame/pin_module.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000045#include "chrome_frame/resource.h"
46#include "chrome_frame/utils.h"
47#include "googleurl/src/url_util.h"
48#include "grit/chrome_frame_resources.h"
49
50using base::win::RegKey;
51
52namespace {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000053
54const wchar_t kInternetSettings[] =
55 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
56
57const wchar_t kProtocolHandlers[] =
58 L"Software\\Classes\\Protocols\\Handler";
59
60const wchar_t kRunOnce[] =
61 L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce";
62
63const wchar_t kRunKeyName[] = L"ChromeFrameHelper";
64
65const wchar_t kChromeFrameHelperExe[] = L"chrome_frame_helper.exe";
66const wchar_t kChromeFrameHelperStartupArg[] = L"--startup";
67
68// Window class and window names.
69// TODO(robertshield): These and other constants need to be refactored into
70// a common chrome_frame_constants.h|cc and built into a separate lib
71// (either chrome_frame_utils or make another one).
72const wchar_t kChromeFrameHelperWindowClassName[] =
73 L"ChromeFrameHelperWindowClass";
74const wchar_t kChromeFrameHelperWindowName[] =
75 L"ChromeFrameHelperWindowName";
76
77// {0562BFC3-2550-45b4-BD8E-A310583D3A6F}
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000078const GUID kChromeFrameProvider =
Torne (Richard Coles)58218062012-11-14 11:43:16 +000079 { 0x562bfc3, 0x2550, 0x45b4,
80 { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } };
81
82const wchar_t kPostPlatformUAKey[] =
83 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\"
84 L"User Agent\\Post Platform";
85const wchar_t kChromeFramePrefix[] = L"chromeframe/";
86
87// See comments in DllGetClassObject.
88LPFNGETCLASSOBJECT g_dll_get_class_object_redir_ptr = NULL;
89
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000090// This function has the side effect of initializing an unprotected
91// vector pointer inside GoogleUrl. If this is called during DLL loading,
92// it has the effect of avoiding an initialization race on that pointer.
93// TODO(siggi): fix GoogleUrl.
94void InitGoogleUrl() {
95 static const char kDummyUrl[] = "http://www.google.com";
96
97 url_util::IsStandard(kDummyUrl,
98 url_parse::MakeRange(0, arraysize(kDummyUrl)));
99}
100
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000101class ChromeTabModule : public CAtlDllModuleT<ChromeTabModule> {
102 public:
103 typedef CAtlDllModuleT<ChromeTabModule> ParentClass;
104
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000105 ChromeTabModule() : do_system_registration_(true), crash_reporting_(NULL) {}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000106
107 DECLARE_LIBID(LIBID_ChromeTabLib)
108 DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CHROMETAB,
109 "{FD9B1B31-F4D8-436A-8F4F-D3C2E36733D3}")
110
111 // Override to add our SYSTIME binary value to registry scripts.
112 // See chrome_frame_activex.rgs for usage.
113 virtual HRESULT AddCommonRGSReplacements(IRegistrarBase* registrar) throw() {
114 HRESULT hr = ParentClass::AddCommonRGSReplacements(registrar);
115
116 if (SUCCEEDED(hr)) {
117 SYSTEMTIME local_time;
118 ::GetSystemTime(&local_time);
119 std::string hex(base::HexEncode(&local_time, sizeof(local_time)));
120 base::StringPiece sp_hex(hex);
121 hr = registrar->AddReplacement(L"SYSTIME",
122 base::SysNativeMBToWide(sp_hex).c_str());
123 DCHECK(SUCCEEDED(hr));
124 }
125
126 if (SUCCEEDED(hr)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000127 base::FilePath app_path =
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000128 chrome_launcher::GetChromeExecutablePath().DirName();
129 hr = registrar->AddReplacement(L"CHROME_APPPATH",
130 app_path.value().c_str());
131 DCHECK(SUCCEEDED(hr));
132 }
133
134 if (SUCCEEDED(hr)) {
135 hr = registrar->AddReplacement(L"CHROME_APPNAME",
136 chrome::kBrowserProcessExecutableName);
137 DCHECK(SUCCEEDED(hr));
138
139 // Fill in VERSION from the VERSIONINFO stored in the DLL's resources.
140 scoped_ptr<FileVersionInfo> module_version_info(
141 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
142 DCHECK(module_version_info != NULL);
143 std::wstring file_version(module_version_info->file_version());
144 hr = registrar->AddReplacement(L"VERSION", file_version.c_str());
145 DCHECK(SUCCEEDED(hr));
146 }
147
148 if (SUCCEEDED(hr)) {
149 // Add the directory of chrome_launcher.exe. This will be the same
150 // as the directory for the current DLL.
151 std::wstring module_dir;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000152 base::FilePath module_path;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000153 if (PathService::Get(base::FILE_MODULE, &module_path)) {
154 module_dir = module_path.DirName().value();
155 } else {
156 NOTREACHED();
157 }
158 hr = registrar->AddReplacement(L"CHROME_LAUNCHER_APPPATH",
159 module_dir.c_str());
160 DCHECK(SUCCEEDED(hr));
161 }
162
163 if (SUCCEEDED(hr)) {
164 // Add the filename of chrome_launcher.exe
165 hr = registrar->AddReplacement(L"CHROME_LAUNCHER_APPNAME",
166 chrome_launcher::kLauncherExeBaseName);
167 DCHECK(SUCCEEDED(hr));
168 }
169
170 if (SUCCEEDED(hr)) {
171 // Add the registry hive to use.
172 // Note: This is ugly as hell. I'd rather use the pMapEntries parameter
173 // to CAtlModule::UpdateRegistryFromResource, unfortunately we have a
174 // few components that are registered by calling their
175 // static T::UpdateRegistry() methods directly, which doesn't allow
176 // pMapEntries to be passed through :-(
177 if (do_system_registration_) {
178 hr = registrar->AddReplacement(L"HIVE", L"HKLM");
179 } else {
180 hr = registrar->AddReplacement(L"HIVE", L"HKCU");
181 }
182 DCHECK(SUCCEEDED(hr));
183 }
184
185 if (SUCCEEDED(hr)) {
186 // Add the Chrome Frame CLSID.
187 wchar_t cf_clsid[64];
188 StringFromGUID2(CLSID_ChromeFrame, &cf_clsid[0], arraysize(cf_clsid));
189 hr = registrar->AddReplacement(L"CHROME_FRAME_CLSID", &cf_clsid[0]);
190 }
191
192 return hr;
193 }
194
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000195 // The module is "locked" when an object takes a reference on it. The first
196 // time it is locked, take a reference on crash reporting to bind its lifetime
197 // to the module.
198 virtual LONG Lock() throw() {
199 LONG result = ParentClass::Lock();
200 if (result == 1) {
201 DCHECK_EQ(crash_reporting_,
202 static_cast<chrome_frame::ScopedCrashReporting*>(NULL));
203 crash_reporting_ = new chrome_frame::ScopedCrashReporting();
204 }
205 return result;
206 }
207
208 // The module is "unlocked" when an object that had a reference on it is
209 // destroyed. The last time it is unlocked, release the reference on crash
210 // reporting.
211 virtual LONG Unlock() throw() {
212 LONG result = ParentClass::Unlock();
213 if (!result) {
214 DCHECK_NE(crash_reporting_,
215 static_cast<chrome_frame::ScopedCrashReporting*>(NULL));
216 delete crash_reporting_;
217 crash_reporting_ = NULL;
218 }
219 return result;
220 }
221
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000222 // See comments in AddCommonRGSReplacements
223 bool do_system_registration_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000224
225 private:
226 // A scoper created when the module is initially locked and destroyed when it
227 // is finally unlocked. This is not a scoped_ptr since that could cause
228 // reporting to shut down at exit, which would lead to problems with the
229 // loader lock.
230 chrome_frame::ScopedCrashReporting* crash_reporting_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000231};
232
233ChromeTabModule _AtlModule;
234
235base::AtExitManager* g_exit_manager = NULL;
236base::FieldTrialList* g_field_trial_list = NULL;
237
238HRESULT RefreshElevationPolicy() {
239 const wchar_t kIEFrameDll[] = L"ieframe.dll";
240 const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
241 HRESULT hr = E_NOTIMPL;
242
243 // Stick an SEH in the chain to prevent the VEH from picking up on first
244 // chance exceptions caused by loading ieframe.dll. Use the vanilla
245 // ExceptionBarrier to report any exceptions that do make their way to us
246 // though.
247 ExceptionBarrier barrier;
248
249 HMODULE ieframe_module = LoadLibrary(kIEFrameDll);
250 if (ieframe_module) {
251 typedef HRESULT (__stdcall *IERefreshPolicy)();
252 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
253 GetProcAddress(ieframe_module, kIERefreshPolicy));
254
255 if (ie_refresh_policy) {
256 hr = ie_refresh_policy();
257 } else {
258 hr = HRESULT_FROM_WIN32(GetLastError());
259 }
260
261 FreeLibrary(ieframe_module);
262 } else {
263 hr = HRESULT_FROM_WIN32(GetLastError());
264 }
265
266 return hr;
267}
268
269// Experimental boot prefetch optimization for Chrome Frame
270//
271// If chrome is warmed up during a single reboot, it gets paged
272// in for subsequent reboots and the cold startup times essentially
273// look like warm times thereafter! The 'warm up' is done by
274// setting up a 'RunOnce' key during DLLRegisterServer of
275// npchrome_frame.dll.
276//
277// This works because chrome prefetch becomes part of boot
278// prefetch file ntosboot-b00dfaad.pf and paged in on subsequent
279// reboots. As long as the sytem does not undergo significant
280// memory pressure those pages remain in memory and we get pretty
281// amazing startup times, down to about 300 ms from 1200 ms
282//
283// The downside is:
284// - Whether chrome frame is used or not, there's a read penalty
285// (1200-300 =) 900 ms for every boot.
286// - Heavy system memory usage after reboot will nullify the benefits
287// but the user will still pay the cost.
288// - Overall the time saved will always be less than total time spent
289// paging in chrome
290// - We are not sure when the chrome 'warm up' will age out from the
291// boot prefetch file.
292//
293// The idea here is to try this out on chrome frame dev channel
294// and see if it produces a significant drift in startup numbers.
295HRESULT SetupRunOnce() {
296 HRESULT result = E_FAIL;
297
298 string16 channel_name;
299 if (base::win::GetVersion() < base::win::VERSION_VISTA &&
300 GoogleUpdateSettings::GetChromeChannelAndModifiers(true, &channel_name)) {
301 std::transform(channel_name.begin(), channel_name.end(),
302 channel_name.begin(), tolower);
303 // Use this only for the dev channel.
304 if (channel_name.find(L"dev") != string16::npos) {
305 HKEY hive = HKEY_CURRENT_USER;
306 if (IsSystemProcess()) {
307 // For system installs, our updates will be running as SYSTEM which
308 // makes writing to a RunOnce key under HKCU not so terribly useful.
309 hive = HKEY_LOCAL_MACHINE;
310 }
311
312 RegKey run_once;
313 LONG ret = run_once.Create(hive, kRunOnce, KEY_READ | KEY_WRITE);
314 if (ret == ERROR_SUCCESS) {
315 CommandLine run_once_cmd(chrome_launcher::GetChromeExecutablePath());
316 run_once_cmd.AppendSwitchASCII(switches::kAutomationClientChannelID,
317 "0");
318 run_once_cmd.AppendSwitch(switches::kChromeFrame);
319 ret = run_once.WriteValue(L"A",
320 run_once_cmd.GetCommandLineString().c_str());
321 }
322 result = HRESULT_FROM_WIN32(ret);
323 } else {
324 result = S_FALSE;
325 }
326 } else {
327 // We're on a non-XP version of Windows or on a stable channel. Nothing
328 // needs doing.
329 result = S_FALSE;
330 }
331
332 return result;
333}
334
335// Helper method called for user-level installs where we don't have admin
336// permissions. Starts up the long running process and registers it to get it
337// started at next boot.
338HRESULT SetupUserLevelHelper() {
339 HRESULT hr = S_OK;
340
341 // Remove existing run-at-startup entry.
342 base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, kRunKeyName);
343
344 // Build the chrome_frame_helper command line.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000345 base::FilePath module_path;
346 base::FilePath helper_path;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000347 if (PathService::Get(base::FILE_MODULE, &module_path)) {
348 module_path = module_path.DirName();
349 helper_path = module_path.Append(kChromeFrameHelperExe);
350 if (!file_util::PathExists(helper_path)) {
351 // If we can't find the helper in the current directory, try looking
352 // one up (this is the layout in the build output folder).
353 module_path = module_path.DirName();
354 helper_path = module_path.Append(kChromeFrameHelperExe);
355 DCHECK(file_util::PathExists(helper_path)) <<
356 "Could not find chrome_frame_helper.exe.";
357 }
358
359 // Find window handle of existing instance.
360 HWND old_window = FindWindow(kChromeFrameHelperWindowClassName,
361 kChromeFrameHelperWindowName);
362
363 if (file_util::PathExists(helper_path)) {
364 std::wstring helper_path_cmd(L"\"");
365 helper_path_cmd += helper_path.value();
366 helper_path_cmd += L"\" ";
367 helper_path_cmd += kChromeFrameHelperStartupArg;
368
369 // Add new run-at-startup entry.
370 if (!base::win::AddCommandToAutoRun(HKEY_CURRENT_USER, kRunKeyName,
371 helper_path_cmd)) {
372 hr = E_FAIL;
373 LOG(ERROR) << "Could not add helper process to auto run key.";
374 }
375
376 // Start new instance.
377 base::LaunchOptions options;
378 options.start_hidden = true;
379 bool launched = base::LaunchProcess(helper_path.value(), options, NULL);
380 if (!launched) {
381 hr = E_FAIL;
382 PLOG(DFATAL) << "Could not launch helper process.";
383 }
384
385 // Kill old instance using window handle.
386 if (IsWindow(old_window)) {
387 BOOL result = PostMessage(old_window, WM_CLOSE, 0, 0);
388 if (!result) {
389 PLOG(ERROR) << "Failed to post close message to old helper process: ";
390 }
391 }
392 } else {
393 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
394 }
395 } else {
396 hr = E_UNEXPECTED;
397 NOTREACHED();
398 }
399
400 return hr;
401}
402
403// To delete the user agent, set value to NULL.
404// The is_system parameter indicates whether this is a per machine or a per
405// user installation.
406HRESULT SetChromeFrameUA(bool is_system, const wchar_t* value) {
407 HRESULT hr = E_FAIL;
408 HKEY parent_hive = is_system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
409
410 RegKey ua_key;
411 LONG reg_result = ua_key.Create(parent_hive, kPostPlatformUAKey,
412 KEY_READ | KEY_WRITE);
413 if (reg_result == ERROR_SUCCESS) {
414 // Make sure that we unregister ChromeFrame UA strings registered previously
415 wchar_t value_name[MAX_PATH + 1] = {};
416 wchar_t value_data[MAX_PATH + 1] = {};
417
418 DWORD value_index = 0;
419 while (value_index < ua_key.GetValueCount()) {
420 DWORD name_size = arraysize(value_name);
421 DWORD value_size = arraysize(value_data);
422 DWORD type = 0;
423 LRESULT ret = ::RegEnumValue(ua_key.Handle(), value_index, value_name,
424 &name_size, NULL, &type,
425 reinterpret_cast<BYTE*>(value_data),
426 &value_size);
427 if (ret == ERROR_SUCCESS) {
428 if (StartsWith(value_name, kChromeFramePrefix, false)) {
429 ua_key.DeleteValue(value_name);
430 } else {
431 ++value_index;
432 }
433 } else {
434 break;
435 }
436 }
437
438 std::wstring chrome_frame_ua_value_name = kChromeFramePrefix;
439 chrome_frame_ua_value_name += GetCurrentModuleVersion();
440 if (value) {
441 ua_key.WriteValue(chrome_frame_ua_value_name.c_str(), value);
442 }
443 hr = S_OK;
444 } else {
445 DLOG(ERROR) << __FUNCTION__ << ": " << kPostPlatformUAKey
446 << ", error code = " << reg_result;
447 hr = HRESULT_FROM_WIN32(reg_result);
448 }
449 return hr;
450}
451
452class SecurityDescBackup {
453 public:
454 explicit SecurityDescBackup(const std::wstring& backup_key)
455 : backup_key_name_(backup_key) {}
456 ~SecurityDescBackup() {}
457
458 // Save given security descriptor to the backup key.
459 bool SaveSecurity(const CSecurityDesc& sd) {
460 CString str;
461 if (!sd.ToString(&str))
462 return false;
463
464 RegKey backup_key(HKEY_LOCAL_MACHINE, backup_key_name_.c_str(),
465 KEY_READ | KEY_WRITE);
466 if (backup_key.Valid()) {
467 return backup_key.WriteValue(NULL, str.GetString()) == ERROR_SUCCESS;
468 }
469
470 return false;
471 }
472
473 // Restore security descriptor from backup key to given key name.
474 bool RestoreSecurity(const wchar_t* key_name) {
475 std::wstring sddl;
476 if (!ReadBackupKey(&sddl))
477 return false;
478
479 // Create security descriptor from string.
480 CSecurityDesc sd;
481 if (!sd.FromString(sddl.c_str()))
482 return false;
483
484 bool result = true;
485 // Restore DACL and Owner of the key from saved security descriptor.
486 CDacl dacl;
487 CSid owner;
488 sd.GetDacl(&dacl);
489 sd.GetOwner(&owner);
490
491 DWORD error = ::SetNamedSecurityInfo(const_cast<wchar_t*>(key_name),
492 SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
493 const_cast<SID*>(owner.GetPSID()), NULL,
494 const_cast<ACL*>(dacl.GetPACL()), NULL);
495
496 DeleteBackupKey();
497 return (error == ERROR_SUCCESS);
498 }
499
500 private:
501 // Read SDDL string from backup key
502 bool ReadBackupKey(std::wstring* sddl) {
503 RegKey backup_key(HKEY_LOCAL_MACHINE, backup_key_name_.c_str(), KEY_READ);
504 if (!backup_key.Valid())
505 return false;
506
507 DWORD len = 0;
508 DWORD reg_type = REG_NONE;
509 if (backup_key.ReadValue(NULL, NULL, &len, &reg_type) != ERROR_SUCCESS)
510 return false;
511 DCHECK_EQ(0u, len % sizeof(wchar_t));
512
513 if ((len == 0) || (reg_type != REG_SZ))
514 return false;
515
516 size_t wchar_count = 1 + len / sizeof(wchar_t);
517 if (backup_key.ReadValue(NULL, WriteInto(sddl, wchar_count), &len,
518 &reg_type) != ERROR_SUCCESS) {
519 return false;
520 }
521
522 return true;
523 }
524
525 void DeleteBackupKey() {
526 ::RegDeleteKey(HKEY_LOCAL_MACHINE, backup_key_name_.c_str());
527 }
528
529 std::wstring backup_key_name_;
530};
531
532struct TokenWithPrivileges {
533 TokenWithPrivileges() {
534 token_.GetEffectiveToken(TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY);
535 token_.GetUser(&user_);
536 }
537
538 ~TokenWithPrivileges() {
539 token_.EnableDisablePrivileges(take_ownership_);
540 token_.EnableDisablePrivileges(restore_);
541 }
542
543 bool EnablePrivileges() {
544 if (take_ownership_.GetCount() == 0)
545 if (!token_.EnablePrivilege(L"SeTakeOwnershipPrivilege",
546 &take_ownership_))
547 return false;
548
549 if (restore_.GetCount() == 0)
550 if (!token_.EnablePrivilege(L"SeRestorePrivilege", &restore_))
551 return false;
552
553 return true;
554 }
555
556 const CSid& GetUser() const {
557 return user_;
558 }
559
560 private:
561 CAccessToken token_;
562 CTokenPrivileges take_ownership_;
563 CTokenPrivileges restore_;
564 CSid user_;
565};
566
567HRESULT SetOrDeleteMimeHandlerKey(bool set, HKEY root_key) {
568 std::wstring key_name = kInternetSettings;
569 key_name.append(L"\\Secure Mime Handlers");
570 RegKey key(root_key, key_name.c_str(), KEY_READ | KEY_WRITE);
571 if (!key.Valid())
572 return false;
573
574 LONG result1 = ERROR_SUCCESS;
575 LONG result2 = ERROR_SUCCESS;
576 if (set) {
577 result1 = key.WriteValue(L"ChromeTab.ChromeActiveDocument", 1);
578 result2 = key.WriteValue(L"ChromeTab.ChromeActiveDocument.1", 1);
579 } else {
580 result1 = key.DeleteValue(L"ChromeTab.ChromeActiveDocument");
581 result2 = key.DeleteValue(L"ChromeTab.ChromeActiveDocument.1");
582 }
583
584 return result1 != ERROR_SUCCESS ? HRESULT_FROM_WIN32(result1) :
585 HRESULT_FROM_WIN32(result2);
586}
587
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000588void OnPinModule() {
589 // Pin crash reporting by leaking a reference.
590 ignore_result(new chrome_frame::ScopedCrashReporting());
591}
592
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000593// Chrome Frame registration functions.
594//-----------------------------------------------------------------------------
595HRESULT RegisterSecuredMimeHandler(bool enable, bool is_system) {
596 if (!is_system) {
597 return SetOrDeleteMimeHandlerKey(enable, HKEY_CURRENT_USER);
598 } else if (base::win::GetVersion() < base::win::VERSION_VISTA) {
599 return SetOrDeleteMimeHandlerKey(enable, HKEY_LOCAL_MACHINE);
600 }
601
602 std::wstring mime_key = kInternetSettings;
603 mime_key.append(L"\\Secure Mime Handlers");
604 std::wstring backup_key = kInternetSettings;
605 backup_key.append(L"\\__backup_SMH__");
606 std::wstring object_name = L"MACHINE\\";
607 object_name.append(mime_key);
608
609 TokenWithPrivileges token_;
610 if (!token_.EnablePrivileges())
611 return E_ACCESSDENIED;
612
613 // If there is a backup key - something bad happened; try to restore
614 // security on "Secure Mime Handlers" from the backup.
615 SecurityDescBackup backup(backup_key);
616 backup.RestoreSecurity(object_name.c_str());
617
618 // Read old security descriptor of the Mime key first.
619 CSecurityDesc sd;
620 if (!AtlGetSecurityDescriptor(object_name.c_str(), SE_REGISTRY_KEY, &sd)) {
621 return E_FAIL;
622 }
623
624 backup.SaveSecurity(sd);
625 HRESULT hr = E_FAIL;
626 // set new owner
627 if (AtlSetOwnerSid(object_name.c_str(), SE_REGISTRY_KEY, token_.GetUser())) {
628 // set new dacl
629 CDacl new_dacl;
630 sd.GetDacl(&new_dacl);
631 new_dacl.AddAllowedAce(token_.GetUser(), GENERIC_WRITE | GENERIC_READ);
632 if (AtlSetDacl(object_name.c_str(), SE_REGISTRY_KEY, new_dacl)) {
633 hr = SetOrDeleteMimeHandlerKey(enable, HKEY_LOCAL_MACHINE);
634 }
635 }
636
637 backup.RestoreSecurity(object_name.c_str());
638 return hr;
639}
640
641HRESULT RegisterActiveDoc(bool reg, bool is_system) {
642 // We have to call the static T::UpdateRegistry function instead of
643 // _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ACTIVEDOC, reg)
644 // because there is specific OLEMISC replacement.
645 return ChromeActiveDocument::UpdateRegistry(reg);
646}
647
648HRESULT RegisterActiveX(bool reg, bool is_system) {
649 // We have to call the static T::UpdateRegistry function instead of
650 // _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ACTIVEX, reg)
651 // because there is specific OLEMISC replacement.
652 return ChromeFrameActivex::UpdateRegistry(reg);
653}
654
655HRESULT RegisterElevationPolicy(bool reg, bool is_system) {
656 HRESULT hr = S_OK;
657 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
658 // Register the elevation policy. This must succeed for Chrome Frame to
659 // be able launch Chrome when running in low-integrity IE.
660 hr = _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ELEVATION, reg);
661 if (SUCCEEDED(hr)) {
662 // Ignore failures since old versions of IE 7 (e.g., 7.0.6000.16386, which
663 // shipped with Vista RTM) do not export IERefreshElevationPolicy.
664 RefreshElevationPolicy();
665 }
666 }
667 return hr;
668}
669
670HRESULT RegisterProtocol(bool reg, bool is_system) {
671 return _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEPROTOCOL, reg);
672}
673
674HRESULT RegisterBhoClsid(bool reg, bool is_system) {
675 return Bho::UpdateRegistry(reg);
676}
677
678HRESULT RegisterBhoIE(bool reg, bool is_system) {
679 if (is_system) {
680 return _AtlModule.UpdateRegistryFromResourceS(IDR_REGISTER_BHO, reg);
681 } else {
682 if (reg) {
683 // Setup the long running process:
684 return SetupUserLevelHelper();
685 } else {
686 // Unschedule the user-level helper. Note that we don't kill it here
687 // so that during updates we don't have a time window with no running
688 // helper. Uninstalls and updates will explicitly kill the helper from
689 // within the installer. Unregister existing run-at-startup entry.
690 return base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER,
691 kRunKeyName) ? S_OK : E_FAIL;
692 }
693 }
694}
695
696HRESULT RegisterTypeLib(bool reg, bool is_system) {
697 if (reg && !is_system) {
698 // Enables the RegisterTypeLib Function function to override default
699 // registry mappings under Windows Vista Service Pack 1 (SP1),
700 // Windows Server 2008, and later operating system versions
701 typedef void (WINAPI* OaEnablePerUserTypeLibReg)(void);
702 OaEnablePerUserTypeLibReg per_user_typelib_func =
703 reinterpret_cast<OaEnablePerUserTypeLibReg>(
704 GetProcAddress(GetModuleHandle(L"oleaut32.dll"),
705 "OaEnablePerUserTLibRegistration"));
706 if (per_user_typelib_func) {
707 (*per_user_typelib_func)();
708 }
709 }
710 return reg ?
711 UtilRegisterTypeLib(_AtlComModule.m_hInstTypeLib,
712 NULL, !is_system) :
713 UtilUnRegisterTypeLib(_AtlComModule.m_hInstTypeLib,
714 NULL, !is_system);
715}
716
717HRESULT RegisterLegacyNPAPICleanup(bool reg, bool is_system) {
718 if (!reg) {
719 _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_NPAPI, reg);
720 UtilRemovePersistentNPAPIMarker();
721 }
722 // Ignore failures.
723 return S_OK;
724}
725
726HRESULT RegisterAppId(bool reg, bool is_system) {
727 return _AtlModule.UpdateRegistryAppId(reg);
728}
729
730HRESULT RegisterUserAgent(bool reg, bool is_system) {
731 if (reg) {
732 return SetChromeFrameUA(is_system, L"1");
733 } else {
734 return SetChromeFrameUA(is_system, NULL);
735 }
736}
737
738enum RegistrationStepId {
739 kStepSecuredMimeHandler = 0,
740 kStepActiveDoc = 1,
741 kStepActiveX = 2,
742 kStepElevationPolicy = 3,
743 kStepProtocol = 4,
744 kStepBhoClsid = 5,
745 kStepBhoRegistration = 6,
746 kStepRegisterTypeLib = 7,
747 kStepNpapiCleanup = 8,
748 kStepAppId = 9,
749 kStepUserAgent = 10,
750 kStepEnd = 11
751};
752
753enum RegistrationFlags {
754 ACTIVEX = 0x0001,
755 ACTIVEDOC = 0x0002,
756 GCF_PROTOCOL = 0x0004,
757 BHO_CLSID = 0x0008,
758 BHO_REGISTRATION = 0x0010,
759 TYPELIB = 0x0020,
760
761 ALL = 0xFFFF
762};
763
764// Mux the failure step into the hresult. We take only the first four bits
765// and stick those into the top four bits of the facility code. We also set the
766// Customer bit to be polite. Graphically, we write our error code to the
767// bits marked with ^:
768// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
769// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
770// +---+-+-+-----------------------+-------------------------------+
771// |Sev|C|R| Facility | Code |
772// +---+-+-+-----------------------+-------------------------------+
773// ^ ^ ^ ^ ^
774// See http://msdn.microsoft.com/en-us/library/cc231198(PROT.10).aspx for
775// more details on HRESULTS.
776//
777// The resulting error can be extracted by:
778// error_code = (fiddled_hr & 0x07800000) >> 23
779HRESULT MuxErrorIntoHRESULT(HRESULT hr, int error_code) {
780 DCHECK_GE(error_code, 0);
781 DCHECK_LT(error_code, kStepEnd);
782 COMPILE_ASSERT(kStepEnd <= 0xF, update_error_muxing_too_many_steps);
783
784 // Check that our four desired bits are clear.
785 // 0xF87FFFFF == 11111000011111111111111111111111
786 DCHECK_EQ(static_cast<HRESULT>(hr & 0xF87FFFFF), hr);
787
788 HRESULT fiddled_hr = ((error_code & 0xF) << 23) | hr;
789 fiddled_hr |= 1 << 29; // Set the customer bit.
790
791 return fiddled_hr;
792}
793
794HRESULT CustomRegistration(uint16 reg_flags, bool reg, bool is_system) {
795 if (reg && (reg_flags & (ACTIVEDOC | ACTIVEX)))
796 reg_flags |= (TYPELIB | GCF_PROTOCOL);
797
798 // Set the flag that gets checked in AddCommonRGSReplacements before doing
799 // registration work.
800 _AtlModule.do_system_registration_ = is_system;
801
802 typedef HRESULT (*RegistrationFn)(bool reg, bool is_system);
803 struct RegistrationStep {
804 RegistrationStepId id;
805 uint16 condition;
806 RegistrationFn func;
807 };
808 static const RegistrationStep registration_steps[] = {
809 { kStepSecuredMimeHandler, ACTIVEDOC, &RegisterSecuredMimeHandler },
810 { kStepActiveDoc, ACTIVEDOC, &RegisterActiveDoc },
811 { kStepActiveX, ACTIVEX, &RegisterActiveX },
812 { kStepElevationPolicy, (ACTIVEDOC | ACTIVEX), &RegisterElevationPolicy },
813 { kStepProtocol, GCF_PROTOCOL, &RegisterProtocol },
814 { kStepBhoClsid, BHO_CLSID, &RegisterBhoClsid },
815 { kStepBhoRegistration, BHO_REGISTRATION, &RegisterBhoIE },
816 { kStepRegisterTypeLib, TYPELIB, &RegisterTypeLib },
817 { kStepNpapiCleanup, ALL, &RegisterLegacyNPAPICleanup },
818 { kStepAppId, ALL, &RegisterAppId },
819 { kStepUserAgent, ALL, &RegisterUserAgent }
820 };
821
822 HRESULT hr = S_OK;
823
824 bool rollback = false;
825 int failure_step = 0;
826 HRESULT step_hr = S_OK;
827 for (int step = 0; step < arraysize(registration_steps); ++step) {
828 if ((reg_flags & registration_steps[step].condition) != 0) {
829 step_hr = registration_steps[step].func(reg, is_system);
830 if (FAILED(step_hr)) {
831 // Store only the first failing HRESULT with the step value muxed in.
832 if (hr == S_OK) {
833 hr = MuxErrorIntoHRESULT(step_hr, step);
834 }
835
836 // On registration if a step fails, we abort and rollback.
837 if (reg) {
838 rollback = true;
839 failure_step = step;
840 break;
841 }
842 }
843 }
844 }
845
846 if (rollback) {
847 DCHECK(reg);
848 // Rollback the failing action and all preceding ones.
849 for (int step = failure_step; step >= 0; --step) {
850 registration_steps[step].func(!reg, is_system);
851 }
852 }
853
854 return hr;
855}
856
857} // namespace
858
859// DLL Entry Point
860extern "C" BOOL WINAPI DllMain(HINSTANCE instance,
861 DWORD reason,
862 LPVOID reserved) {
863 UNREFERENCED_PARAMETER(instance);
864 if (reason == DLL_PROCESS_ATTACH) {
865#ifndef NDEBUG
866 // Silence traces from the ATL registrar to reduce the log noise.
867 ATL::CTrace::s_trace.ChangeCategory(atlTraceRegistrar, 0,
868 ATLTRACESTATUS_DISABLED);
869#endif
870 InitGoogleUrl();
871
872 g_exit_manager = new base::AtExitManager();
873 CommandLine::Init(0, NULL);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000874 logging::InitLogging(
875 NULL,
876 logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
877 logging::LOCK_LOG_FILE,
878 logging::DELETE_OLD_LOG_FILE,
879 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
880
881 // Log the same items as Chrome.
882 logging::SetLogItems(true, // enable_process_id
883 true, // enable_thread_id
884 false, // enable_timestamp
885 true); // enable_tickcount
886
887 DllRedirector* dll_redirector = DllRedirector::GetInstance();
888 DCHECK(dll_redirector);
889
890 if (!dll_redirector->RegisterAsFirstCFModule()) {
891 // Someone else was here first, try and get a pointer to their
892 // DllGetClassObject export:
893 g_dll_get_class_object_redir_ptr =
894 dll_redirector->GetDllGetClassObjectPtr();
895 DCHECK(g_dll_get_class_object_redir_ptr != NULL)
896 << "Found CF module with no DllGetClassObject export.";
897 }
898
899 // Enable trace control and transport through event tracing for Windows.
900 logging::LogEventProvider::Initialize(kChromeFrameProvider);
901
902 // Initialize the field test infrastructure. Must be done somewhere that
903 // can only get called once. For Chrome Frame, that is here.
904 g_field_trial_list = new base::FieldTrialList(
905 new metrics::SHA1EntropyProvider(MetricsService::GetClientID()));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000906
907 // Set a callback so that crash reporting can be pinned when the module is
908 // pinned.
909 chrome_frame::SetPinModuleCallback(&OnPinModule);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000910 } else if (reason == DLL_PROCESS_DETACH) {
911 delete g_field_trial_list;
912 g_field_trial_list = NULL;
913
914 DllRedirector* dll_redirector = DllRedirector::GetInstance();
915 DCHECK(dll_redirector);
916 dll_redirector->UnregisterAsFirstCFModule();
917
918 g_patch_helper.UnpatchIfNeeded();
919
920 delete g_exit_manager;
921 g_exit_manager = NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000922 }
923 return _AtlModule.DllMain(reason, reserved);
924}
925
926// Used to determine whether the DLL can be unloaded by OLE
927STDAPI DllCanUnloadNow() {
928 return _AtlModule.DllCanUnloadNow();
929}
930
931// Returns a class factory to create an object of the requested type
932STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000933 chrome_frame::ScopedCrashReporting crash_reporting;
934
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000935 // If we found another module present when we were loaded, then delegate to
936 // that:
937 if (g_dll_get_class_object_redir_ptr) {
938 return g_dll_get_class_object_redir_ptr(rclsid, riid, ppv);
939 }
940
941 // Enable sniffing and switching only if asked for BHO
942 // (we use BHO to get loaded in IE).
943 if (rclsid == CLSID_ChromeFrameBHO) {
944 g_patch_helper.InitializeAndPatchProtocolsIfNeeded();
945 }
946
947 return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
948}
949
950// DllRegisterServer - Adds entries to the system registry
951STDAPI DllRegisterServer() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000952 chrome_frame::ScopedCrashReporting crash_reporting;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000953 uint16 flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL |
954 BHO_CLSID | BHO_REGISTRATION;
955
956 HRESULT hr = CustomRegistration(flags, true, true);
957 if (SUCCEEDED(hr)) {
958 SetupRunOnce();
959 }
960
961 return hr;
962}
963
964// DllUnregisterServer - Removes entries from the system registry
965STDAPI DllUnregisterServer() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000966 chrome_frame::ScopedCrashReporting crash_reporting;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000967 HRESULT hr = CustomRegistration(ALL, false, true);
968 return hr;
969}
970
971// DllRegisterUserServer - Adds entries to the HKCU hive in the registry.
972STDAPI DllRegisterUserServer() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000973 chrome_frame::ScopedCrashReporting crash_reporting;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000974 UINT flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL |
975 BHO_CLSID | BHO_REGISTRATION;
976
977 HRESULT hr = CustomRegistration(flags, TRUE, false);
978 if (SUCCEEDED(hr)) {
979 SetupRunOnce();
980 }
981
982 return hr;
983}
984
985// DllUnregisterUserServer - Removes entries from the HKCU hive in the registry.
986STDAPI DllUnregisterUserServer() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000987 chrome_frame::ScopedCrashReporting crash_reporting;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000988 HRESULT hr = CustomRegistration(ALL, FALSE, false);
989 return hr;
990}
991
992// Object entries go here instead of with each object, so that we can move
993// the objects to a lib. Also reduces magic.
994OBJECT_ENTRY_AUTO(CLSID_ChromeFrameBHO, Bho)
995OBJECT_ENTRY_AUTO(__uuidof(ChromeActiveDocument), ChromeActiveDocument)
996OBJECT_ENTRY_AUTO(__uuidof(ChromeFrame), ChromeFrameActivex)
997OBJECT_ENTRY_AUTO(__uuidof(ChromeProtocol), ChromeProtocol)