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
HasPageActionVisibilityReachedTarget(Browser * browser,size_t target_visible_page_action_count)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
HaveAllExtensionRenderViewHostsFinishedLoading(extensions::ProcessManager * manager)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.
callback_list()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
Add(int type,const content::NotificationSource & source)76 void ExtensionTestNotificationObserver::NotificationSet::Add(
77 int type,
78 const content::NotificationSource& source) {
79 notification_registrar_.Add(this, type, source);
80 }
81
Add(int type)82 void ExtensionTestNotificationObserver::NotificationSet::Add(int type) {
83 Add(type, content::NotificationService::AllSources());
84 }
85
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
ExtensionTestNotificationObserver(Browser * browser)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
~ExtensionTestNotificationObserver()105 ExtensionTestNotificationObserver::~ExtensionTestNotificationObserver() {}
106
GetProfile()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
WaitForNotification(int notification_type)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
WaitForPageActionVisibilityChangeTo(int count)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
WaitForExtensionViewsToLoad()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
WaitForExtensionInstall()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
WaitForExtensionInstallError()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
WaitForExtensionLoad()167 void ExtensionTestNotificationObserver::WaitForExtensionLoad() {
168 WaitForNotification(extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED);
169 }
170
WaitForExtensionAndViewLoad()171 void ExtensionTestNotificationObserver::WaitForExtensionAndViewLoad() {
172 this->WaitForExtensionLoad();
173 WaitForExtensionViewsToLoad();
174 }
175
WaitForExtensionLoadError()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
WaitForExtensionCrash(const std::string & extension_id)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
WaitForCrxInstallerDone()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
Watch(int type,const content::NotificationSource & source)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
Wait()211 void ExtensionTestNotificationObserver::Wait() {
212 observer_->Wait();
213
214 registrar_.RemoveAll();
215 observer_.reset();
216 }
217
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
OnPageActionsUpdated(content::WebContents * web_contents)258 void ExtensionTestNotificationObserver::OnPageActionsUpdated(
259 content::WebContents* web_contents) {
260 MaybeQuit();
261 }
262
WaitForCondition(const ConditionCallback & condition,NotificationSet * notification_set)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
MaybeQuit()286 void ExtensionTestNotificationObserver::MaybeQuit() {
287 if (condition_.Run())
288 quit_closure_.Run();
289 }
290