1 // Copyright (c) 2011 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 "base/process_util.h"
6 #include "chrome/browser/browser_process.h"
7 #include "chrome/browser/extensions/extension_browsertest.h"
8 #include "chrome/browser/extensions/extension_host.h"
9 #include "chrome/browser/extensions/extension_process_manager.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/notifications/balloon_host.h"
12 #include "chrome/browser/notifications/notification.h"
13 #include "chrome/browser/notifications/notification_delegate.h"
14 #include "chrome/browser/notifications/notification_ui_manager.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/tabs/tab_strip_model.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/test/ui_test_utils.h"
19 #include "content/browser/renderer_host/render_process_host.h"
20 #include "content/browser/renderer_host/render_view_host.h"
21 #include "content/browser/tab_contents/tab_contents.h"
22 #include "content/common/result_codes.h"
23
24 class ExtensionCrashRecoveryTest : public ExtensionBrowserTest {
25 protected:
GetExtensionService()26 ExtensionService* GetExtensionService() {
27 return browser()->profile()->GetExtensionService();
28 }
29
GetExtensionProcessManager()30 ExtensionProcessManager* GetExtensionProcessManager() {
31 return browser()->profile()->GetExtensionProcessManager();
32 }
33
GetNotificationDelegate(size_t index)34 Balloon* GetNotificationDelegate(size_t index) {
35 NotificationUIManager* manager =
36 g_browser_process->notification_ui_manager();
37 BalloonCollection::Balloons balloons =
38 manager->balloon_collection()->GetActiveBalloons();
39 return balloons.at(index);
40 }
41
AcceptNotification(size_t index)42 void AcceptNotification(size_t index) {
43 Balloon* balloon = GetNotificationDelegate(index);
44 ASSERT_TRUE(balloon);
45 balloon->OnClick();
46 WaitForExtensionLoad();
47 }
48
CancelNotification(size_t index)49 void CancelNotification(size_t index) {
50 Balloon* balloon = GetNotificationDelegate(index);
51 NotificationUIManager* manager =
52 g_browser_process->notification_ui_manager();
53 manager->CancelById(balloon->notification().notification_id());
54 }
55
CountBalloons()56 size_t CountBalloons() {
57 NotificationUIManager* manager =
58 g_browser_process->notification_ui_manager();
59 BalloonCollection::Balloons balloons =
60 manager->balloon_collection()->GetActiveBalloons();
61 return balloons.size();
62 }
63
CrashExtension(size_t index)64 void CrashExtension(size_t index) {
65 ASSERT_LT(index, GetExtensionService()->extensions()->size());
66 const Extension* extension =
67 GetExtensionService()->extensions()->at(index);
68 ASSERT_TRUE(extension);
69 std::string extension_id(extension->id());
70 ExtensionHost* extension_host =
71 GetExtensionProcessManager()->GetBackgroundHostForExtension(extension);
72 ASSERT_TRUE(extension_host);
73
74 RenderProcessHost* extension_rph =
75 extension_host->render_view_host()->process();
76 base::KillProcess(extension_rph->GetHandle(), ResultCodes::KILLED, false);
77 ASSERT_TRUE(WaitForExtensionCrash(extension_id));
78 ASSERT_FALSE(
79 GetExtensionProcessManager()->GetBackgroundHostForExtension(extension));
80 }
81
CheckExtensionConsistency(size_t index)82 void CheckExtensionConsistency(size_t index) {
83 ASSERT_LT(index, GetExtensionService()->extensions()->size());
84 const Extension* extension =
85 GetExtensionService()->extensions()->at(index);
86 ASSERT_TRUE(extension);
87 ExtensionHost* extension_host =
88 GetExtensionProcessManager()->GetBackgroundHostForExtension(extension);
89 ASSERT_TRUE(extension_host);
90 ASSERT_TRUE(GetExtensionProcessManager()->HasExtensionHost(extension_host));
91 ASSERT_TRUE(extension_host->IsRenderViewLive());
92 ASSERT_EQ(extension_host->render_view_host()->process(),
93 GetExtensionProcessManager()->GetExtensionProcess(extension->id()));
94 }
95
LoadTestExtension()96 void LoadTestExtension() {
97 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
98 const size_t size_before = GetExtensionService()->extensions()->size();
99 ASSERT_TRUE(LoadExtension(
100 test_data_dir_.AppendASCII("common").AppendASCII("background_page")));
101 const Extension* extension = GetExtensionService()->extensions()->back();
102 ASSERT_TRUE(extension);
103 first_extension_id_ = extension->id();
104 CheckExtensionConsistency(size_before);
105 }
106
LoadSecondExtension()107 void LoadSecondExtension() {
108 int offset = GetExtensionService()->extensions()->size();
109 ASSERT_TRUE(LoadExtension(
110 test_data_dir_.AppendASCII("install").AppendASCII("install")));
111 const Extension* extension =
112 GetExtensionService()->extensions()->at(offset);
113 ASSERT_TRUE(extension);
114 second_extension_id_ = extension->id();
115 CheckExtensionConsistency(offset);
116 }
117
118 std::string first_extension_id_;
119 std::string second_extension_id_;
120 };
121
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,Basic)122 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, Basic) {
123 const size_t size_before = GetExtensionService()->extensions()->size();
124 const size_t crash_size_before =
125 GetExtensionService()->terminated_extensions()->size();
126 LoadTestExtension();
127 CrashExtension(size_before);
128 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
129 ASSERT_EQ(crash_size_before + 1,
130 GetExtensionService()->terminated_extensions()->size());
131 AcceptNotification(0);
132
133 SCOPED_TRACE("after clicking the balloon");
134 CheckExtensionConsistency(size_before);
135 ASSERT_EQ(crash_size_before,
136 GetExtensionService()->terminated_extensions()->size());
137 }
138
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,CloseAndReload)139 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CloseAndReload) {
140 const size_t size_before = GetExtensionService()->extensions()->size();
141 const size_t crash_size_before =
142 GetExtensionService()->terminated_extensions()->size();
143 LoadTestExtension();
144 CrashExtension(size_before);
145
146 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
147 ASSERT_EQ(crash_size_before + 1,
148 GetExtensionService()->terminated_extensions()->size());
149
150 CancelNotification(0);
151 ReloadExtension(first_extension_id_);
152
153 SCOPED_TRACE("after reloading");
154 CheckExtensionConsistency(size_before);
155 ASSERT_EQ(crash_size_before,
156 GetExtensionService()->terminated_extensions()->size());
157 }
158
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,ReloadIndependently)159 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, ReloadIndependently) {
160 const size_t size_before = GetExtensionService()->extensions()->size();
161 LoadTestExtension();
162 CrashExtension(size_before);
163 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
164
165 ReloadExtension(first_extension_id_);
166
167 SCOPED_TRACE("after reloading");
168 CheckExtensionConsistency(size_before);
169
170 TabContents* current_tab = browser()->GetSelectedTabContents();
171 ASSERT_TRUE(current_tab);
172
173 // The balloon should automatically hide after the extension is successfully
174 // reloaded.
175 ASSERT_EQ(0U, CountBalloons());
176 }
177
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,ReloadIndependentlyChangeTabs)178 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
179 ReloadIndependentlyChangeTabs) {
180 const size_t size_before = GetExtensionService()->extensions()->size();
181 LoadTestExtension();
182 CrashExtension(size_before);
183 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
184
185 TabContents* original_tab = browser()->GetSelectedTabContents();
186 ASSERT_TRUE(original_tab);
187 ASSERT_EQ(1U, CountBalloons());
188
189 // Open a new tab, but the balloon will still be there.
190 browser()->NewTab();
191 TabContents* new_current_tab = browser()->GetSelectedTabContents();
192 ASSERT_TRUE(new_current_tab);
193 ASSERT_NE(new_current_tab, original_tab);
194 ASSERT_EQ(1U, CountBalloons());
195
196 ReloadExtension(first_extension_id_);
197
198 SCOPED_TRACE("after reloading");
199 CheckExtensionConsistency(size_before);
200
201 // The balloon should automatically hide after the extension is successfully
202 // reloaded.
203 ASSERT_EQ(0U, CountBalloons());
204 }
205
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,ReloadIndependentlyNavigatePage)206 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
207 ReloadIndependentlyNavigatePage) {
208 const size_t size_before = GetExtensionService()->extensions()->size();
209 LoadTestExtension();
210 CrashExtension(size_before);
211 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
212
213 TabContents* current_tab = browser()->GetSelectedTabContents();
214 ASSERT_TRUE(current_tab);
215 ASSERT_EQ(1U, CountBalloons());
216
217 // Navigate to another page.
218 ui_test_utils::NavigateToURL(browser(),
219 ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
220 FilePath(FILE_PATH_LITERAL("title1.html"))));
221 ASSERT_EQ(1U, CountBalloons());
222
223 ReloadExtension(first_extension_id_);
224
225 SCOPED_TRACE("after reloading");
226 CheckExtensionConsistency(size_before);
227
228 // The infobar should automatically hide after the extension is successfully
229 // reloaded.
230 ASSERT_EQ(0U, CountBalloons());
231 }
232
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,ReloadIndependentlyTwoInfoBars)233 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
234 ReloadIndependentlyTwoInfoBars) {
235 const size_t size_before = GetExtensionService()->extensions()->size();
236 LoadTestExtension();
237
238 // Open a new window so that there will be an info bar in each.
239 Browser* browser2 = CreateBrowser(browser()->profile());
240
241 CrashExtension(size_before);
242 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
243
244 TabContents* current_tab = browser()->GetSelectedTabContents();
245 ASSERT_TRUE(current_tab);
246 ASSERT_EQ(1U, CountBalloons());
247
248 TabContents* current_tab2 = browser2->GetSelectedTabContents();
249 ASSERT_TRUE(current_tab2);
250 ASSERT_EQ(1U, CountBalloons());
251
252 ReloadExtension(first_extension_id_);
253
254 SCOPED_TRACE("after reloading");
255 CheckExtensionConsistency(size_before);
256
257 // Both infobars should automatically hide after the extension is successfully
258 // reloaded.
259 ASSERT_EQ(0U, CountBalloons());
260 ASSERT_EQ(0U, CountBalloons());
261 }
262
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,ReloadIndependentlyTwoInfoBarsSameBrowser)263 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
264 ReloadIndependentlyTwoInfoBarsSameBrowser) {
265 const size_t size_before = GetExtensionService()->extensions()->size();
266 LoadTestExtension();
267
268 // Open a new window so that there will be an info bar in each.
269 Browser* browser2 = CreateBrowser(browser()->profile());
270
271 CrashExtension(size_before);
272 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
273
274 TabContents* current_tab = browser()->GetSelectedTabContents();
275 ASSERT_TRUE(current_tab);
276 ASSERT_EQ(1U, CountBalloons());
277
278 TabContents* current_tab2 = browser2->GetSelectedTabContents();
279 ASSERT_TRUE(current_tab2);
280 ASSERT_EQ(1U, CountBalloons());
281
282 // Move second window into first browser so there will be multiple tabs
283 // with the info bar for the same extension in one browser.
284 TabContentsWrapper* contents =
285 browser2->tabstrip_model()->DetachTabContentsAt(0);
286 browser()->tabstrip_model()->AppendTabContents(contents, true);
287 current_tab2 = browser()->GetSelectedTabContents();
288 ASSERT_EQ(1U, CountBalloons());
289 ASSERT_NE(current_tab2, current_tab);
290
291 ReloadExtension(first_extension_id_);
292
293 SCOPED_TRACE("after reloading");
294 CheckExtensionConsistency(size_before);
295
296 // Both infobars should automatically hide after the extension is successfully
297 // reloaded.
298 ASSERT_EQ(0U, CountBalloons());
299 browser()->SelectPreviousTab();
300 ASSERT_EQ(current_tab, browser()->GetSelectedTabContents());
301 ASSERT_EQ(0U, CountBalloons());
302 }
303
304 // Make sure that when we don't do anything about the crashed extension
305 // and close the browser, it doesn't crash. The browser is closed implicitly
306 // at the end of each browser test.
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,ShutdownWhileCrashed)307 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, ShutdownWhileCrashed) {
308 const size_t size_before = GetExtensionService()->extensions()->size();
309 LoadTestExtension();
310 CrashExtension(size_before);
311 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
312 }
313
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,TwoExtensionsCrashFirst)314 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashFirst) {
315 const size_t size_before = GetExtensionService()->extensions()->size();
316 LoadTestExtension();
317 LoadSecondExtension();
318 CrashExtension(size_before);
319 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
320 AcceptNotification(0);
321
322 SCOPED_TRACE("after clicking the balloon");
323 CheckExtensionConsistency(size_before);
324 CheckExtensionConsistency(size_before + 1);
325 }
326
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,TwoExtensionsCrashSecond)327 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashSecond) {
328 const size_t size_before = GetExtensionService()->extensions()->size();
329 LoadTestExtension();
330 LoadSecondExtension();
331 CrashExtension(size_before + 1);
332 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
333 AcceptNotification(0);
334
335 SCOPED_TRACE("after clicking the balloon");
336 CheckExtensionConsistency(size_before);
337 CheckExtensionConsistency(size_before + 1);
338 }
339
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,TwoExtensionsCrashBothAtOnce)340 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
341 TwoExtensionsCrashBothAtOnce) {
342 const size_t size_before = GetExtensionService()->extensions()->size();
343 const size_t crash_size_before =
344 GetExtensionService()->terminated_extensions()->size();
345 LoadTestExtension();
346 LoadSecondExtension();
347 CrashExtension(size_before);
348 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
349 ASSERT_EQ(crash_size_before + 1,
350 GetExtensionService()->terminated_extensions()->size());
351 CrashExtension(size_before);
352 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
353 ASSERT_EQ(crash_size_before + 2,
354 GetExtensionService()->terminated_extensions()->size());
355
356 {
357 SCOPED_TRACE("first balloon");
358 AcceptNotification(0);
359 CheckExtensionConsistency(size_before);
360 }
361
362 {
363 SCOPED_TRACE("second balloon");
364 AcceptNotification(0);
365 CheckExtensionConsistency(size_before);
366 CheckExtensionConsistency(size_before + 1);
367 }
368 }
369
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,TwoExtensionsOneByOne)370 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsOneByOne) {
371 const size_t size_before = GetExtensionService()->extensions()->size();
372 LoadTestExtension();
373 CrashExtension(size_before);
374 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
375 LoadSecondExtension();
376 CrashExtension(size_before);
377 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
378
379 {
380 SCOPED_TRACE("first balloon");
381 AcceptNotification(0);
382 CheckExtensionConsistency(size_before);
383 }
384
385 {
386 SCOPED_TRACE("second balloon");
387 AcceptNotification(0);
388 CheckExtensionConsistency(size_before);
389 CheckExtensionConsistency(size_before + 1);
390 }
391 }
392
393 // Make sure that when we don't do anything about the crashed extensions
394 // and close the browser, it doesn't crash. The browser is closed implicitly
395 // at the end of each browser test.
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,TwoExtensionsShutdownWhileCrashed)396 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
397 TwoExtensionsShutdownWhileCrashed) {
398 const size_t size_before = GetExtensionService()->extensions()->size();
399 LoadTestExtension();
400 CrashExtension(size_before);
401 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
402 LoadSecondExtension();
403 CrashExtension(size_before);
404 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
405 }
406
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,TwoExtensionsIgnoreFirst)407 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsIgnoreFirst) {
408 const size_t size_before = GetExtensionService()->extensions()->size();
409 LoadTestExtension();
410 LoadSecondExtension();
411 CrashExtension(size_before);
412 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
413 CrashExtension(size_before);
414 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
415
416 CancelNotification(0);
417 // Cancelling the balloon at 0 will close the balloon, and the balloon in
418 // index 1 will move into index 0.
419 AcceptNotification(0);
420
421 SCOPED_TRACE("balloons done");
422 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
423 CheckExtensionConsistency(size_before);
424 }
425
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,TwoExtensionsReloadIndependently)426 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
427 TwoExtensionsReloadIndependently) {
428 const size_t size_before = GetExtensionService()->extensions()->size();
429 LoadTestExtension();
430 LoadSecondExtension();
431 CrashExtension(size_before);
432 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
433 CrashExtension(size_before);
434 ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
435
436 {
437 SCOPED_TRACE("first: reload");
438 TabContents* current_tab = browser()->GetSelectedTabContents();
439 ASSERT_TRUE(current_tab);
440 // At the beginning we should have one infobar displayed for each extension.
441 ASSERT_EQ(2U, CountBalloons());
442 ReloadExtension(first_extension_id_);
443 // One of the infobars should hide after the extension is reloaded.
444 ASSERT_EQ(1U, CountBalloons());
445 CheckExtensionConsistency(size_before);
446 }
447
448 {
449 SCOPED_TRACE("second: balloon");
450 AcceptNotification(0);
451 CheckExtensionConsistency(size_before);
452 CheckExtensionConsistency(size_before + 1);
453 }
454 }
455
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,CrashAndUninstall)456 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CrashAndUninstall) {
457 const size_t size_before = GetExtensionService()->extensions()->size();
458 const size_t crash_size_before =
459 GetExtensionService()->terminated_extensions()->size();
460 LoadTestExtension();
461 LoadSecondExtension();
462 CrashExtension(size_before);
463 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
464 ASSERT_EQ(crash_size_before + 1,
465 GetExtensionService()->terminated_extensions()->size());
466
467 ASSERT_EQ(1U, CountBalloons());
468 UninstallExtension(first_extension_id_);
469 MessageLoop::current()->RunAllPending();
470
471 SCOPED_TRACE("after uninstalling");
472 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
473 ASSERT_EQ(crash_size_before,
474 GetExtensionService()->terminated_extensions()->size());
475 ASSERT_EQ(0U, CountBalloons());
476 }
477
IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,CrashAndUnloadAll)478 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CrashAndUnloadAll) {
479 const size_t size_before = GetExtensionService()->extensions()->size();
480 const size_t crash_size_before =
481 GetExtensionService()->terminated_extensions()->size();
482 LoadTestExtension();
483 LoadSecondExtension();
484 CrashExtension(size_before);
485 ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
486 ASSERT_EQ(crash_size_before + 1,
487 GetExtensionService()->terminated_extensions()->size());
488
489 GetExtensionService()->UnloadAllExtensions();
490 ASSERT_EQ(crash_size_before,
491 GetExtensionService()->terminated_extensions()->size());
492 }
493