- add sources.
[platform/framework/web/crosswalk.git] / src / chrome_frame / test / ie_configurator.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome_frame/test/ie_configurator.h"
6
7 #include <windows.h>
8 #include <objbase.h>
9
10 #include <ios>
11 #include <list>
12 #include <vector>
13
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"
22
23 namespace chrome_frame_test {
24
25 namespace {
26
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";
61
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 {
67  public:
68   RegistrySetter();
69   ~RegistrySetter();
70
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,
75                      DWORD data);
76
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,
81                         FILETIME data);
82
83   // Applies all mutations in the order they were added.  Errors encountered
84   // along the way are logged, but do not stop progress.
85   void Apply() const;
86
87   // Applies the undo data in the reverse order that their corresponding
88   // mutations were added.
89   void Revert() const;
90
91  private:
92   // The data for an individual registry value.  A non-existent value is
93   // indicated by type = REG_NONE and empty data.
94   struct RegistryData {
95     HKEY root;
96     const wchar_t* key;
97     const wchar_t* value;
98     DWORD type;
99     std::vector<uint8> data;
100   };
101
102   typedef std::list<RegistryData> RegistryDataList;
103
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);
108
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
114   // deleted).
115   void SaveCurrentValue(HKEY root, const wchar_t* key, const wchar_t* value);
116
117   // Applies all mutations in |data_list| in order.
118   static void ApplyList(const RegistryDataList& data_list);
119
120   RegistryDataList apply_list_;
121   RegistryDataList revert_list_;
122
123   DISALLOW_COPY_AND_ASSIGN(RegistrySetter);
124 };
125
126 // A Google Test event listener that delegates to a configurator.
127 class ConfiguratorDriver : public testing::EmptyTestEventListener {
128  public:
129   explicit ConfiguratorDriver(IEConfigurator* configurator);
130   virtual ~ConfiguratorDriver();
131
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;
135
136  private:
137   scoped_ptr<IEConfigurator> configurator_;
138   DISALLOW_COPY_AND_ASSIGN(ConfiguratorDriver);
139 };
140
141 // A configurator for Internet Explorer 7.
142 class IE7Configurator : public IEConfigurator {
143  public:
144   IE7Configurator();
145   virtual ~IE7Configurator();
146
147   virtual void Initialize() OVERRIDE;
148   virtual void ApplySettings() OVERRIDE;
149   virtual void RevertSettings() OVERRIDE;
150
151  private:
152   RegistrySetter setter_;
153
154   DISALLOW_COPY_AND_ASSIGN(IE7Configurator);
155 };
156
157 // A configurator for Internet Explorer 8, 9, and 10.
158 class ModernIEConfigurator : public IEConfigurator {
159  public:
160   explicit ModernIEConfigurator(IEVersion ie_version);
161   virtual ~ModernIEConfigurator();
162
163   virtual void Initialize() OVERRIDE;
164   virtual void ApplySettings() OVERRIDE;
165   virtual void RevertSettings() OVERRIDE;
166
167  private:
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;
179   };
180
181   static const RunOnceValueNames kIE8ValueNames;
182   static const RunOnceValueNames kIE9ValueNames;
183   static const RunOnceValueNames kIE10ValueNames;
184
185   static const RunOnceValueNames* RunOnceNamesForVersion(IEVersion ie_version);
186   bool IsPerUserSetupComplete();
187   static string16 GetChromeFrameBHOCLSID();
188   static bool IsAddonPromptDisabledForChromeFrame();
189
190   const IEVersion ie_version_;
191   const RunOnceValueNames* run_once_value_names_;
192   RegistrySetter setter_;
193
194   DISALLOW_COPY_AND_ASSIGN(ModernIEConfigurator);
195 };
196
197 // RegistrySetter implementation.
198
199 RegistrySetter::RegistrySetter() {
200 }
201
202 RegistrySetter::~RegistrySetter() {
203 }
204
205 void RegistrySetter::AddValue(HKEY root,
206                               const wchar_t* key,
207                               const wchar_t* value,
208                               DWORD type,
209                               const uint8* value_begin,
210                               size_t value_len) {
211   RegistryData the_data = {
212     root,
213     key,
214     value,
215     type,
216     std::vector<uint8>(value_begin, value_begin + value_len)
217   };
218   apply_list_.push_back(the_data);
219   SaveCurrentValue(root, key, value);
220 }
221
222 void RegistrySetter::SaveCurrentValue(HKEY root,
223                                       const wchar_t* key,
224                                       const wchar_t* value) {
225   base::win::RegKey the_key;
226   RegistryData the_data = { root, key, value, REG_NONE };
227
228   LONG result = the_key.Open(root, key, KEY_QUERY_VALUE);
229   if (result == ERROR_SUCCESS) {
230     DWORD size = 0;
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,
239                                  &the_data.type);
240       if (result == ERROR_SUCCESS) {
241         revert_list_.push_front(the_data);
242       } else {
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.";
247       }
248     } else {
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.";
253     }
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);
257   } else {
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.";
262   }
263 }
264
265 void RegistrySetter::AddDWORDValue(HKEY root,
266                                    const wchar_t* key,
267                                    const wchar_t* value,
268                                    DWORD data) {
269   const uint8* data_ptr = reinterpret_cast<uint8*>(&data);
270   AddValue(root, key, value, REG_DWORD, data_ptr, sizeof(data));
271 }
272
273 void RegistrySetter::AddFILETIMEValue(HKEY root,
274                                       const wchar_t* key,
275                                       const wchar_t* value,
276                                       FILETIME data) {
277   const uint8* data_ptr = reinterpret_cast<uint8*>(&data);
278   AddValue(root, key, value, REG_BINARY, data_ptr, sizeof(data));
279 }
280
281 // static
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());
289     result = do_delete ?
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) {
293       if (do_delete) {
294         result = key.DeleteValue(data.value);
295       } else {
296         result = key.WriteValue(data.value,
297                                 data.data.empty() ? NULL : &data.data[0],
298                                 data.data.size(), data.type);
299       }
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;
306       }
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;
311     }
312   }
313 }
314
315 void RegistrySetter::Apply() const {
316   ApplyList(apply_list_);
317 }
318
319 void RegistrySetter::Revert() const {
320   ApplyList(revert_list_);
321 }
322
323 // ConfiguratorDriver implementation.
324
325 ConfiguratorDriver::ConfiguratorDriver(IEConfigurator* configurator)
326     : configurator_(configurator) {
327   DCHECK(configurator);
328 }
329
330 ConfiguratorDriver::~ConfiguratorDriver() {
331 }
332
333 void ConfiguratorDriver::OnTestProgramStart(
334     const testing::UnitTest& unit_test) {
335   configurator_->Initialize();
336 }
337
338 void ConfiguratorDriver::OnTestStart(const testing::TestInfo& test_info) {
339   configurator_->ApplySettings();
340 }
341
342 void ConfiguratorDriver::OnTestProgramEnd(const testing::UnitTest& unit_test) {
343   configurator_->RevertSettings();
344 }
345
346 // IE7Configurator implementation
347
348 IE7Configurator::IE7Configurator() {
349 }
350
351 IE7Configurator::~IE7Configurator() {
352 }
353
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,
360                         kValueFirstTime, 0);
361 }
362
363 void IE7Configurator::ApplySettings() {
364   setter_.Apply();
365 }
366
367 void IE7Configurator::RevertSettings() {
368   setter_.Revert();
369 }
370
371 // ModernIEConfigurator implementation
372
373 const ModernIEConfigurator::RunOnceValueNames
374     ModernIEConfigurator::kIE8ValueNames = {
375   kValueIE8Completed,
376   kValueIE8CompletionTime,
377   kValueIE8RunOnceLastShown,
378   kValueIE8RunOnceLastShownTimestamp,
379 };
380
381 const ModernIEConfigurator::RunOnceValueNames
382     ModernIEConfigurator::kIE9ValueNames = {
383   kValueIE9Completed,
384   kValueIE9CompletionTime,
385   kValueIE9RunOnceLastShown,
386   kValueIE9RunOnceLastShownTimestamp,
387 };
388
389 const ModernIEConfigurator::RunOnceValueNames
390     ModernIEConfigurator::kIE10ValueNames = {
391   kValueIE10Completed,
392   kValueIE10CompletionTime,
393   kValueIE10RunOnceLastShown,
394   kValueIE10RunOnceLastShownTimestamp,
395 };
396
397 ModernIEConfigurator::ModernIEConfigurator(IEVersion ie_version)
398     : ie_version_(ie_version),
399       run_once_value_names_(RunOnceNamesForVersion(ie_version)) {
400 }
401
402 ModernIEConfigurator::~ModernIEConfigurator() {
403 }
404
405 // static
406 const ModernIEConfigurator::RunOnceValueNames*
407     ModernIEConfigurator::RunOnceNamesForVersion(
408         IEVersion ie_version) {
409   switch (ie_version) {
410     case IE_8:
411       return &kIE8ValueNames;
412       break;
413     case IE_9:
414       return &kIE9ValueNames;
415       break;
416     case IE_10:
417       return &kIE10ValueNames;
418       break;
419     default:
420       NOTREACHED();
421   }
422   return NULL;
423 }
424
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;
429
430   if (key_main.Open(HKEY_CURRENT_USER, kKeyIEMain,
431                     KEY_QUERY_VALUE) != ERROR_SUCCESS) {
432     return false;
433   }
434
435   DWORD dword_value = 0;
436   FILETIME shown_time = {};
437   FILETIME completion_time = {};
438   DWORD size = sizeof(completion_time);
439
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)) {
444     return false;
445   }
446
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)) {
451     return false;
452   }
453
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)
458     return false;
459
460   return true;
461 }
462
463 // Returns the path to the IE9 Approved Extensions key for Chrome Frame.
464 // static
465 string16 ModernIEConfigurator::GetChromeFrameBHOCLSID() {
466   string16 bho_guid(39, L'\0');
467   int guid_len = StringFromGUID2(CLSID_ChromeFrameBHO, &bho_guid[0],
468                                  bho_guid.size());
469   DCHECK_EQ(guid_len, static_cast<int>(bho_guid.size()));
470   bho_guid.resize(guid_len - 1);
471   return bho_guid;
472 }
473
474 // Returns true if the add-on enablement prompt is disabled by Group Policy.
475 // static
476 bool ModernIEConfigurator::IsAddonPromptDisabledForChromeFrame() {
477   bool is_disabled = false;
478   base::win::RegKey key;
479
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;
484     DWORD size = 0;
485     if (key.ReadValue(GetChromeFrameBHOCLSID().c_str(), NULL, &size,
486                       &type) == ERROR_SUCCESS &&
487         type == REG_BINARY) {
488       is_disabled = true;
489     }
490   }
491
492   // Now check if the prompt is disabled for all add-ons via Group Policy.
493   if (!is_disabled &&
494       key.Open(HKEY_LOCAL_MACHINE, kKeyPoliciesExt,
495                KEY_QUERY_VALUE) == ERROR_SUCCESS) {
496     DWORD ignore = 0;
497     if (key.ReadValueDW(kValueIgnoreFrameApprovalCheck,
498                         &ignore) == ERROR_SUCCESS &&
499         ignore != 0) {
500       is_disabled = true;
501     }
502   }
503
504   return is_disabled;
505 }
506
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,
514                           1);
515     setter_.AddDWORDValue(root, kKeyIEMain, run_once_value_names_->completed,
516                           1);
517     setter_.AddFILETIMEValue(root, kKeyIEMain,
518                              run_once_value_names_->last_shown_timestamp,
519                              time.ToFileTime());
520     time += base::TimeDelta::FromMilliseconds(10);
521     setter_.AddFILETIMEValue(root, kKeyIEMain,
522                              run_once_value_names_->completion_time,
523                              time.ToFileTime());
524     if (ie_version_ < IE_10) {
525       // Don't show a tour of IE 8 and 9.
526       setter_.AddDWORDValue(
527           root,
528           kKeyIEMain,
529           (ie_version_ == IE_8 ? kValueIE8TourNoShow : kValueIE9TourNoShow),
530           1);
531     }
532     // Turn off the phishing filter.
533     setter_.AddDWORDValue(
534         root,
535         kKeyIEPhishingFilter,
536         (ie_version_ == IE_8 ? kValueEnabledV8 : kValueEnabledV9),
537         0);
538     // Don't download compatibility view lists.
539     setter_.AddDWORDValue(root, kKeyIEBrowserEmulation,
540                           kValueMSCompatibilityMode, 0);
541   }
542
543   // Turn off the "'Foo' add-on from 'Bar' is ready for use." prompt via Group
544   // Policy.
545   if (!IsAddonPromptDisabledForChromeFrame()) {
546     setter_.AddDWORDValue(HKEY_LOCAL_MACHINE, kKeyPoliciesExt,
547                           kValueIgnoreFrameApprovalCheck, 1);
548   }
549 }
550
551 void ModernIEConfigurator::ApplySettings() {
552   setter_.Apply();
553 }
554
555 void ModernIEConfigurator::RevertSettings() {
556   setter_.Revert();
557 }
558
559 }  // namespace
560
561 // Configurator implementation.
562
563 IEConfigurator::IEConfigurator() {
564 }
565
566 IEConfigurator::~IEConfigurator() {
567 }
568
569 IEConfigurator* CreateConfigurator() {
570   IEConfigurator* configurator = NULL;
571
572   IEVersion ie_version = GetInstalledIEVersion();
573   switch (ie_version) {
574     case IE_7:
575       configurator = new IE7Configurator();
576       break;
577     case IE_8:
578     case IE_9:
579     case IE_10:
580       configurator = new ModernIEConfigurator(ie_version);
581       break;
582     default:
583       break;
584   }
585
586   return configurator;
587 }
588
589 void InstallIEConfigurator() {
590   IEConfigurator* configurator = CreateConfigurator();
591
592   if (configurator != NULL) {
593     testing::UnitTest::GetInstance()->listeners().Append(
594         new ConfiguratorDriver(configurator));
595   }
596 }
597
598 }  // namespace chrome_frame_test