Home | History | Annotate | Download | only in extensions
      1 // Copyright 2013 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/browser/extensions/extension_test_notification_observer.h"
      6 
      7 #include "base/callback_list.h"
      8 #include "chrome/browser/extensions/extension_service.h"
      9 #include "chrome/browser/profiles/profile_manager.h"
     10 #include "chrome/browser/ui/browser.h"
     11 #include "chrome/browser/ui/browser_window.h"
     12 #include "content/public/browser/notification_registrar.h"
     13 #include "content/public/browser/notification_service.h"
     14 #include "content/public/browser/render_view_host.h"
     15 #include "content/public/test/test_utils.h"
     16 #include "extensions/browser/extension_system.h"
     17 #include "extensions/browser/process_manager.h"
     18 #include "extensions/common/extension.h"
     19 
     20 using extensions::Extension;
     21 
     22 namespace {
     23 
     24 bool HasExtensionPageActionCountReachedTarget(LocationBarTesting* location_bar,
     25                                               int target_page_action_count) {
     26   VLOG(1) << "Number of page actions: " << location_bar->PageActionCount();
     27   return location_bar->PageActionCount() == target_page_action_count;
     28 }
     29 
     30 bool HasExtensionPageActionVisibilityReachedTarget(
     31     LocationBarTesting* location_bar,
     32     int target_visible_page_action_count) {
     33   VLOG(1) << "Number of visible page actions: "
     34           << location_bar->PageActionVisibleCount();
     35   return location_bar->PageActionVisibleCount() ==
     36          target_visible_page_action_count;
     37 }
     38 
     39 bool HaveAllExtensionRenderViewHostsFinishedLoading(
     40     extensions::ProcessManager* manager) {
     41   extensions::ProcessManager::ViewSet all_views = manager->GetAllViews();
     42   for (extensions::ProcessManager::ViewSet::const_iterator iter =
     43            all_views.begin();
     44        iter != all_views.end(); ++iter) {
     45     if ((*iter)->IsLoading())
     46       return false;
     47   }
     48   return true;
     49 }
     50 
     51 class NotificationSet : public content::NotificationObserver {
     52  public:
     53   void Add(int type, const content::NotificationSource& source);
     54   void Add(int type);
     55 
     56   // Notified any time an Add()ed notification is received.
     57   // The details of the notification are dropped.
     58   base::CallbackList<void()>& callback_list() {
     59     return callback_list_;
     60   }
     61 
     62  private:
     63   // content::NotificationObserver:
     64   virtual void Observe(int type,
     65                        const content::NotificationSource& source,
     66                        const content::NotificationDetails& details) OVERRIDE;
     67 
     68   content::NotificationRegistrar notification_registrar_;
     69   base::CallbackList<void()> callback_list_;
     70 };
     71 
     72 void NotificationSet::Add(
     73     int type,
     74     const content::NotificationSource& source) {
     75   notification_registrar_.Add(this, type, source);
     76 }
     77 
     78 void NotificationSet::Add(int type) {
     79   Add(type, content::NotificationService::AllSources());
     80 }
     81 
     82 void NotificationSet::Observe(
     83     int type,
     84     const content::NotificationSource& source,
     85     const content::NotificationDetails& details) {
     86   callback_list_.Notify();
     87 }
     88 
     89 void MaybeQuit(content::MessageLoopRunner* runner,
     90                const base::Callback<bool(void)>& condition) {
     91   if (condition.Run())
     92     runner->Quit();
     93 }
     94 
     95 void WaitForCondition(
     96     const base::Callback<bool(void)>& condition,
     97     NotificationSet* notification_set) {
     98   if (condition.Run())
     99     return;
    100 
    101   scoped_refptr<content::MessageLoopRunner> runner(
    102       new content::MessageLoopRunner);
    103   scoped_ptr<base::CallbackList<void()>::Subscription> subscription =
    104       notification_set->callback_list().Add(
    105           base::Bind(&MaybeQuit, base::Unretained(runner.get()), condition));
    106   runner->Run();
    107 }
    108 
    109 void WaitForCondition(
    110     const base::Callback<bool(void)>& condition,
    111     int type) {
    112   NotificationSet notification_set;
    113   notification_set.Add(type);
    114   WaitForCondition(condition, &notification_set);
    115 }
    116 
    117 }  // namespace
    118 
    119 ExtensionTestNotificationObserver::ExtensionTestNotificationObserver(
    120     Browser* browser)
    121     : browser_(browser),
    122       profile_(NULL),
    123       extension_installs_observed_(0),
    124       extension_load_errors_observed_(0),
    125       crx_installers_done_observed_(0) {
    126 }
    127 
    128 ExtensionTestNotificationObserver::~ExtensionTestNotificationObserver() {}
    129 
    130 Profile* ExtensionTestNotificationObserver::GetProfile() {
    131   if (!profile_) {
    132     if (browser_)
    133       profile_ = browser_->profile();
    134     else
    135       profile_ = ProfileManager::GetActiveUserProfile();
    136   }
    137   return profile_;
    138 }
    139 
    140 void ExtensionTestNotificationObserver::WaitForNotification(
    141     int notification_type) {
    142   // TODO(bauerb): Using a WindowedNotificationObserver like this can break
    143   // easily, if the notification we're waiting for is sent before this method.
    144   // Change it so that the WindowedNotificationObserver is constructed earlier.
    145   content::NotificationRegistrar registrar;
    146   registrar.Add(
    147       this, notification_type, content::NotificationService::AllSources());
    148   content::WindowedNotificationObserver(
    149       notification_type, content::NotificationService::AllSources()).Wait();
    150 }
    151 
    152 bool ExtensionTestNotificationObserver::WaitForPageActionCountChangeTo(
    153     int count) {
    154   LocationBarTesting* location_bar =
    155       browser_->window()->GetLocationBar()->GetLocationBarForTesting();
    156   WaitForCondition(
    157       base::Bind(
    158           &HasExtensionPageActionCountReachedTarget, location_bar, count),
    159       chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED);
    160   return true;
    161 }
    162 
    163 bool ExtensionTestNotificationObserver::WaitForPageActionVisibilityChangeTo(
    164     int count) {
    165   LocationBarTesting* location_bar =
    166       browser_->window()->GetLocationBar()->GetLocationBarForTesting();
    167   WaitForCondition(
    168       base::Bind(
    169           &HasExtensionPageActionVisibilityReachedTarget, location_bar, count),
    170       chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED);
    171   return true;
    172 }
    173 
    174 bool ExtensionTestNotificationObserver::WaitForExtensionViewsToLoad() {
    175   extensions::ProcessManager* manager =
    176       extensions::ExtensionSystem::Get(GetProfile())->process_manager();
    177   NotificationSet notification_set;
    178   notification_set.Add(content::NOTIFICATION_WEB_CONTENTS_DESTROYED);
    179   notification_set.Add(content::NOTIFICATION_LOAD_STOP);
    180   WaitForCondition(
    181       base::Bind(&HaveAllExtensionRenderViewHostsFinishedLoading, manager),
    182       &notification_set);
    183   return true;
    184 }
    185 
    186 bool ExtensionTestNotificationObserver::WaitForExtensionInstall() {
    187   int before = extension_installs_observed_;
    188   WaitForNotification(chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED);
    189   return extension_installs_observed_ == (before + 1);
    190 }
    191 
    192 bool ExtensionTestNotificationObserver::WaitForExtensionInstallError() {
    193   int before = extension_installs_observed_;
    194   content::WindowedNotificationObserver(
    195       chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
    196       content::NotificationService::AllSources()).Wait();
    197   return extension_installs_observed_ == before;
    198 }
    199 
    200 void ExtensionTestNotificationObserver::WaitForExtensionLoad() {
    201   WaitForNotification(chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED);
    202 }
    203 
    204 void ExtensionTestNotificationObserver::WaitForExtensionAndViewLoad() {
    205   this->WaitForExtensionLoad();
    206   WaitForExtensionViewsToLoad();
    207 }
    208 
    209 bool ExtensionTestNotificationObserver::WaitForExtensionLoadError() {
    210   int before = extension_load_errors_observed_;
    211   WaitForNotification(chrome::NOTIFICATION_EXTENSION_LOAD_ERROR);
    212   return extension_load_errors_observed_ != before;
    213 }
    214 
    215 bool ExtensionTestNotificationObserver::WaitForExtensionCrash(
    216     const std::string& extension_id) {
    217   ExtensionService* service = extensions::ExtensionSystem::Get(
    218       GetProfile())->extension_service();
    219 
    220   if (!service->GetExtensionById(extension_id, true)) {
    221     // The extension is already unloaded, presumably due to a crash.
    222     return true;
    223   }
    224   content::WindowedNotificationObserver(
    225       chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
    226       content::NotificationService::AllSources()).Wait();
    227   return (service->GetExtensionById(extension_id, true) == NULL);
    228 }
    229 
    230 bool ExtensionTestNotificationObserver::WaitForCrxInstallerDone() {
    231   int before = crx_installers_done_observed_;
    232   WaitForNotification(chrome::NOTIFICATION_CRX_INSTALLER_DONE);
    233   return crx_installers_done_observed_ == (before + 1);
    234 }
    235 
    236 void ExtensionTestNotificationObserver::Watch(
    237     int type,
    238     const content::NotificationSource& source) {
    239   CHECK(!observer_);
    240   observer_.reset(new content::WindowedNotificationObserver(type, source));
    241   registrar_.Add(this, type, source);
    242 }
    243 
    244 void ExtensionTestNotificationObserver::Wait() {
    245   observer_->Wait();
    246 
    247   registrar_.RemoveAll();
    248   observer_.reset();
    249 }
    250 
    251 void ExtensionTestNotificationObserver::Observe(
    252     int type,
    253     const content::NotificationSource& source,
    254     const content::NotificationDetails& details) {
    255   switch (type) {
    256     case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED:
    257       last_loaded_extension_id_ =
    258         content::Details<const Extension>(details).ptr()->id();
    259       VLOG(1) << "Got EXTENSION_LOADED notification.";
    260       break;
    261 
    262     case chrome::NOTIFICATION_CRX_INSTALLER_DONE:
    263       VLOG(1) << "Got CRX_INSTALLER_DONE notification.";
    264       {
    265           const Extension* extension =
    266             content::Details<const Extension>(details).ptr();
    267           if (extension)
    268             last_loaded_extension_id_ = extension->id();
    269           else
    270             last_loaded_extension_id_.clear();
    271       }
    272       ++crx_installers_done_observed_;
    273       break;
    274 
    275     case chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED:
    276       VLOG(1) << "Got EXTENSION_INSTALLED notification.";
    277       ++extension_installs_observed_;
    278       break;
    279 
    280     case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR:
    281       VLOG(1) << "Got EXTENSION_LOAD_ERROR notification.";
    282       ++extension_load_errors_observed_;
    283       break;
    284 
    285     default:
    286       NOTREACHED();
    287       break;
    288   }
    289 }
    290