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