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/strings/string16.h"
17 #include "base/time/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 kValueEnabledV8
[] = L
"EnabledV8";
40 const wchar_t kValueEnabledV9
[] = L
"EnabledV9";
41 const wchar_t kValueFirstTime
[] = L
"FirstTime";
42 const wchar_t kValueIE8Completed
[] = L
"IE8RunOncePerInstallCompleted";
43 const wchar_t kValueIE8CompletionTime
[] = L
"IE8RunOnceCompletionTime";
44 const wchar_t kValueIE8RunOnceLastShown
[] = L
"IE8RunOnceLastShown";
45 const wchar_t kValueIE8RunOnceLastShownTimestamp
[] =
46 L
"IE8RunOnceLastShown_TIMESTAMP";
47 const wchar_t kValueIE8TourNoShow
[] = L
"IE8TourNoShow";
48 const wchar_t kValueIE9Completed
[] = L
"IE9RunOncePerInstallCompleted";
49 const wchar_t kValueIE9CompletionTime
[] = L
"IE9RunOnceCompletionTime";
50 const wchar_t kValueIE9RunOnceLastShown
[] = L
"IE9RunOnceLastShown";
51 const wchar_t kValueIE9RunOnceLastShownTimestamp
[] =
52 L
"IE9RunOnceLastShown_TIMESTAMP";
53 const wchar_t kValueIE9TourNoShow
[] = L
"IE9TourNoShow";
54 const wchar_t kValueIE10Completed
[] = L
"IE10RunOncePerInstallCompleted";
55 const wchar_t kValueIE10CompletionTime
[] = L
"IE10RunOnceCompletionTime";
56 const wchar_t kValueIE10RunOnceLastShown
[] = L
"IE10RunOnceLastShown";
57 const wchar_t kValueIE10RunOnceLastShownTimestamp
[] =
58 L
"IE10RunOnceLastShown_TIMESTAMP";
59 const wchar_t kValueIgnoreFrameApprovalCheck
[] = L
"IgnoreFrameApprovalCheck";
60 const wchar_t kValueMSCompatibilityMode
[] = L
"MSCompatibilityMode";
62 // A helper class that accumulate a set of registry mutations and corresponding
63 // undo data via calls to its Add*() methods. The mutations can be applied to
64 // the registry (repeatedly, if desired) via a call to Apply(). Revert() can be
65 // called to apply the accumulated undo data.
66 class RegistrySetter
{
71 // Adds a mutation that sets a REG_DWORD registry value, creating any
72 // intermediate keys as necessary. |key| and |value| must remain valid
73 // throughout all calls to Apply().
74 void AddDWORDValue(HKEY root
, const wchar_t* key
, const wchar_t* value
,
77 // Adds a mutation that assigns a FILETIME to a REG_BINARY registry value,
78 // creating any intermediate keys as necessary. |key| and |value| must remain
79 // valid throughout all calls to Apply().
80 void AddFILETIMEValue(HKEY root
, const wchar_t* key
, const wchar_t* value
,
83 // Applies all mutations in the order they were added. Errors encountered
84 // along the way are logged, but do not stop progress.
87 // Applies the undo data in the reverse order that their corresponding
88 // mutations were added.
92 // The data for an individual registry value. A non-existent value is
93 // indicated by type = REG_NONE and empty data.
99 std::vector
<uint8
> data
;
102 typedef std::list
<RegistryData
> RegistryDataList
;
104 // Adds a mutation to the end of the apply list with the given data, and a
105 // mutation to the revert list with the current state of the value.
106 void AddValue(HKEY root
, const wchar_t* key
, const wchar_t* value
, DWORD type
,
107 const uint8
* value_begin
, size_t value_len
);
109 // Add a mutation to the revert list that restores a registry value to its
110 // current state. This only adds entries to set or remove |value|. If
111 // portions of the hierarchy identified by |key| do not exist, but are created
112 // between the invocation of this method and the time the revert list is
113 // applied, only |value| is deleted upon revert (intermediate keys are not
115 void SaveCurrentValue(HKEY root
, const wchar_t* key
, const wchar_t* value
);
117 // Applies all mutations in |data_list| in order.
118 static void ApplyList(const RegistryDataList
& data_list
);
120 RegistryDataList apply_list_
;
121 RegistryDataList revert_list_
;
123 DISALLOW_COPY_AND_ASSIGN(RegistrySetter
);
126 // A Google Test event listener that delegates to a configurator.
127 class ConfiguratorDriver
: public testing::EmptyTestEventListener
{
129 explicit ConfiguratorDriver(IEConfigurator
* configurator
);
130 virtual ~ConfiguratorDriver();
132 virtual void OnTestProgramStart(const testing::UnitTest
& unit_test
) OVERRIDE
;
133 virtual void OnTestStart(const testing::TestInfo
& test_info
) OVERRIDE
;
134 virtual void OnTestProgramEnd(const testing::UnitTest
& unit_test
) OVERRIDE
;
137 scoped_ptr
<IEConfigurator
> configurator_
;
138 DISALLOW_COPY_AND_ASSIGN(ConfiguratorDriver
);
141 // A configurator for Internet Explorer 7.
142 class IE7Configurator
: public IEConfigurator
{
145 virtual ~IE7Configurator();
147 virtual void Initialize() OVERRIDE
;
148 virtual void ApplySettings() OVERRIDE
;
149 virtual void RevertSettings() OVERRIDE
;
152 RegistrySetter setter_
;
154 DISALLOW_COPY_AND_ASSIGN(IE7Configurator
);
157 // A configurator for Internet Explorer 8, 9, and 10.
158 class ModernIEConfigurator
: public IEConfigurator
{
160 explicit ModernIEConfigurator(IEVersion ie_version
);
161 virtual ~ModernIEConfigurator();
163 virtual void Initialize() OVERRIDE
;
164 virtual void ApplySettings() OVERRIDE
;
165 virtual void RevertSettings() OVERRIDE
;
168 // The names of the registry values used to determine if IE's one-time
169 // initialization has been completed.
170 struct RunOnceValueNames
{
171 // This DWORD value is non-zero once initialization has been completed.
172 const wchar_t* completed
;
173 // This 8-byte binary value is the FILETIME of completion.
174 const wchar_t* completion_time
;
175 // This DWORD value is non-zero if run-once was previously deferred.
176 const wchar_t* last_shown
;
177 // This 8-byte binary value is the FILETIME of run-once deferral.
178 const wchar_t* last_shown_timestamp
;
181 static const RunOnceValueNames kIE8ValueNames
;
182 static const RunOnceValueNames kIE9ValueNames
;
183 static const RunOnceValueNames kIE10ValueNames
;
185 static const RunOnceValueNames
* RunOnceNamesForVersion(IEVersion ie_version
);
186 bool IsPerUserSetupComplete();
187 static string16
GetChromeFrameBHOCLSID();
188 static bool IsAddonPromptDisabledForChromeFrame();
190 const IEVersion ie_version_
;
191 const RunOnceValueNames
* run_once_value_names_
;
192 RegistrySetter setter_
;
194 DISALLOW_COPY_AND_ASSIGN(ModernIEConfigurator
);
197 // RegistrySetter implementation.
199 RegistrySetter::RegistrySetter() {
202 RegistrySetter::~RegistrySetter() {
205 void RegistrySetter::AddValue(HKEY root
,
207 const wchar_t* value
,
209 const uint8
* value_begin
,
211 RegistryData the_data
= {
216 std::vector
<uint8
>(value_begin
, value_begin
+ value_len
)
218 apply_list_
.push_back(the_data
);
219 SaveCurrentValue(root
, key
, value
);
222 void RegistrySetter::SaveCurrentValue(HKEY root
,
224 const wchar_t* value
) {
225 base::win::RegKey the_key
;
226 RegistryData the_data
= { root
, key
, value
, REG_NONE
};
228 LONG result
= the_key
.Open(root
, key
, KEY_QUERY_VALUE
);
229 if (result
== ERROR_SUCCESS
) {
231 result
= the_key
.ReadValue(value
, NULL
, &size
, &the_data
.type
);
232 if (result
== ERROR_FILE_NOT_FOUND
) {
233 // Add a mutation to delete the value.
234 the_data
.type
= REG_NONE
;
235 revert_list_
.push_front(the_data
);
236 } else if (result
== ERROR_SUCCESS
) {
237 the_data
.data
.resize(size
);
238 result
= the_key
.ReadValue(value
, &the_data
.data
[0], &size
,
240 if (result
== ERROR_SUCCESS
) {
241 revert_list_
.push_front(the_data
);
243 ::SetLastError(result
);
244 PLOG(ERROR
) << __FUNCTION__
<< " unexpected error reading data for "
245 << value
<< " from key " << key
246 << ". The current value will not be restored upon revert.";
249 ::SetLastError(result
);
250 PLOG(ERROR
) << __FUNCTION__
<< " unexpected error reading " << value
251 << " from key " << key
252 << ". The current value will not be restored upon revert.";
254 } else if (result
== ERROR_FILE_NOT_FOUND
) {
255 // Add a mutation to delete the value (but not any keys).
256 revert_list_
.push_front(the_data
);
258 ::SetLastError(result
);
259 PLOG(ERROR
) << __FUNCTION__
<< " unexpected error opening key " << key
260 << " to read value " << value
261 << ". The current value will not be restored upon revert.";
265 void RegistrySetter::AddDWORDValue(HKEY root
,
267 const wchar_t* value
,
269 const uint8
* data_ptr
= reinterpret_cast<uint8
*>(&data
);
270 AddValue(root
, key
, value
, REG_DWORD
, data_ptr
, sizeof(data
));
273 void RegistrySetter::AddFILETIMEValue(HKEY root
,
275 const wchar_t* value
,
277 const uint8
* data_ptr
= reinterpret_cast<uint8
*>(&data
);
278 AddValue(root
, key
, value
, REG_BINARY
, data_ptr
, sizeof(data
));
282 void RegistrySetter::ApplyList(const RegistryDataList
& data_list
) {
283 base::win::RegKey key
;
284 LONG result
= ERROR_SUCCESS
;
285 for (RegistryDataList::const_iterator
scan(data_list
.begin());
286 scan
!= data_list
.end(); ++scan
) {
287 const RegistryData
& data
= *scan
;
288 const bool do_delete
= (data
.type
== REG_NONE
&& data
.data
.empty());
290 key
.Open(data
.root
, data
.key
, KEY_SET_VALUE
) :
291 key
.Create(data
.root
, data
.key
, KEY_SET_VALUE
);
292 if (result
== ERROR_SUCCESS
) {
294 result
= key
.DeleteValue(data
.value
);
296 result
= key
.WriteValue(data
.value
,
297 data
.data
.empty() ? NULL
: &data
.data
[0],
298 data
.data
.size(), data
.type
);
300 if (result
!= ERROR_SUCCESS
) {
301 ::SetLastError(result
);
302 PLOG(ERROR
) << "Failed to " << (do_delete
? "delete" : "set")
303 << " value " << data
.value
304 << " in registry key " << data
.key
305 << " in hive " << std::hex
<<data
.root
<< std::dec
;
307 } else if (!do_delete
|| result
!= ERROR_FILE_NOT_FOUND
) {
308 ::SetLastError(result
);
309 PLOG(ERROR
) << "Failed to create/open registry key " << data
.key
310 << " in hive " << std::hex
<< data
.root
<< std::dec
;
315 void RegistrySetter::Apply() const {
316 ApplyList(apply_list_
);
319 void RegistrySetter::Revert() const {
320 ApplyList(revert_list_
);
323 // ConfiguratorDriver implementation.
325 ConfiguratorDriver::ConfiguratorDriver(IEConfigurator
* configurator
)
326 : configurator_(configurator
) {
327 DCHECK(configurator
);
330 ConfiguratorDriver::~ConfiguratorDriver() {
333 void ConfiguratorDriver::OnTestProgramStart(
334 const testing::UnitTest
& unit_test
) {
335 configurator_
->Initialize();
338 void ConfiguratorDriver::OnTestStart(const testing::TestInfo
& test_info
) {
339 configurator_
->ApplySettings();
342 void ConfiguratorDriver::OnTestProgramEnd(const testing::UnitTest
& unit_test
) {
343 configurator_
->RevertSettings();
346 // IE7Configurator implementation
348 IE7Configurator::IE7Configurator() {
351 IE7Configurator::~IE7Configurator() {
354 void IE7Configurator::Initialize() {
355 // Suppress the friendly "Hi! I popped up an info bar for you. Did you see
356 // the info bar I just showed you? There's a yellow thing in your IE window
357 // that wasn't there before! I call it an Information Bar. Did you notice
358 // the Information Bar?" dialog that it likes to show.
359 setter_
.AddDWORDValue(HKEY_CURRENT_USER
, kKeyIEInformationBar
,
363 void IE7Configurator::ApplySettings() {
367 void IE7Configurator::RevertSettings() {
371 // ModernIEConfigurator implementation
373 const ModernIEConfigurator::RunOnceValueNames
374 ModernIEConfigurator::kIE8ValueNames
= {
376 kValueIE8CompletionTime
,
377 kValueIE8RunOnceLastShown
,
378 kValueIE8RunOnceLastShownTimestamp
,
381 const ModernIEConfigurator::RunOnceValueNames
382 ModernIEConfigurator::kIE9ValueNames
= {
384 kValueIE9CompletionTime
,
385 kValueIE9RunOnceLastShown
,
386 kValueIE9RunOnceLastShownTimestamp
,
389 const ModernIEConfigurator::RunOnceValueNames
390 ModernIEConfigurator::kIE10ValueNames
= {
392 kValueIE10CompletionTime
,
393 kValueIE10RunOnceLastShown
,
394 kValueIE10RunOnceLastShownTimestamp
,
397 ModernIEConfigurator::ModernIEConfigurator(IEVersion ie_version
)
398 : ie_version_(ie_version
),
399 run_once_value_names_(RunOnceNamesForVersion(ie_version
)) {
402 ModernIEConfigurator::~ModernIEConfigurator() {
406 const ModernIEConfigurator::RunOnceValueNames
*
407 ModernIEConfigurator::RunOnceNamesForVersion(
408 IEVersion ie_version
) {
409 switch (ie_version
) {
411 return &kIE8ValueNames
;
414 return &kIE9ValueNames
;
417 return &kIE10ValueNames
;
425 // Returns true if the per-user setup is complete.
426 bool ModernIEConfigurator::IsPerUserSetupComplete() {
427 bool is_complete
= false;
428 base::win::RegKey key_main
;
430 if (key_main
.Open(HKEY_CURRENT_USER
, kKeyIEMain
,
431 KEY_QUERY_VALUE
) != ERROR_SUCCESS
) {
435 DWORD dword_value
= 0;
436 FILETIME shown_time
= {};
437 FILETIME completion_time
= {};
438 DWORD size
= sizeof(completion_time
);
440 // See if the user has seen the first-run prompt.
441 if (key_main
.ReadValue(run_once_value_names_
->last_shown_timestamp
,
442 &shown_time
, &size
, NULL
) != ERROR_SUCCESS
||
443 size
!= sizeof(shown_time
)) {
447 // See if setup was completed.
448 if (key_main
.ReadValue(run_once_value_names_
->completion_time
,
449 &completion_time
, &size
, NULL
) != ERROR_SUCCESS
||
450 size
!= sizeof(completion_time
)) {
454 // See if setup was completed after the last time the prompt was shown.
455 base::Time time_shown
= base::Time::FromFileTime(shown_time
);
456 base::Time time_completed
= base::Time::FromFileTime(completion_time
);
457 if (time_shown
>= time_completed
)
463 // Returns the path to the IE9 Approved Extensions key for Chrome Frame.
465 string16
ModernIEConfigurator::GetChromeFrameBHOCLSID() {
466 string16
bho_guid(39, L
'\0');
467 int guid_len
= StringFromGUID2(CLSID_ChromeFrameBHO
, &bho_guid
[0],
469 DCHECK_EQ(guid_len
, static_cast<int>(bho_guid
.size()));
470 bho_guid
.resize(guid_len
- 1);
474 // Returns true if the add-on enablement prompt is disabled by Group Policy.
476 bool ModernIEConfigurator::IsAddonPromptDisabledForChromeFrame() {
477 bool is_disabled
= false;
478 base::win::RegKey key
;
480 // See if the prompt is disabled for this user of IE.
481 if (key
.Open(HKEY_CURRENT_USER
, kKeyIEApprovedExtensions
,
482 KEY_QUERY_VALUE
) == ERROR_SUCCESS
) {
483 DWORD type
= REG_NONE
;
485 if (key
.ReadValue(GetChromeFrameBHOCLSID().c_str(), NULL
, &size
,
486 &type
) == ERROR_SUCCESS
&&
487 type
== REG_BINARY
) {
492 // Now check if the prompt is disabled for all add-ons via Group Policy.
494 key
.Open(HKEY_LOCAL_MACHINE
, kKeyPoliciesExt
,
495 KEY_QUERY_VALUE
) == ERROR_SUCCESS
) {
497 if (key
.ReadValueDW(kValueIgnoreFrameApprovalCheck
,
498 &ignore
) == ERROR_SUCCESS
&&
507 void ModernIEConfigurator::Initialize() {
508 // Check for per-user IE setup.
509 if (!IsPerUserSetupComplete()) {
510 base::Time
time(base::Time::Now());
511 const HKEY root
= HKEY_CURRENT_USER
;
512 // Suppress the "Set up Internet Explorer" dialog.
513 setter_
.AddDWORDValue(root
, kKeyIEMain
, run_once_value_names_
->last_shown
,
515 setter_
.AddDWORDValue(root
, kKeyIEMain
, run_once_value_names_
->completed
,
517 setter_
.AddFILETIMEValue(root
, kKeyIEMain
,
518 run_once_value_names_
->last_shown_timestamp
,
520 time
+= base::TimeDelta::FromMilliseconds(10);
521 setter_
.AddFILETIMEValue(root
, kKeyIEMain
,
522 run_once_value_names_
->completion_time
,
524 if (ie_version_
< IE_10
) {
525 // Don't show a tour of IE 8 and 9.
526 setter_
.AddDWORDValue(
529 (ie_version_
== IE_8
? kValueIE8TourNoShow
: kValueIE9TourNoShow
),
532 // Turn off the phishing filter.
533 setter_
.AddDWORDValue(
535 kKeyIEPhishingFilter
,
536 (ie_version_
== IE_8
? kValueEnabledV8
: kValueEnabledV9
),
538 // Don't download compatibility view lists.
539 setter_
.AddDWORDValue(root
, kKeyIEBrowserEmulation
,
540 kValueMSCompatibilityMode
, 0);
543 // Turn off the "'Foo' add-on from 'Bar' is ready for use." prompt via Group
545 if (!IsAddonPromptDisabledForChromeFrame()) {
546 setter_
.AddDWORDValue(HKEY_LOCAL_MACHINE
, kKeyPoliciesExt
,
547 kValueIgnoreFrameApprovalCheck
, 1);
551 void ModernIEConfigurator::ApplySettings() {
555 void ModernIEConfigurator::RevertSettings() {
561 // Configurator implementation.
563 IEConfigurator::IEConfigurator() {
566 IEConfigurator::~IEConfigurator() {
569 IEConfigurator
* CreateConfigurator() {
570 IEConfigurator
* configurator
= NULL
;
572 IEVersion ie_version
= GetInstalledIEVersion();
573 switch (ie_version
) {
575 configurator
= new IE7Configurator();
580 configurator
= new ModernIEConfigurator(ie_version
);
589 void InstallIEConfigurator() {
590 IEConfigurator
* configurator
= CreateConfigurator();
592 if (configurator
!= NULL
) {
593 testing::UnitTest::GetInstance()->listeners().Append(
594 new ConfiguratorDriver(configurator
));
598 } // namespace chrome_frame_test