• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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