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