diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-08 19:45:45 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-08 19:45:45 +0000 |
commit | 9f7b860a6c4e84f80c0666980d6c947bf2ce9bf2 (patch) | |
tree | b9a653d8fc2d1dc4fd418cd5b735433fd3150d77 | |
parent | c1702d71f2fb3ba58cceb044c8e7401da1d5c9c8 (diff) | |
download | chromium_src-9f7b860a6c4e84f80c0666980d6c947bf2ce9bf2.zip chromium_src-9f7b860a6c4e84f80c0666980d6c947bf2ce9bf2.tar.gz chromium_src-9f7b860a6c4e84f80c0666980d6c947bf2ce9bf2.tar.bz2 |
Make Chrome Frame registration and unregistration more robust and make it return extra information in the failure code.
Specifically, there are a series of registration changes made by Chrome Frame at registration and unregistration time. Previously, if any of those changes failed, the previous changes were left and subsequent changes were not made.
During registration this could result in partial registration data being left if one of the items failed which is bad but wasn't a problem in practice, because if ever registration failed, unregistration would then be performed by the installer. Also the most likely step to fail happens first.
During unregistration, if any of the steps fail, the remaining unregistration steps will not be taken. This is bad, as uninstallation is a best effort, "clean up everything you can" type thing.
Changed this to for registration roll back the previous N-1 steps of step N fails and for unregistration to attempt to perform all N steps disregarding failures.
Updated installer logging.
BUG=94848
TEST=Unregistration of e.g. the user-agent modification should always be performed even if an earlier step fails.
Review URL: http://codereview.chromium.org/7824010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@100224 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/installer/util/self_reg_work_item.cc | 29 | ||||
-rw-r--r-- | chrome_frame/chrome_tab.cc | 687 |
2 files changed, 423 insertions, 293 deletions
diff --git a/chrome/installer/util/self_reg_work_item.cc b/chrome/installer/util/self_reg_work_item.cc index 79e4cc3..70f1853 100644 --- a/chrome/installer/util/self_reg_work_item.cc +++ b/chrome/installer/util/self_reg_work_item.cc @@ -27,6 +27,26 @@ SelfRegWorkItem::SelfRegWorkItem(const std::wstring& dll_path, SelfRegWorkItem::~SelfRegWorkItem() { } +// This is designed to unmux error codes that may be shoe-horned in to HRESULT +// return codes by ORing a number into the top four bits of the facility code +// Any number thus found will be returned in |error_code|. The "cleaned" +// HRESULT is then returned. +// +// This only has an effect if the customer bit is set in the HRESULT, if it is +// not set then *error_code will be unchanged and the original HRESULT is +// returned. +// +// Note that this will do the wrong thing for high-valued facility codes. +HRESULT UnMuxHRESULTErrorCode(HRESULT hr, int* error_code) { + DCHECK(error_code); + if (hr & (1 << 29)) { + *error_code = (hr & 0x07800000) >> 23; + return hr & 0xF87FFFFF; + } + + return hr; +} + bool SelfRegWorkItem::RegisterDll(bool do_register) { VLOG(1) << "COM " << (do_register ? "registration of " : "unregistration of ") << dll_path_; @@ -52,9 +72,12 @@ bool SelfRegWorkItem::RegisterDll(bool do_register) { HRESULT hr = register_server_func(); success = SUCCEEDED(hr); if (!success) { - PLOG(ERROR) << "Failed to " << (do_register ? "register" : "unregister") - << " DLL at " << dll_path_.c_str() << - base::StringPrintf(" 0x%08X", hr); + int error_code = 0; + HRESULT unmuxed_hr = UnMuxHRESULTErrorCode(hr, &error_code); + LOG(ERROR) << "Failed to " << (do_register ? "register" : "unregister") + << " DLL at " << dll_path_.c_str() << ", hr=" + << base::StringPrintf(" 0x%08X", unmuxed_hr) << ", code=" + << error_code; } } else { LOG(ERROR) << "COM registration export function not found"; diff --git a/chrome_frame/chrome_tab.cc b/chrome_frame/chrome_tab.cc index b77d94e..7b80394 100644 --- a/chrome_frame/chrome_tab.cc +++ b/chrome_frame/chrome_tab.cc @@ -52,11 +52,6 @@ void InitGoogleUrl() { url_util::IsStandard(kDummyUrl, url_parse::MakeRange(0, arraysize(kDummyUrl))); } -} - -static const wchar_t kBhoRegistryPath[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" - L"\\Browser Helper Objects"; const wchar_t kInternetSettings[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; @@ -86,13 +81,10 @@ static const GUID kChromeFrameProvider = { 0x562bfc3, 0x2550, 0x45b4, { 0xbd, 0x8e, 0xa3, 0x10, 0x58, 0x3d, 0x3a, 0x6f } }; -// Object entries go here instead of with each object, so that we can move -// the objects to a lib. Also reduces magic. -OBJECT_ENTRY_AUTO(CLSID_ChromeFrameBHO, Bho) -OBJECT_ENTRY_AUTO(__uuidof(ChromeActiveDocument), ChromeActiveDocument) -OBJECT_ENTRY_AUTO(__uuidof(ChromeFrame), ChromeFrameActivex) -OBJECT_ENTRY_AUTO(__uuidof(ChromeProtocol), ChromeProtocol) - +const wchar_t kPostPlatformUAKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\" + L"User Agent\\Post Platform"; +const wchar_t kChromeFramePrefix[] = L"chromeframe/"; // See comments in DllGetClassObject. LPFNGETCLASSOBJECT g_dll_get_class_object_redir_ptr = NULL; @@ -191,61 +183,7 @@ class ChromeTabModule : public CAtlDllModuleT<ChromeTabModule> { ChromeTabModule _AtlModule; base::AtExitManager* g_exit_manager = NULL; -bool RegisterSecuredMimeHandler(bool enable, bool is_system); // forward - -// DLL Entry Point -extern "C" BOOL WINAPI DllMain(HINSTANCE instance, - DWORD reason, - LPVOID reserved) { - UNREFERENCED_PARAMETER(instance); - if (reason == DLL_PROCESS_ATTACH) { -#ifndef NDEBUG - // Silence traces from the ATL registrar to reduce the log noise. - ATL::CTrace::s_trace.ChangeCategory(atlTraceRegistrar, 0, - ATLTRACESTATUS_DISABLED); -#endif - InitGoogleUrl(); - - g_exit_manager = new base::AtExitManager(); - CommandLine::Init(0, NULL); - InitializeCrashReporting(); - logging::InitLogging( - NULL, - logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, - logging::LOCK_LOG_FILE, - logging::DELETE_OLD_LOG_FILE, - logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); - - DllRedirector* dll_redirector = DllRedirector::GetInstance(); - DCHECK(dll_redirector); - - if (!dll_redirector->RegisterAsFirstCFModule()) { - // Someone else was here first, try and get a pointer to their - // DllGetClassObject export: - g_dll_get_class_object_redir_ptr = - dll_redirector->GetDllGetClassObjectPtr(); - DCHECK(g_dll_get_class_object_redir_ptr != NULL) - << "Found CF module with no DllGetClassObject export."; - } - - // Enable ETW logging. - logging::LogEventProvider::Initialize(kChromeFrameProvider); - } else if (reason == DLL_PROCESS_DETACH) { - DllRedirector* dll_redirector = DllRedirector::GetInstance(); - DCHECK(dll_redirector); - dll_redirector->UnregisterAsFirstCFModule(); - g_patch_helper.UnpatchIfNeeded(); - delete g_exit_manager; - g_exit_manager = NULL; - ShutdownCrashReporting(); - } - return _AtlModule.DllMain(reason, reserved); -} - -#ifdef _MANAGED -#pragma managed(pop) -#endif HRESULT RefreshElevationPolicy() { const wchar_t kIEFrameDll[] = L"ieframe.dll"; @@ -348,7 +286,9 @@ HRESULT SetupRunOnce() { // Helper method called for user-level installs where we don't have admin // permissions. Starts up the long running process and registers it to get it // started at next boot. -void SetupUserLevelHelper() { +HRESULT SetupUserLevelHelper() { + HRESULT hr = S_OK; + // Remove existing run-at-startup entry. base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, kRunKeyName); @@ -366,72 +306,51 @@ void SetupUserLevelHelper() { DCHECK(file_util::PathExists(helper_path)) << "Could not find chrome_frame_helper.exe."; } - } else { - NOTREACHED(); - } - - // Find window handle of existing instance. - HWND old_window = FindWindow(kChromeFrameHelperWindowClassName, - kChromeFrameHelperWindowName); - - if (file_util::PathExists(helper_path)) { - std::wstring helper_path_cmd(L"\""); - helper_path_cmd += helper_path.value(); - helper_path_cmd += L"\" "; - helper_path_cmd += kChromeFrameHelperStartupArg; - - // Add new run-at-startup entry. - base::win::AddCommandToAutoRun(HKEY_CURRENT_USER, kRunKeyName, - helper_path_cmd); - - // Start new instance. - base::LaunchOptions options; - options.start_hidden = true; - bool launched = base::LaunchProcess(helper_path.value(), options, NULL); - if (!launched) { - NOTREACHED(); - LOG(ERROR) << "Could not launch helper process."; - } - // Kill old instance using window handle. - if (IsWindow(old_window)) { - BOOL result = PostMessage(old_window, WM_CLOSE, 0, 0); - if (!result) { - LOG(ERROR) << "Failed to post close message to old helper process: " - << GetLastError(); + // Find window handle of existing instance. + HWND old_window = FindWindow(kChromeFrameHelperWindowClassName, + kChromeFrameHelperWindowName); + + if (file_util::PathExists(helper_path)) { + std::wstring helper_path_cmd(L"\""); + helper_path_cmd += helper_path.value(); + helper_path_cmd += L"\" "; + helper_path_cmd += kChromeFrameHelperStartupArg; + + // Add new run-at-startup entry. + if (!base::win::AddCommandToAutoRun(HKEY_CURRENT_USER, kRunKeyName, + helper_path_cmd)) { + hr = E_FAIL; + LOG(ERROR) << "Could not add helper process to auto run key."; } - } - } -} + // Start new instance. + base::LaunchOptions options; + options.start_hidden = true; + bool launched = base::LaunchProcess(helper_path.value(), options, NULL); + if (!launched) { + hr = E_FAIL; + PLOG(DFATAL) << "Could not launch helper process."; + } -// Used to determine whether the DLL can be unloaded by OLE -STDAPI DllCanUnloadNow() { - return _AtlModule.DllCanUnloadNow(); -} - -// Returns a class factory to create an object of the requested type -STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { - // If we found another module present when we were loaded, then delegate to - // that: - if (g_dll_get_class_object_redir_ptr) { - return g_dll_get_class_object_redir_ptr(rclsid, riid, ppv); - } - - // Enable sniffing and switching only if asked for BHO - // (we use BHO to get loaded in IE). - if (rclsid == CLSID_ChromeFrameBHO) { - g_patch_helper.InitializeAndPatchProtocolsIfNeeded(); + // Kill old instance using window handle. + if (IsWindow(old_window)) { + BOOL result = PostMessage(old_window, WM_CLOSE, 0, 0); + if (!result) { + PLOG(ERROR) << "Failed to post close message to old helper process: "; + } + } + } else { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } else { + hr = E_UNEXPECTED; + NOTREACHED(); } - return _AtlModule.DllGetClassObject(rclsid, riid, ppv); + return hr; } -const wchar_t kPostPlatformUAKey[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\" - L"User Agent\\Post Platform"; -const wchar_t kChromeFramePrefix[] = L"chromeframe/"; - // To delete the user agent, set value to NULL. // The is_system parameter indicates whether this is a per machine or a per // user installation. @@ -440,8 +359,9 @@ HRESULT SetChromeFrameUA(bool is_system, const wchar_t* value) { HKEY parent_hive = is_system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; RegKey ua_key; - if (ua_key.Create(parent_hive, kPostPlatformUAKey, - KEY_READ | KEY_WRITE) == ERROR_SUCCESS) { + LONG reg_result = ua_key.Create(parent_hive, kPostPlatformUAKey, + KEY_READ | KEY_WRITE); + if (reg_result == ERROR_SUCCESS) { // Make sure that we unregister ChromeFrame UA strings registered previously wchar_t value_name[MAX_PATH + 1] = {}; wchar_t value_data[MAX_PATH + 1] = {}; @@ -473,165 +393,13 @@ HRESULT SetChromeFrameUA(bool is_system, const wchar_t* value) { } hr = S_OK; } else { - DLOG(ERROR) << __FUNCTION__ << ": " << kPostPlatformUAKey; - hr = E_UNEXPECTED; + DLOG(ERROR) << __FUNCTION__ << ": " << kPostPlatformUAKey + << ", error code = " << reg_result; + hr = HRESULT_FROM_WIN32(reg_result); } return hr; } -enum RegistrationFlags { - ACTIVEX = 0x0001, - ACTIVEDOC = 0x0002, - GCF_PROTOCOL = 0x0004, - BHO_CLSID = 0x0008, - BHO_REGISTRATION = 0x0010, - TYPELIB = 0x0020, - - ALL = 0xFFFF -}; - -STDAPI CustomRegistration(UINT reg_flags, BOOL reg, bool is_system) { - UINT flags = reg_flags; - - if (reg && (flags & (ACTIVEDOC | ACTIVEX))) - flags |= (TYPELIB | GCF_PROTOCOL); - - HRESULT hr = S_OK; - - // Set the flag that gets checked in AddCommonRGSReplacements before doing - // registration work. - _AtlModule.do_system_registration_ = is_system; - - if ((hr == S_OK) && (flags & ACTIVEDOC)) { - // Don't fail to unregister if we can't undo the secure mime - // handler registration. This was observed getting hit during - // uninstallation. - if (!RegisterSecuredMimeHandler(reg ? true : false, is_system) && reg) - return E_FAIL; - hr = ChromeActiveDocument::UpdateRegistry(reg); - } - - if ((hr == S_OK) && (flags & ACTIVEX)) { - // We have to call the static T::UpdateRegistry function instead of - // _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ACTIVEX, reg) - // because there is specific OLEMISC replacement. - hr = ChromeFrameActivex::UpdateRegistry(reg); - } - - // Register the elevation policy. We do this only for developer convenience - // as the installer is really responsible for doing this. - // Because of that, we do not unregister this policy and just leave that up - // to the installer. - if (hr == S_OK && (flags & (ACTIVEDOC | ACTIVEX)) && reg) { - _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ELEVATION, reg); - RefreshElevationPolicy(); - } - - if ((hr == S_OK) && (flags & GCF_PROTOCOL)) { - hr = _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEPROTOCOL, reg); - } - - if ((hr == S_OK) && (flags & BHO_CLSID)) { - hr = Bho::UpdateRegistry(reg); - } - - if ((hr == S_OK) && (flags & BHO_REGISTRATION)) { - if (is_system) { - _AtlModule.UpdateRegistryFromResourceS(IDR_REGISTER_BHO, reg); - } else { - if (reg) { - // Setup the long running process: - SetupUserLevelHelper(); - } else { - // Unschedule the user-level helper. Note that we don't kill it here so - // that during updates we don't have a time window with no running - // helper. Uninstalls and updates will explicitly kill the helper from - // within the installer. Unregister existing run-at-startup entry. - base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, kRunKeyName); - } - } - } - - if ((hr == S_OK) && (flags & TYPELIB)) { - if (reg && !is_system) { - // Enables the RegisterTypeLib Function function to override default - // registry mappings under Windows Vista Service Pack 1 (SP1), - // Windows Server 2008, and later operating system versions - typedef void (WINAPI* OaEnablePerUserTypeLibReg)(void); - OaEnablePerUserTypeLibReg per_user_typelib_func = - reinterpret_cast<OaEnablePerUserTypeLibReg>( - GetProcAddress(GetModuleHandle(L"oleaut32.dll"), - "OaEnablePerUserTLibRegistration")); - if (per_user_typelib_func) { - (*per_user_typelib_func)(); - } - } - hr = (reg)? - UtilRegisterTypeLib(_AtlComModule.m_hInstTypeLib, NULL, !is_system) : - UtilUnRegisterTypeLib(_AtlComModule.m_hInstTypeLib, NULL, !is_system); - } - - // Unconditionally remove NPAPI registration when unregistering any component. - if ((hr == S_OK) && !reg) { - // Ignore failures. - _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_NPAPI, reg); - UtilRemovePersistentNPAPIMarker(); - } - - if (hr == S_OK) { - hr = _AtlModule.UpdateRegistryAppId(reg); - } - - if (hr == S_OK) { - if (reg) { - hr = SetChromeFrameUA(is_system, L"1"); - } else { - hr = SetChromeFrameUA(is_system, NULL); - } - } - return hr; -} - - - -// DllRegisterServer - Adds entries to the system registry -STDAPI DllRegisterServer() { - UINT flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL | - BHO_CLSID | BHO_REGISTRATION; - - HRESULT hr = CustomRegistration(flags, TRUE, true); - if (SUCCEEDED(hr)) { - SetupRunOnce(); - } - - return hr; -} - -// DllUnregisterServer - Removes entries from the system registry -STDAPI DllUnregisterServer() { - HRESULT hr = CustomRegistration(ALL, FALSE, true); - return hr; -} - -// DllRegisterServer - Adds entries to the HKCU hive in the registry -STDAPI DllRegisterUserServer() { - UINT flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL | - BHO_CLSID | BHO_REGISTRATION; - - HRESULT hr = CustomRegistration(flags, TRUE, false); - if (SUCCEEDED(hr)) { - SetupRunOnce(); - } - - return hr; -} - -// DllRegisterServer - Removes entries from the HKCU hive in the registry. -STDAPI DllUnregisterUserServer() { - HRESULT hr = CustomRegistration(ALL, FALSE, false); - return hr; -} - class SecurityDescBackup { public: explicit SecurityDescBackup(const std::wstring& backup_key) @@ -746,7 +514,7 @@ struct TokenWithPrivileges { CSid user_; }; -static bool SetOrDeleteMimeHandlerKey(bool set, HKEY root_key) { +HRESULT SetOrDeleteMimeHandlerKey(bool set, HKEY root_key) { std::wstring key_name = kInternetSettings; key_name.append(L"\\Secure Mime Handlers"); RegKey key(root_key, key_name.c_str(), KEY_READ | KEY_WRITE); @@ -763,10 +531,13 @@ static bool SetOrDeleteMimeHandlerKey(bool set, HKEY root_key) { result2 = key.DeleteValue(L"ChromeTab.ChromeActiveDocument.1"); } - return (result2 == ERROR_SUCCESS) && (result2 == ERROR_SUCCESS); + return result1 != ERROR_SUCCESS ? HRESULT_FROM_WIN32(result1) : + HRESULT_FROM_WIN32(result2); } -bool RegisterSecuredMimeHandler(bool enable, bool is_system) { +// Chrome Frame registration functions. +//----------------------------------------------------------------------------- +HRESULT RegisterSecuredMimeHandler(bool enable, bool is_system) { if (!is_system) { return SetOrDeleteMimeHandlerKey(enable, HKEY_CURRENT_USER); } else if (base::win::GetVersion() < base::win::VERSION_VISTA) { @@ -782,7 +553,7 @@ bool RegisterSecuredMimeHandler(bool enable, bool is_system) { TokenWithPrivileges token_; if (!token_.EnablePrivileges()) - return false; + return E_ACCESSDENIED; // If there is a backup key - something bad happened; try to restore // security on "Secure Mime Handlers" from the backup. @@ -792,11 +563,11 @@ bool RegisterSecuredMimeHandler(bool enable, bool is_system) { // Read old security descriptor of the Mime key first. CSecurityDesc sd; if (!AtlGetSecurityDescriptor(object_name.c_str(), SE_REGISTRY_KEY, &sd)) { - return false; + return E_FAIL; } backup.SaveSecurity(sd); - bool result = false; + HRESULT hr = E_FAIL; // set new owner if (AtlSetOwnerSid(object_name.c_str(), SE_REGISTRY_KEY, token_.GetUser())) { // set new dacl @@ -804,10 +575,346 @@ bool RegisterSecuredMimeHandler(bool enable, bool is_system) { sd.GetDacl(&new_dacl); new_dacl.AddAllowedAce(token_.GetUser(), GENERIC_WRITE | GENERIC_READ); if (AtlSetDacl(object_name.c_str(), SE_REGISTRY_KEY, new_dacl)) { - result = SetOrDeleteMimeHandlerKey(enable, HKEY_LOCAL_MACHINE); + hr = SetOrDeleteMimeHandlerKey(enable, HKEY_LOCAL_MACHINE); } } backup.RestoreSecurity(object_name.c_str()); - return result; + return hr; +} + +HRESULT RegisterActiveDoc(bool reg, bool is_system) { + // We have to call the static T::UpdateRegistry function instead of + // _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ACTIVEDOC, reg) + // because there is specific OLEMISC replacement. + return ChromeActiveDocument::UpdateRegistry(reg); +} + +HRESULT RegisterActiveX(bool reg, bool is_system) { + // We have to call the static T::UpdateRegistry function instead of + // _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ACTIVEX, reg) + // because there is specific OLEMISC replacement. + return ChromeFrameActivex::UpdateRegistry(reg); +} + +HRESULT RegisterElevationPolicy(bool reg, bool is_system) { + HRESULT hr = S_OK; + if (reg && base::win::GetVersion() >= base::win::VERSION_VISTA) { + // Register the elevation policy. This must succeed for Chrome Frame to + // be able launch Chrome when running in low-integrity IE. + // Note that this is not done on unregistration, the installer will + // explicitly remove the policy on uninstall. + hr = _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_ELEVATION, + reg); + if (SUCCEEDED(hr)) { + hr = RefreshElevationPolicy(); + } + } + return hr; +} + +HRESULT RegisterProtocol(bool reg, bool is_system) { + return _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEPROTOCOL, reg); +} + +HRESULT RegisterBhoClsid(bool reg, bool is_system) { + return Bho::UpdateRegistry(reg); +} + +HRESULT RegisterBhoIE(bool reg, bool is_system) { + if (is_system) { + return _AtlModule.UpdateRegistryFromResourceS(IDR_REGISTER_BHO, reg); + } else { + if (reg) { + // Setup the long running process: + return SetupUserLevelHelper(); + } else { + // Unschedule the user-level helper. Note that we don't kill it here + // so that during updates we don't have a time window with no running + // helper. Uninstalls and updates will explicitly kill the helper from + // within the installer. Unregister existing run-at-startup entry. + return base::win::RemoveCommandFromAutoRun(HKEY_CURRENT_USER, + kRunKeyName) ? S_OK : E_FAIL; + } + } +} + +HRESULT RegisterTypeLib(bool reg, bool is_system) { + if (reg && !is_system) { + // Enables the RegisterTypeLib Function function to override default + // registry mappings under Windows Vista Service Pack 1 (SP1), + // Windows Server 2008, and later operating system versions + typedef void (WINAPI* OaEnablePerUserTypeLibReg)(void); + OaEnablePerUserTypeLibReg per_user_typelib_func = + reinterpret_cast<OaEnablePerUserTypeLibReg>( + GetProcAddress(GetModuleHandle(L"oleaut32.dll"), + "OaEnablePerUserTLibRegistration")); + if (per_user_typelib_func) { + (*per_user_typelib_func)(); + } + } + return reg ? + UtilRegisterTypeLib(_AtlComModule.m_hInstTypeLib, + NULL, !is_system) : + UtilUnRegisterTypeLib(_AtlComModule.m_hInstTypeLib, + NULL, !is_system); +} + +HRESULT RegisterLegacyNPAPICleanup(bool reg, bool is_system) { + if (!reg) { + _AtlModule.UpdateRegistryFromResourceS(IDR_CHROMEFRAME_NPAPI, reg); + UtilRemovePersistentNPAPIMarker(); + } + // Ignore failures. + return S_OK; +} + +HRESULT RegisterAppId(bool reg, bool is_system) { + return _AtlModule.UpdateRegistryAppId(reg); +} + +HRESULT RegisterUserAgent(bool reg, bool is_system) { + if (reg) { + return SetChromeFrameUA(is_system, L"1"); + } else { + return SetChromeFrameUA(is_system, NULL); + } +} + +enum RegistrationStepId { + kStepSecuredMimeHandler = 0, + kStepActiveDoc = 1, + kStepActiveX = 2, + kStepElevationPolicy = 3, + kStepProtocol = 4, + kStepBhoClsid = 5, + kStepBhoRegistration = 6, + kStepRegisterTypeLib = 7, + kStepNpapiCleanup = 8, + kStepAppId = 9, + kStepUserAgent = 10, + kStepEnd = 11 +}; + +enum RegistrationFlags { + ACTIVEX = 0x0001, + ACTIVEDOC = 0x0002, + GCF_PROTOCOL = 0x0004, + BHO_CLSID = 0x0008, + BHO_REGISTRATION = 0x0010, + TYPELIB = 0x0020, + + ALL = 0xFFFF +}; + +// Mux the failure step into the hresult. We take only the first four bits +// and stick those into the top four bits of the facility code. We also set the +// Customer bit to be polite. Graphically, we write our error code to the +// bits marked with ^: +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 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 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// ^ ^ ^ ^ ^ +// See http://msdn.microsoft.com/en-us/library/cc231198(PROT.10).aspx for +// more details on HRESULTS. +// +// The resulting error can be extracted by: +// error_code = (fiddled_hr & 0x07800000) >> 23 +HRESULT MuxErrorIntoHRESULT(HRESULT hr, int error_code) { + DCHECK_GE(error_code, 0); + DCHECK_LT(error_code, kStepEnd); + COMPILE_ASSERT(kStepEnd <= 0xF, update_error_muxing_too_many_steps); + + // Check that our four desired bits are clear. + // 0xF87FFFFF == 11111000011111111111111111111111 + DCHECK_EQ(static_cast<HRESULT>(hr & 0xF87FFFFF), hr); + + HRESULT fiddled_hr = ((error_code & 0xF) << 23) | hr; + fiddled_hr |= 1 << 29; // Set the customer bit. + + return fiddled_hr; +} + +HRESULT CustomRegistration(uint16 reg_flags, bool reg, bool is_system) { + if (reg && (reg_flags & (ACTIVEDOC | ACTIVEX))) + reg_flags |= (TYPELIB | GCF_PROTOCOL); + + // Set the flag that gets checked in AddCommonRGSReplacements before doing + // registration work. + _AtlModule.do_system_registration_ = is_system; + + typedef HRESULT (*RegistrationFn)(bool reg, bool is_system); + struct RegistrationStep { + RegistrationStepId id; + uint16 condition; + RegistrationFn func; + }; + static const RegistrationStep registration_steps[] = { + { kStepSecuredMimeHandler, ACTIVEDOC, &RegisterSecuredMimeHandler }, + { kStepActiveDoc, ACTIVEDOC, &RegisterActiveDoc }, + { kStepActiveX, ACTIVEX, &RegisterActiveX }, + { kStepElevationPolicy, (ACTIVEDOC | ACTIVEX), &RegisterElevationPolicy }, + { kStepProtocol, GCF_PROTOCOL, &RegisterProtocol }, + { kStepBhoClsid, BHO_CLSID, &RegisterBhoClsid }, + { kStepBhoRegistration, BHO_REGISTRATION, &RegisterBhoIE }, + { kStepRegisterTypeLib, TYPELIB, &RegisterTypeLib }, + { kStepNpapiCleanup, ALL, &RegisterLegacyNPAPICleanup }, + { kStepAppId, ALL, &RegisterAppId }, + { kStepUserAgent, ALL, &RegisterUserAgent } + }; + + HRESULT hr = S_OK; + + bool rollback = false; + int failure_step = 0; + HRESULT step_hr = S_OK; + for (int step = 0; step < arraysize(registration_steps); ++step) { + if ((reg_flags & registration_steps[step].condition) != 0) { + step_hr = registration_steps[step].func(reg, is_system); + if (FAILED(step_hr)) { + // Store only the first failing HRESULT with the step value muxed in. + if (hr == S_OK) { + hr = MuxErrorIntoHRESULT(step_hr, step); + } + + // On registration if a step fails, we abort and rollback. + if (reg) { + rollback = true; + failure_step = step; + break; + } + } + } + } + + if (rollback) { + DCHECK(reg); + // Rollback the failing action and all preceding ones. + for (int step = failure_step; step >= 0; --step) { + registration_steps[step].func(!reg, is_system); + } + } + + return hr; +} + +} // namespace + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE instance, + DWORD reason, + LPVOID reserved) { + UNREFERENCED_PARAMETER(instance); + if (reason == DLL_PROCESS_ATTACH) { +#ifndef NDEBUG + // Silence traces from the ATL registrar to reduce the log noise. + ATL::CTrace::s_trace.ChangeCategory(atlTraceRegistrar, 0, + ATLTRACESTATUS_DISABLED); +#endif + InitGoogleUrl(); + + g_exit_manager = new base::AtExitManager(); + CommandLine::Init(0, NULL); + InitializeCrashReporting(); + logging::InitLogging( + NULL, + logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE, + logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); + + DllRedirector* dll_redirector = DllRedirector::GetInstance(); + DCHECK(dll_redirector); + + if (!dll_redirector->RegisterAsFirstCFModule()) { + // Someone else was here first, try and get a pointer to their + // DllGetClassObject export: + g_dll_get_class_object_redir_ptr = + dll_redirector->GetDllGetClassObjectPtr(); + DCHECK(g_dll_get_class_object_redir_ptr != NULL) + << "Found CF module with no DllGetClassObject export."; + } + + // Enable ETW logging. + logging::LogEventProvider::Initialize(kChromeFrameProvider); + } else if (reason == DLL_PROCESS_DETACH) { + DllRedirector* dll_redirector = DllRedirector::GetInstance(); + DCHECK(dll_redirector); + + dll_redirector->UnregisterAsFirstCFModule(); + g_patch_helper.UnpatchIfNeeded(); + delete g_exit_manager; + g_exit_manager = NULL; + ShutdownCrashReporting(); + } + return _AtlModule.DllMain(reason, reserved); +} + +// Used to determine whether the DLL can be unloaded by OLE +STDAPI DllCanUnloadNow() { + return _AtlModule.DllCanUnloadNow(); } + +// Returns a class factory to create an object of the requested type +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { + // If we found another module present when we were loaded, then delegate to + // that: + if (g_dll_get_class_object_redir_ptr) { + return g_dll_get_class_object_redir_ptr(rclsid, riid, ppv); + } + + // Enable sniffing and switching only if asked for BHO + // (we use BHO to get loaded in IE). + if (rclsid == CLSID_ChromeFrameBHO) { + g_patch_helper.InitializeAndPatchProtocolsIfNeeded(); + } + + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + +// DllRegisterServer - Adds entries to the system registry +STDAPI DllRegisterServer() { + uint16 flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL | + BHO_CLSID | BHO_REGISTRATION; + + HRESULT hr = CustomRegistration(flags, true, true); + if (SUCCEEDED(hr)) { + SetupRunOnce(); + } + + return hr; +} + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer() { + HRESULT hr = CustomRegistration(ALL, false, true); + return hr; +} + +// DllRegisterUserServer - Adds entries to the HKCU hive in the registry. +STDAPI DllRegisterUserServer() { + UINT flags = ACTIVEX | ACTIVEDOC | TYPELIB | GCF_PROTOCOL | + BHO_CLSID | BHO_REGISTRATION; + + HRESULT hr = CustomRegistration(flags, TRUE, false); + if (SUCCEEDED(hr)) { + SetupRunOnce(); + } + + return hr; +} + +// DllUnregisterUserServer - Removes entries from the HKCU hive in the registry. +STDAPI DllUnregisterUserServer() { + HRESULT hr = CustomRegistration(ALL, FALSE, false); + return hr; +} + +// Object entries go here instead of with each object, so that we can move +// the objects to a lib. Also reduces magic. +OBJECT_ENTRY_AUTO(CLSID_ChromeFrameBHO, Bho) +OBJECT_ENTRY_AUTO(__uuidof(ChromeActiveDocument), ChromeActiveDocument) +OBJECT_ENTRY_AUTO(__uuidof(ChromeFrame), ChromeFrameActivex) +OBJECT_ENTRY_AUTO(__uuidof(ChromeProtocol), ChromeProtocol) |