extension_crash_recovery_browsertest.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
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/extensions_service.h"
11#include "chrome/browser/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/ui/browser.h"
17#include "chrome/test/ui_test_utils.h"
18
19class ExtensionCrashRecoveryTest : public ExtensionBrowserTest {
20 protected:
21  ExtensionsService* GetExtensionsService() {
22    return browser()->profile()->GetExtensionsService();
23  }
24
25  ExtensionProcessManager* GetExtensionProcessManager() {
26    return browser()->profile()->GetExtensionProcessManager();
27  }
28
29  CrashedExtensionInfoBarDelegate* GetCrashedExtensionInfoBarDelegate(
30      int index) {
31    TabContents* current_tab = browser()->GetSelectedTabContents();
32    EXPECT_LT(index, current_tab->infobar_delegate_count());
33    InfoBarDelegate* delegate = current_tab->GetInfoBarDelegateAt(index);
34    return delegate->AsCrashedExtensionInfoBarDelegate();
35  }
36
37  void AcceptCrashedExtensionInfobar(int index) {
38    CrashedExtensionInfoBarDelegate* infobar =
39        GetCrashedExtensionInfoBarDelegate(index);
40    ASSERT_TRUE(infobar);
41    infobar->Accept();
42    WaitForExtensionLoad();
43  }
44
45  void CancelCrashedExtensionInfobar(int index) {
46    CrashedExtensionInfoBarDelegate* infobar =
47        GetCrashedExtensionInfoBarDelegate(index);
48    ASSERT_TRUE(infobar);
49    infobar->Cancel();
50  }
51
52  void CrashExtension(size_t index) {
53    ASSERT_LT(index, GetExtensionsService()->extensions()->size());
54    const Extension* extension =
55        GetExtensionsService()->extensions()->at(index);
56    ASSERT_TRUE(extension);
57    std::string extension_id(extension->id());
58    ExtensionHost* extension_host =
59        GetExtensionProcessManager()->GetBackgroundHostForExtension(extension);
60    ASSERT_TRUE(extension_host);
61
62    RenderProcessHost* extension_rph =
63        extension_host->render_view_host()->process();
64    base::KillProcess(extension_rph->GetHandle(),
65                      base::PROCESS_END_KILLED_BY_USER, false);
66    ASSERT_TRUE(WaitForExtensionCrash(extension_id));
67    ASSERT_FALSE(
68        GetExtensionProcessManager()->GetBackgroundHostForExtension(extension));
69  }
70
71  void CheckExtensionConsistency(size_t index) {
72    ASSERT_LT(index, GetExtensionsService()->extensions()->size());
73    const Extension* extension =
74        GetExtensionsService()->extensions()->at(index);
75    ASSERT_TRUE(extension);
76    ExtensionHost* extension_host =
77        GetExtensionProcessManager()->GetBackgroundHostForExtension(extension);
78    ASSERT_TRUE(extension_host);
79    ASSERT_TRUE(GetExtensionProcessManager()->HasExtensionHost(extension_host));
80    ASSERT_TRUE(extension_host->IsRenderViewLive());
81    ASSERT_EQ(extension_host->render_view_host()->process(),
82        GetExtensionProcessManager()->GetExtensionProcess(extension->id()));
83  }
84
85  void LoadTestExtension() {
86    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
87    const size_t size_before = GetExtensionsService()->extensions()->size();
88    ASSERT_TRUE(LoadExtension(
89        test_data_dir_.AppendASCII("common").AppendASCII("background_page")));
90    const Extension* extension = GetExtensionsService()->extensions()->back();
91    ASSERT_TRUE(extension);
92    first_extension_id_ = extension->id();
93    CheckExtensionConsistency(size_before);
94  }
95
96  void LoadSecondExtension() {
97    int offset = GetExtensionsService()->extensions()->size();
98    ASSERT_TRUE(LoadExtension(
99        test_data_dir_.AppendASCII("install").AppendASCII("install")));
100    const Extension* extension =
101        GetExtensionsService()->extensions()->at(offset);
102    ASSERT_TRUE(extension);
103    second_extension_id_ = extension->id();
104    CheckExtensionConsistency(offset);
105  }
106
107  std::string first_extension_id_;
108  std::string second_extension_id_;
109};
110
111IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, Basic) {
112  const size_t size_before = GetExtensionsService()->extensions()->size();
113  LoadTestExtension();
114  CrashExtension(size_before);
115  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
116  AcceptCrashedExtensionInfobar(0);
117
118  SCOPED_TRACE("after clicking the infobar");
119  CheckExtensionConsistency(size_before);
120}
121
122IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CloseAndReload) {
123  const size_t size_before = GetExtensionsService()->extensions()->size();
124  LoadTestExtension();
125  CrashExtension(size_before);
126  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
127  CancelCrashedExtensionInfobar(0);
128  ReloadExtension(first_extension_id_);
129
130  SCOPED_TRACE("after reloading");
131  CheckExtensionConsistency(size_before);
132}
133
134IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, ReloadIndependently) {
135  const size_t size_before = GetExtensionsService()->extensions()->size();
136  LoadTestExtension();
137  CrashExtension(size_before);
138  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
139
140  ReloadExtension(first_extension_id_);
141
142  SCOPED_TRACE("after reloading");
143  CheckExtensionConsistency(size_before);
144
145  TabContents* current_tab = browser()->GetSelectedTabContents();
146  ASSERT_TRUE(current_tab);
147
148  // The infobar should automatically hide after the extension is successfully
149  // reloaded.
150  ASSERT_EQ(0, current_tab->infobar_delegate_count());
151}
152
153// Make sure that when we don't do anything about the crashed extension
154// and close the browser, it doesn't crash. The browser is closed implicitly
155// at the end of each browser test.
156IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, ShutdownWhileCrashed) {
157  const size_t size_before = GetExtensionsService()->extensions()->size();
158  LoadTestExtension();
159  CrashExtension(size_before);
160  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
161}
162
163IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashFirst) {
164  const size_t size_before = GetExtensionsService()->extensions()->size();
165  LoadTestExtension();
166  LoadSecondExtension();
167  CrashExtension(size_before);
168  ASSERT_EQ(size_before + 1, GetExtensionsService()->extensions()->size());
169  AcceptCrashedExtensionInfobar(0);
170
171  SCOPED_TRACE("after clicking the infobar");
172  CheckExtensionConsistency(size_before);
173  CheckExtensionConsistency(size_before + 1);
174}
175
176IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashSecond) {
177  const size_t size_before = GetExtensionsService()->extensions()->size();
178  LoadTestExtension();
179  LoadSecondExtension();
180  CrashExtension(size_before + 1);
181  ASSERT_EQ(size_before + 1, GetExtensionsService()->extensions()->size());
182  AcceptCrashedExtensionInfobar(0);
183
184  SCOPED_TRACE("after clicking the infobar");
185  CheckExtensionConsistency(size_before);
186  CheckExtensionConsistency(size_before + 1);
187}
188
189IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
190                       TwoExtensionsCrashBothAtOnce) {
191  const size_t size_before = GetExtensionsService()->extensions()->size();
192  LoadTestExtension();
193  LoadSecondExtension();
194  CrashExtension(size_before);
195  ASSERT_EQ(size_before + 1, GetExtensionsService()->extensions()->size());
196  CrashExtension(size_before);
197  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
198
199  {
200    SCOPED_TRACE("first infobar");
201    AcceptCrashedExtensionInfobar(0);
202    CheckExtensionConsistency(size_before);
203  }
204
205  {
206    SCOPED_TRACE("second infobar");
207    AcceptCrashedExtensionInfobar(0);
208    CheckExtensionConsistency(size_before);
209    CheckExtensionConsistency(size_before + 1);
210  }
211}
212
213IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsOneByOne) {
214  const size_t size_before = GetExtensionsService()->extensions()->size();
215  LoadTestExtension();
216  CrashExtension(size_before);
217  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
218  LoadSecondExtension();
219  CrashExtension(size_before);
220  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
221
222  {
223    SCOPED_TRACE("first infobar");
224    AcceptCrashedExtensionInfobar(0);
225    CheckExtensionConsistency(size_before);
226  }
227
228  {
229    SCOPED_TRACE("second infobar");
230    AcceptCrashedExtensionInfobar(0);
231    CheckExtensionConsistency(size_before);
232    CheckExtensionConsistency(size_before + 1);
233  }
234}
235
236// Make sure that when we don't do anything about the crashed extensions
237// and close the browser, it doesn't crash. The browser is closed implicitly
238// at the end of each browser test.
239IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
240                       TwoExtensionsShutdownWhileCrashed) {
241  const size_t size_before = GetExtensionsService()->extensions()->size();
242  LoadTestExtension();
243  CrashExtension(size_before);
244  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
245  LoadSecondExtension();
246  CrashExtension(size_before);
247  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
248}
249
250IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
251                       TwoExtensionsIgnoreFirst) {
252  const size_t size_before = GetExtensionsService()->extensions()->size();
253  LoadTestExtension();
254  LoadSecondExtension();
255  CrashExtension(size_before);
256  ASSERT_EQ(size_before + 1, GetExtensionsService()->extensions()->size());
257  CrashExtension(size_before);
258  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
259
260  CancelCrashedExtensionInfobar(0);
261  AcceptCrashedExtensionInfobar(1);
262
263  SCOPED_TRACE("infobars done");
264  ASSERT_EQ(size_before + 1, GetExtensionsService()->extensions()->size());
265  CheckExtensionConsistency(size_before);
266}
267
268IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
269                       TwoExtensionsReloadIndependently) {
270  const size_t size_before = GetExtensionsService()->extensions()->size();
271  LoadTestExtension();
272  LoadSecondExtension();
273  CrashExtension(size_before);
274  ASSERT_EQ(size_before + 1, GetExtensionsService()->extensions()->size());
275  CrashExtension(size_before);
276  ASSERT_EQ(size_before, GetExtensionsService()->extensions()->size());
277
278  {
279    SCOPED_TRACE("first: reload");
280    TabContents* current_tab = browser()->GetSelectedTabContents();
281    ASSERT_TRUE(current_tab);
282    // At the beginning we should have one infobar displayed for each extension.
283    ASSERT_EQ(2, current_tab->infobar_delegate_count());
284    ReloadExtension(first_extension_id_);
285    // One of the infobars should hide after the extension is reloaded.
286    ASSERT_EQ(1, current_tab->infobar_delegate_count());
287    CheckExtensionConsistency(size_before);
288  }
289
290  {
291    SCOPED_TRACE("second: infobar");
292    AcceptCrashedExtensionInfobar(0);
293    CheckExtensionConsistency(size_before);
294    CheckExtensionConsistency(size_before + 1);
295  }
296}
297