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