blob: 0de3b15240992e69b3438730242597a4441f560b [file] [log] [blame]
rdevlin.cronin235441342016-09-26 22:39:011// Copyright 2016 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
Jun Cai53c54b222018-08-16 00:14:045#include "base/feature_list.h"
Devlin Cronin14025c62019-06-27 17:56:146#include "base/run_loop.h"
7#include "base/scoped_observer.h"
Greg Thompsonce8736b2019-07-18 10:52:318#include "build/build_config.h"
rdevlin.cronin235441342016-09-26 22:39:019#include "chrome/browser/extensions/extension_browsertest.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/ui/tabs/tab_strip_model.h"
Devlin Cronin14025c62019-06-27 17:56:1412#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
rdevlin.cronin235441342016-09-26 22:39:0113#include "chrome/test/base/ui_test_utils.h"
creiscaec94b62017-03-25 00:25:1014#include "content/public/browser/render_frame_host.h"
Devlin Cronin14025c62019-06-27 17:56:1415#include "content/public/browser/web_contents.h"
creiscaec94b62017-03-25 00:25:1016#include "content/public/test/browser_test_utils.h"
Devlin Cronin14025c62019-06-27 17:56:1417#include "content/public/test/no_renderer_crashes_assertion.h"
18#include "content/public/test/test_navigation_observer.h"
19#include "content/public/test/test_utils.h"
20#include "extensions/browser/disable_reason.h"
21#include "extensions/common/constants.h"
22#include "extensions/common/extension.h"
23#include "extensions/test/test_extension_dir.h"
creiscaec94b62017-03-25 00:25:1024#include "net/dns/mock_host_resolver.h"
25#include "net/test/embedded_test_server/embedded_test_server.h"
Jun Cai53c54b222018-08-16 00:14:0426#include "services/network/public/cpp/features.h"
rdevlin.cronin235441342016-09-26 22:39:0127#include "ui/base/window_open_disposition.h"
Devlin Cronin14025c62019-06-27 17:56:1428#include "url/origin.h"
29#include "url/url_constants.h"
rdevlin.cronin235441342016-09-26 22:39:0130
31namespace extensions {
32
Devlin Cronin14025c62019-06-27 17:56:1433namespace {
34
35// A helper class to wait for a particular tab count. Requires the tab strip
36// to outlive this object.
37class TestTabStripModelObserver : public TabStripModelObserver {
38 public:
39 explicit TestTabStripModelObserver(TabStripModel* model)
40 : model_(model), desired_count_(0), scoped_observer_(this) {
41 scoped_observer_.Add(model);
42 }
43 ~TestTabStripModelObserver() override = default;
44
45 void WaitForTabCount(int count) {
46 if (model_->count() == count)
47 return;
48 desired_count_ = count;
49 run_loop_.Run();
50 }
51
52 private:
53 // TabStripModelObserver:
54 void OnTabStripModelChanged(
55 TabStripModel* tab_strip_model,
56 const TabStripModelChange& change,
57 const TabStripSelectionChange& selection) override {
58 if (model_->count() == desired_count_)
59 run_loop_.Quit();
60 }
61
62 TabStripModel* model_;
63 int desired_count_;
64 base::RunLoop run_loop_;
65 ScopedObserver<TabStripModel, TabStripModelObserver> scoped_observer_;
66
67 DISALLOW_COPY_AND_ASSIGN(TestTabStripModelObserver);
68};
69
70} // namespace
71
jambb11ed742017-05-01 17:27:5972class ExtensionUnloadBrowserTest : public ExtensionBrowserTest {
73 public:
74 void SetUpOnMainThread() override {
75 ExtensionBrowserTest::SetUpOnMainThread();
76 host_resolver()->AddRule("maps.google.com", "127.0.0.1");
77 }
78};
rdevlin.cronin235441342016-09-26 22:39:0179
80IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, TestUnload) {
81 // Load an extension that installs unload and beforeunload listeners.
82 const Extension* extension =
83 LoadExtension(test_data_dir_.AppendASCII("unload_listener"));
84 ASSERT_TRUE(extension);
85 std::string id = extension->id();
86 ASSERT_EQ(1, browser()->tab_strip_model()->count());
87 GURL initial_tab_url =
88 browser()->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL();
89 ui_test_utils::NavigateToURLWithDisposition(
90 browser(), extension->GetResourceURL("page.html"),
91 WindowOpenDisposition::NEW_FOREGROUND_TAB,
92 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
93 EXPECT_EQ(2, browser()->tab_strip_model()->count());
creiscaec94b62017-03-25 00:25:1094 DisableExtension(id);
rdevlin.cronin235441342016-09-26 22:39:0195 // There should only be one remaining web contents - the initial one.
96 ASSERT_EQ(1, browser()->tab_strip_model()->count());
97 EXPECT_EQ(
98 initial_tab_url,
99 browser()->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL());
100}
101
creiscaec94b62017-03-25 00:25:10102// After an extension is uninstalled, network requests from its content scripts
103// should fail but not kill the renderer process.
104IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, UnloadWithContentScripts) {
creiscaec94b62017-03-25 00:25:10105 ASSERT_TRUE(embedded_test_server()->Start());
106
107 // Load an extension with a content script that has a button to send XHRs.
108 const Extension* extension =
109 LoadExtension(test_data_dir_.AppendASCII("xhr_from_content_script"));
110 ASSERT_TRUE(extension);
111 std::string id = extension->id();
112 ASSERT_EQ(1, browser()->tab_strip_model()->count());
113 GURL test_url = embedded_test_server()->GetURL("/title1.html");
114 ui_test_utils::NavigateToURL(browser(), test_url);
115
116 // Sending an XHR with the extension's Origin header should succeed when the
117 // extension is installed.
118 const char kSendXhrScript[] = "document.getElementById('xhrButton').click();";
119 bool xhr_result = false;
120 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
121 browser()->tab_strip_model()->GetActiveWebContents(), kSendXhrScript,
122 &xhr_result));
123 EXPECT_TRUE(xhr_result);
124
125 DisableExtension(id);
126
127 // The tab should still be open with the content script injected.
128 ASSERT_EQ(1, browser()->tab_strip_model()->count());
129 EXPECT_EQ(
130 test_url,
131 browser()->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL());
132
133 // Sending an XHR with the extension's Origin header should fail but not kill
134 // the tab.
135 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
136 browser()->tab_strip_model()->GetActiveWebContents(), kSendXhrScript,
137 &xhr_result));
138 EXPECT_FALSE(xhr_result);
139
140 // Ensure the process has not been killed.
141 EXPECT_TRUE(browser()
142 ->tab_strip_model()
143 ->GetActiveWebContents()
144 ->GetMainFrame()
145 ->IsRenderFrameLive());
146}
147
Devlin Cronin14025c62019-06-27 17:56:14148// Tests that windows with opaque origins opened by the extension are closed
149// when the extension is unloaded. Regression test for https://crbug.com/894477.
150IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, OpenedOpaqueWindows) {
151 TestExtensionDir test_dir;
152 constexpr char kManifest[] =
153 R"({
154 "name": "Test",
155 "manifest_version": 2,
156 "version": "0.1",
157 "background": {
158 "scripts": ["background.js"]
159 }
160 })";
161 test_dir.WriteManifest(kManifest);
162 test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
163 "window.open('about:blank');");
164
165 const GURL about_blank(url::kAboutBlankURL);
166 content::TestNavigationObserver about_blank_observer(about_blank);
167 about_blank_observer.StartWatchingNewWebContents();
168 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
169 ASSERT_TRUE(extension);
170 about_blank_observer.WaitForNavigationFinished();
171
172 EXPECT_EQ(2, browser()->tab_strip_model()->count());
173 content::WebContents* web_contents =
174 browser()->tab_strip_model()->GetActiveWebContents();
175 EXPECT_EQ(about_blank, web_contents->GetLastCommittedURL());
176 url::Origin frame_origin =
177 web_contents->GetMainFrame()->GetLastCommittedOrigin();
178 url::SchemeHostPort precursor_tuple =
179 frame_origin.GetTupleOrPrecursorTupleIfOpaque();
180 EXPECT_EQ(kExtensionScheme, precursor_tuple.scheme());
181 EXPECT_EQ(extension->id(), precursor_tuple.host());
182
183 TestTabStripModelObserver test_tab_strip_model_observer(
184 browser()->tab_strip_model());
185 extension_service()->DisableExtension(extension->id(),
186 disable_reason::DISABLE_USER_ACTION);
187 test_tab_strip_model_observer.WaitForTabCount(1);
188
189 EXPECT_EQ(1, browser()->tab_strip_model()->count());
190}
191
Greg Thompsonce8736b2019-07-18 10:52:31192// Flaky timeouts on Win7 Tests (dbg)(1); see https://crbug.com/985255.
193#if defined(OS_WIN) && !defined(NDEBUG)
194#define MAYBE_CrashedTabs DISABLED_CrashedTabs
195#else
196#define MAYBE_CrashedTabs CrashedTabs
197#endif
198IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, MAYBE_CrashedTabs) {
Devlin Cronin14025c62019-06-27 17:56:14199 TestExtensionDir test_dir;
200 test_dir.WriteManifest(
201 R"({
202 "name": "test extension",
203 "manifest_version": 2,
204 "version": "0.1"
205 })");
206 test_dir.WriteFile(FILE_PATH_LITERAL("page.html"),
207 "<!doctype html><html><body>Hello world</body></html>");
208 scoped_refptr<const Extension> extension(
209 LoadExtension(test_dir.UnpackedPath()));
210 ASSERT_TRUE(extension);
211 const GURL page_url = extension->GetResourceURL("page.html");
212 ui_test_utils::NavigateToURLWithDisposition(
213 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
214 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
215
216 EXPECT_EQ(2, browser()->tab_strip_model()->count());
217
218 content::WebContents* active_tab =
219 browser()->tab_strip_model()->GetActiveWebContents();
220 EXPECT_EQ(page_url, active_tab->GetLastCommittedURL());
221
222 {
223 content::ScopedAllowRendererCrashes allow_renderer_crashes(
224 active_tab->GetMainFrame()->GetProcess());
225 ui_test_utils::NavigateToURLWithDisposition(
226 browser(), GURL("chrome://crash"), WindowOpenDisposition::CURRENT_TAB,
227 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
228 }
229
230 // There should still be two open tabs, but the active one is crashed.
231 EXPECT_EQ(2, browser()->tab_strip_model()->count());
232 EXPECT_TRUE(active_tab->IsCrashed());
233
234 // Even though the tab is crashed, it should still have the last committed
235 // URL of the extension page.
236 EXPECT_EQ(page_url, active_tab->GetLastCommittedURL());
237
238 // Unloading the extension should close the crashed tab, since its origin was
239 // still the extension's origin.
240 TestTabStripModelObserver test_tab_strip_model_observer(
241 browser()->tab_strip_model());
242 extension_service()->DisableExtension(extension->id(),
243 disable_reason::DISABLE_USER_ACTION);
244 test_tab_strip_model_observer.WaitForTabCount(1);
245
246 EXPECT_EQ(1, browser()->tab_strip_model()->count());
247 EXPECT_NE(extension->url().GetOrigin(), browser()
248 ->tab_strip_model()
249 ->GetActiveWebContents()
250 ->GetLastCommittedURL()
251 .GetOrigin());
252}
253
rdevlin.cronin235441342016-09-26 22:39:01254// TODO(devlin): Investigate what to do for embedded iframes.
255
256} // namespace extensions