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 ¬ification_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