Home | History | Annotate | Download | only in test
      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
    599