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.
5 #include "chrome_frame/test/ie_configurator.h"
14 #include "base/compiler_specific.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/string16.h"
17 #include "base/time.h"
18 #include "base/win/registry.h"
19 #include "chrome_frame/chrome_tab.h"
20 #include "chrome_frame/test/chrome_frame_test_utils.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 namespace chrome_frame_test
{
27 const wchar_t kKeyIEApprovedExtensions
[] =
28 L
"Software\\Microsoft\\Internet Explorer\\Approved Extensions\\";
29 const wchar_t kKeyIEInformationBar
[] =
30 L
"Software\\Microsoft\\Internet Explorer\\InformationBar";
31 const wchar_t kKeyIEMain
[] =
32 L
"Software\\Microsoft\\Internet Explorer\\Main";
33 const wchar_t kKeyIEPhishingFilter
[] =
34 L
"Software\\Microsoft\\Internet Explorer\\PhishingFilter";
35 const wchar_t kKeyIEBrowserEmulation
[] =
36 L
"Software\\Microsoft\\Internet Explorer\\BrowserEmulation";
37 const wchar_t kKeyPoliciesExt
[] =
38 L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Ext";
39 const wchar_t kValueEnabledV9
[] = L
"EnabledV9";
40 const wchar_t kValueFirstTime
[] = L
"FirstTime";
41 const wchar_t kValueIE9Completed
[] = L
"IE9RunOncePerInstallCompleted";
42 const wchar_t kValueIE9CompletionTime
[] = L
"IE9RunOnceCompletionTime";
43 const wchar_t kValueIE9LastShown
[] = L
"IE9RunOnceLastShown";
44 const wchar_t kValueIE9TourNoShow
[] = L
"IE9TourNoShow";
45 const wchar_t kValueIE10Completed
[] = L
"IE10RunOncePerInstallCompleted";
46 const wchar_t kValueIE10CompletionTime
[] = L
"IE10RunOnceCompletionTime";
47 const wchar_t kValueIgnoreFrameApprovalCheck
[] = L
"IgnoreFrameApprovalCheck";
48 const wchar_t kValueMSCompatibilityMode
[] = L
"MSCompatibilityMode";
50 // A helper class that accumulate a set of registry mutations and corresponding
51 // undo data via calls to its Add*() methods. The mutations can be applied to
52 // the registry (repeatedly, if desired) via a call to Apply(). Revert() can be
53 // called to apply the accumulated undo data.
54 class RegistrySetter
{
59 // Adds a mutation that sets a REG_DWORD registry value, creating any
60 // intermediate keys as necessary. |key| and |value| must remain valid
61 // throughout all calls to Apply().
62 void AddDWORDValue(HKEY root
, const wchar_t* key
, const wchar_t* value
,
65 // Adds a mutation that assigns a FILETIME to a REG_BINARY registry value,
66 // creating any intermediate keys as necessary. |key| and |value| must remain
67 // valid throughout all calls to Apply().
68 void AddFILETIMEValue(HKEY root
, const wchar_t* key
, const wchar_t* value
,
71 // Applies all mutations in the order they were added. Errors encountered
72 // along the way are logged, but do not stop progress.
75 // Applies the undo data in the reverse order that their corresponding
76 // mutations were added.
80 // The data for an individual registry value. A non-existent value is
81 // indicated by type = REG_NONE and empty data.
87 std::vector
<uint8
> data
;
90 typedef std::list
<RegistryData
> RegistryDataList
;
92 // Adds a mutation to the end of the apply list with the given data, and a
93 // mutation to the revert list with the current state of the value.
94 void AddValue(HKEY root
, const wchar_t* key
, const wchar_t* value
, DWORD type
,
95 const uint8
* value_begin
, size_t value_len
);
97 // Add a mutation to the revert list that restores a registry value to its
98 // current state. This only adds entries to set or remove |value|. If
99 // portions of the hierarchy identified by |key| do not exist, but are created
100 // between the invocation of this method and the time the revert list is
101 // applied, only |value| is deleted upon revert (intermediate keys are not
103 void SaveCurrentValue(HKEY root
, const wchar_t* key
, const wchar_t* value
);
105 // Applies all mutations in |data_list| in order.
106 static void ApplyList(const RegistryDataList
& data_list
);
108 RegistryDataList apply_list_
;
109 RegistryDataList revert_list_
;
111 DISALLOW_COPY_AND_ASSIGN(RegistrySetter
);
114 // A Google Test event listener that delegates to a configurator.
115 class ConfiguratorDriver
: public testing::EmptyTestEventListener
{
117 explicit ConfiguratorDriver(IEConfigurator
* configurator
);
118 virtual ~ConfiguratorDriver();
120 virtual void OnTestProgramStart(const testing::UnitTest
& unit_test
) OVERRIDE
;
121 virtual void OnTestStart(const testing::TestInfo
& test_info
) OVERRIDE
;
122 virtual void OnTestProgramEnd(const testing::UnitTest
& unit_test
) OVERRIDE
;
125 scoped_ptr
<IEConfigurator
> configurator_
;
126 DISALLOW_COPY_AND_ASSIGN(ConfiguratorDriver
);
129 // A configurator for Internet Explorer 7.
130 class IE7Configurator
: public IEConfigurator
{
133 virtual ~IE7Configurator();
135 virtual void Initialize() OVERRIDE
;
136 virtual void ApplySettings() OVERRIDE
;
137 virtual void RevertSettings() OVERRIDE
;
140 RegistrySetter setter_
;
142 DISALLOW_COPY_AND_ASSIGN(IE7Configurator
);
145 // A configurator for Internet Explorer 9 and 10.
146 class ModernIEConfigurator
: public IEConfigurator
{
148 explicit ModernIEConfigurator(IEVersion ie_version
);
149 virtual ~ModernIEConfigurator();
151 virtual void Initialize() OVERRIDE
;
152 virtual void ApplySettings() OVERRIDE
;
153 virtual void RevertSettings() OVERRIDE
;
156 // The names of the registry values used to determine if IE's one-time
157 // initialization has been completed.
158 struct RunOnceValueNames
{
159 // This DWORD value is non-zero once initialization has been completed.
160 const wchar_t* completed
;
161 // This 8-byte binary value is the FILETIME of completion.
162 const wchar_t* completion_time
;
165 static const RunOnceValueNames kIE9ValueNames
;
166 static const RunOnceValueNames kIE10ValueNames
;
168 static const RunOnceValueNames
* RunOnceNamesForVersion(IEVersion ie_version
);
169 bool IsPerUserSetupComplete();
170 static string16
GetChromeFrameBHOCLSID();
171 static bool IsAddonPromptDisabledForChromeFrame();
173 const IEVersion ie_version_
;
174 const RunOnceValueNames
* run_once_value_names_
;
175 RegistrySetter setter_
;
177 DISALLOW_COPY_AND_ASSIGN(ModernIEConfigurator
);
180 // RegistrySetter implementation.
182 RegistrySetter::RegistrySetter() {
185 RegistrySetter::~RegistrySetter() {
188 void RegistrySetter::AddValue(HKEY root
,
190 const wchar_t* value
,
192 const uint8
* value_begin
,
194 RegistryData the_data
= {
199 std::vector
<uint8
>(value_begin
, value_begin
+ value_len
)
201 apply_list_
.push_back(the_data
);
202 SaveCurrentValue(root
, key
, value
);
205 void RegistrySetter::SaveCurrentValue(HKEY root
,
207 const wchar_t* value
) {
208 base::win::RegKey the_key
;
209 RegistryData the_data
= { root
, key
, value
, REG_NONE
};
211 LONG result
= the_key
.Open(root
, key
, KEY_QUERY_VALUE
);
212 if (result
== ERROR_SUCCESS
) {
214 result
= the_key
.ReadValue(value
, NULL
, &size
, &the_data
.type
);
215 if (result
== ERROR_FILE_NOT_FOUND
) {
216 // Add a mutation to delete the value.
217 the_data
.type
= REG_NONE
;
218 revert_list_
.push_front(the_data
);
219 } else if (result
== ERROR_SUCCESS
) {
220 the_data
.data
.resize(size
);
221 result
= the_key
.ReadValue(value
, &the_data
.data
[0], &size
,
223 if (result
== ERROR_SUCCESS
) {
224 revert_list_
.push_front(the_data
);
226 ::SetLastError(result
);
227 PLOG(ERROR
) << __FUNCTION__
<< " unexpected error reading data for "
228 << value
<< " from key " << key
229 << ". The current value will not be restored upon revert.";
232 ::SetLastError(result
);
233 PLOG(ERROR
) << __FUNCTION__
<< " unexpected error reading " << value
234 << " from key " << key
235 << ". The current value will not be restored upon revert.";
237 } else if (result
== ERROR_FILE_NOT_FOUND
) {
238 // Add a mutation to delete the value (but not any keys).
239 revert_list_
.push_front(the_data
);
241 ::SetLastError(result
);
242 PLOG(ERROR
) << __FUNCTION__
<< " unexpected error opening key " << key
243 << " to read value " << value
244 << ". The current value will not be restored upon revert.";
248 void RegistrySetter::AddDWORDValue(HKEY root
,
250 const wchar_t* value
,
252 const uint8
* data_ptr
= reinterpret_cast<uint8
*>(&data
);
253 AddValue(root
, key
, value
, REG_DWORD
, data_ptr
, sizeof(data
));
256 void RegistrySetter::AddFILETIMEValue(HKEY root
,
258 const wchar_t* value
,
260 const uint8
* data_ptr
= reinterpret_cast<uint8
*>(&data
);
261 AddValue(root
, key
, value
, REG_BINARY
, data_ptr
, sizeof(data
));
265 void RegistrySetter::ApplyList(const RegistryDataList
& data_list
) {
266 base::win::RegKey key
;
267 LONG result
= ERROR_SUCCESS
;
268 for (RegistryDataList::const_iterator
scan(data_list
.begin());
269 scan
!= data_list
.end(); ++scan
) {
270 const RegistryData
& data
= *scan
;
271 const bool do_delete
= (data
.type
== REG_NONE
&& data
.data
.empty());
273 key
.Open(data
.root
, data
.key
, KEY_SET_VALUE
) :
274 key
.Create(data
.root
, data
.key
, KEY_SET_VALUE
);
275 if (result
== ERROR_SUCCESS
) {
277 result
= key
.DeleteValue(data
.value
);
279 result
= key
.WriteValue(data
.value
,
280 data
.data
.empty() ? NULL
: &data
.data
[0],
281 data
.data
.size(), data
.type
);
283 if (result
!= ERROR_SUCCESS
) {
284 ::SetLastError(result
);
285 PLOG(ERROR
) << "Failed to " << (do_delete
? "delete" : "set")
286 << " value " << data
.value
287 << " in registry key " << data
.key
288 << " in hive " << std::hex
<<data
.root
<< std::dec
;
290 } else if (!do_delete
|| result
!= ERROR_FILE_NOT_FOUND
) {
291 ::SetLastError(result
);
292 PLOG(ERROR
) << "Failed to create/open registry key " << data
.key
293 << " in hive " << std::hex
<< data
.root
<< std::dec
;
298 void RegistrySetter::Apply() const {
299 ApplyList(apply_list_
);
302 void RegistrySetter::Revert() const {
303 ApplyList(revert_list_
);
306 // ConfiguratorDriver implementation.
308 ConfiguratorDriver::ConfiguratorDriver(IEConfigurator
* configurator
)
309 : configurator_(configurator
) {
310 DCHECK(configurator
);
313 ConfiguratorDriver::~ConfiguratorDriver() {
316 void ConfiguratorDriver::OnTestProgramStart(
317 const testing::UnitTest
& unit_test
) {
318 configurator_
->Initialize();
321 void ConfiguratorDriver::OnTestStart(const testing::TestInfo
& test_info
) {
322 configurator_
->ApplySettings();
325 void ConfiguratorDriver::OnTestProgramEnd(const testing::UnitTest
& unit_test
) {
326 configurator_
->RevertSettings();
329 // IE7Configurator implementation
331 IE7Configurator::IE7Configurator() {
334 IE7Configurator::~IE7Configurator() {
337 void IE7Configurator::Initialize() {
338 // Suppress the friendly "Hi! I popped up an info bar for you. Did you see
339 // the info bar I just showed you? There's a yellow thing in your IE window
340 // that wasn't there before! I call it an Information Bar. Did you notice
341 // the Information Bar?" dialog that it likes to show.
342 setter_
.AddDWORDValue(HKEY_CURRENT_USER
, kKeyIEInformationBar
,
346 void IE7Configurator::ApplySettings() {
350 void IE7Configurator::RevertSettings() {
354 // ModernIEConfigurator implementation
356 const ModernIEConfigurator::RunOnceValueNames
357 ModernIEConfigurator::kIE9ValueNames
= {
359 kValueIE9CompletionTime
,
362 const ModernIEConfigurator::RunOnceValueNames
363 ModernIEConfigurator::kIE10ValueNames
= {
365 kValueIE10CompletionTime
,
368 ModernIEConfigurator::ModernIEConfigurator(IEVersion ie_version
)
369 : ie_version_(ie_version
),
370 run_once_value_names_(RunOnceNamesForVersion(ie_version
)) {
373 ModernIEConfigurator::~ModernIEConfigurator() {
377 const ModernIEConfigurator::RunOnceValueNames
*
378 ModernIEConfigurator::RunOnceNamesForVersion(
379 IEVersion ie_version
) {
380 switch (ie_version
) {
382 return &kIE9ValueNames
;
385 return &kIE10ValueNames
;
393 // Returns true if the per-user setup is complete.
394 bool ModernIEConfigurator::IsPerUserSetupComplete() {
395 bool is_complete
= false;
396 base::win::RegKey key_main
;
398 if (key_main
.Open(HKEY_CURRENT_USER
, kKeyIEMain
,
399 KEY_QUERY_VALUE
) == ERROR_SUCCESS
) {
401 FILETIME completion_time
= {};
402 DWORD size
= sizeof(completion_time
);
404 if (key_main
.ReadValueDW(run_once_value_names_
->completed
,
405 &completed
) == ERROR_SUCCESS
&&
407 key_main
.ReadValue(run_once_value_names_
->completion_time
,
408 &completion_time
, &size
, NULL
) == ERROR_SUCCESS
&&
409 size
== sizeof(completion_time
)) {
417 // Returns the path to the IE9 Approved Extensions key for Chrome Frame.
419 string16
ModernIEConfigurator::GetChromeFrameBHOCLSID() {
420 string16
bho_guid(39, L
'\0');
421 int guid_len
= StringFromGUID2(CLSID_ChromeFrameBHO
, &bho_guid
[0],
423 DCHECK_EQ(guid_len
, static_cast<int>(bho_guid
.size()));
424 bho_guid
.resize(guid_len
- 1);
428 // Returns true if the add-on enablement prompt is disabled by Group Policy.
430 bool ModernIEConfigurator::IsAddonPromptDisabledForChromeFrame() {
431 bool is_disabled
= false;
432 base::win::RegKey key
;
434 // See if the prompt is disabled for this user of IE.
435 if (key
.Open(HKEY_CURRENT_USER
, kKeyIEApprovedExtensions
,
436 KEY_QUERY_VALUE
) == ERROR_SUCCESS
) {
437 DWORD type
= REG_NONE
;
439 if (key
.ReadValue(GetChromeFrameBHOCLSID().c_str(), NULL
, &size
,
440 &type
) == ERROR_SUCCESS
&&
441 type
== REG_BINARY
) {
446 // Now check if the prompt is disabled for all add-ons via Group Policy.
448 key
.Open(HKEY_LOCAL_MACHINE
, kKeyPoliciesExt
,
449 KEY_QUERY_VALUE
) == ERROR_SUCCESS
) {
451 if (key
.ReadValueDW(kValueIgnoreFrameApprovalCheck
,
452 &ignore
) == ERROR_SUCCESS
&&
461 void ModernIEConfigurator::Initialize() {
462 // Check for per-user IE setup.
463 if (!IsPerUserSetupComplete()) {
464 const HKEY root
= HKEY_CURRENT_USER
;
465 // Suppress the "Set up Internet Explorer" dialog.
466 setter_
.AddDWORDValue(root
, kKeyIEMain
, run_once_value_names_
->completed
,
468 setter_
.AddFILETIMEValue(root
, kKeyIEMain
,
469 run_once_value_names_
->completion_time
,
470 base::Time::Now().ToFileTime());
471 if (ie_version_
== IE_9
) {
472 setter_
.AddDWORDValue(root
, kKeyIEMain
, kValueIE9LastShown
, 1);
473 // Don't show a tour of IE 9.
474 setter_
.AddDWORDValue(root
, kKeyIEMain
, kValueIE9TourNoShow
, 1);
476 // Turn off the phishing filter.
477 setter_
.AddDWORDValue(root
, kKeyIEPhishingFilter
, kValueEnabledV9
, 0);
478 // Don't download compatibility view lists.
479 setter_
.AddDWORDValue(root
, kKeyIEBrowserEmulation
,
480 kValueMSCompatibilityMode
, 0);
483 // Turn off the "'Foo' add-on from 'Bar' is ready for use." prompt via Group
485 if (!IsAddonPromptDisabledForChromeFrame()) {
486 setter_
.AddDWORDValue(HKEY_LOCAL_MACHINE
, kKeyPoliciesExt
,
487 kValueIgnoreFrameApprovalCheck
, 1);
491 void ModernIEConfigurator::ApplySettings() {
495 void ModernIEConfigurator::RevertSettings() {
501 // Configurator implementation.
503 IEConfigurator::IEConfigurator() {
506 IEConfigurator::~IEConfigurator() {
509 IEConfigurator
* CreateConfigurator() {
510 IEConfigurator
* configurator
= NULL
;
512 IEVersion ie_version
= GetInstalledIEVersion();
513 switch (ie_version
) {
515 configurator
= new IE7Configurator();
519 configurator
= new ModernIEConfigurator(ie_version
);
528 void InstallIEConfigurator() {
529 IEConfigurator
* configurator
= CreateConfigurator();
531 if (configurator
!= NULL
) {
532 testing::UnitTest::GetInstance()->listeners().Append(
533 new ConfiguratorDriver(configurator
));
537 } // namespace chrome_frame_test