blob: d99aee2effe62ce078d2f5d1775cb18e2fc8f4a0 [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)
Elly Fong-Jones4e3d25c72019-08-12 18:17:1540 : model_(model), desired_count_(0) {
41 model->AddObserver(this);
Devlin Cronin14025c62019-06-27 17:56:1442 }
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_;
Devlin Cronin14025c62019-06-27 17:56:1465
66 DISALLOW_COPY_AND_ASSIGN(TestTabStripModelObserver);
67};
68
69} // namespace
70
jambb11ed742017-05-01 17:27:5971class ExtensionUnloadBrowserTest : public ExtensionBrowserTest {
72 public:
73 void SetUpOnMainThread() override {
74 ExtensionBrowserTest::SetUpOnMainThread();
75 host_resolver()->AddRule("maps.google.com", "127.0.0.1");
76 }
77};
rdevlin.cronin235441342016-09-26 22:39:0178
79IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, TestUnload) {
80 // Load an extension that installs unload and beforeunload listeners.
81 const Extension* extension =
82 LoadExtension(test_data_dir_.AppendASCII("unload_listener"));
83 ASSERT_TRUE(extension);
84 std::string id = extension->id();
85 ASSERT_EQ(1, browser()->tab_strip_model()->count());
86 GURL initial_tab_url =
87 browser()->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL();
88 ui_test_utils::NavigateToURLWithDisposition(
89 browser(), extension->GetResourceURL("page.html"),
90 WindowOpenDisposition::NEW_FOREGROUND_TAB,
91 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
92 EXPECT_EQ(2, browser()->tab_strip_model()->count());
creiscaec94b62017-03-25 00:25:1093 DisableExtension(id);
rdevlin.cronin235441342016-09-26 22:39:0194 // There should only be one remaining web contents - the initial one.
95 ASSERT_EQ(1, browser()->tab_strip_model()->count());
96 EXPECT_EQ(
97 initial_tab_url,
98 browser()->tab_strip_model()->GetWebContentsAt(0)->GetLastCommittedURL());
99}
100
creiscaec94b62017-03-25 00:25:10101// After an extension is uninstalled, network requests from its content scripts
102// should fail but not kill the renderer process.
103IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, UnloadWithContentScripts) {
creiscaec94b62017-03-25 00:25:10104 ASSERT_TRUE(embedded_test_server()->Start());
105
106 // Load an extension with a content script that has a button to send XHRs.
107 const Extension* extension =
108 LoadExtension(test_data_dir_.AppendASCII("xhr_from_content_script"));
109 ASSERT_TRUE(extension);
110 std::string id = extension->id();
111 ASSERT_EQ(1, browser()->tab_strip_model()->count());
112 GURL test_url = embedded_test_server()->GetURL("/title1.html");
113 ui_test_utils::NavigateToURL(browser(), test_url);
114
Lukasz Anforowicz87d094592019-09-09 18:13:43115 // The content script sends an XHR with the webpage's (rather than
116 // extension's) Origin header - this should succeed (given that
117 // xhr.txt.mock-http-headers says `Access-Control-Allow-Origin: *`).
creiscaec94b62017-03-25 00:25:10118 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
Lukasz Anforowicz87d094592019-09-09 18:13:43133 // The content script sends an XHR with the webpage's (rather than
134 // extension's) Origin header - this should succeed (given that
135 // xhr.txt.mock-http-headers says `Access-Control-Allow-Origin: *`).
creiscaec94b62017-03-25 00:25:10136 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
137 browser()->tab_strip_model()->GetActiveWebContents(), kSendXhrScript,
138 &xhr_result));
Lukasz Anforowicz87d094592019-09-09 18:13:43139 EXPECT_TRUE(xhr_result);
creiscaec94b62017-03-25 00:25:10140
141 // Ensure the process has not been killed.
142 EXPECT_TRUE(browser()
143 ->tab_strip_model()
144 ->GetActiveWebContents()
145 ->GetMainFrame()
146 ->IsRenderFrameLive());
147}
148
Devlin Cronin14025c62019-06-27 17:56:14149// Tests that windows with opaque origins opened by the extension are closed
150// when the extension is unloaded. Regression test for https://crbug.com/894477.
151IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, OpenedOpaqueWindows) {
152 TestExtensionDir test_dir;
153 constexpr char kManifest[] =
154 R"({
155 "name": "Test",
156 "manifest_version": 2,
157 "version": "0.1",
158 "background": {
159 "scripts": ["background.js"]
160 }
161 })";
162 test_dir.WriteManifest(kManifest);
163 test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
164 "window.open('about:blank');");
165
166 const GURL about_blank(url::kAboutBlankURL);
167 content::TestNavigationObserver about_blank_observer(about_blank);
168 about_blank_observer.StartWatchingNewWebContents();
169 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
170 ASSERT_TRUE(extension);
171 about_blank_observer.WaitForNavigationFinished();
172
173 EXPECT_EQ(2, browser()->tab_strip_model()->count());
174 content::WebContents* web_contents =
175 browser()->tab_strip_model()->GetActiveWebContents();
176 EXPECT_EQ(about_blank, web_contents->GetLastCommittedURL());
177 url::Origin frame_origin =
178 web_contents->GetMainFrame()->GetLastCommittedOrigin();
179 url::SchemeHostPort precursor_tuple =
180 frame_origin.GetTupleOrPrecursorTupleIfOpaque();
181 EXPECT_EQ(kExtensionScheme, precursor_tuple.scheme());
182 EXPECT_EQ(extension->id(), precursor_tuple.host());
183
184 TestTabStripModelObserver test_tab_strip_model_observer(
185 browser()->tab_strip_model());
186 extension_service()->DisableExtension(extension->id(),
187 disable_reason::DISABLE_USER_ACTION);
188 test_tab_strip_model_observer.WaitForTabCount(1);
189
190 EXPECT_EQ(1, browser()->tab_strip_model()->count());
191}
192
Greg Thompsonce8736b2019-07-18 10:52:31193// Flaky timeouts on Win7 Tests (dbg)(1); see https://crbug.com/985255.
194#if defined(OS_WIN) && !defined(NDEBUG)
195#define MAYBE_CrashedTabs DISABLED_CrashedTabs
196#else
197#define MAYBE_CrashedTabs CrashedTabs
198#endif
199IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, MAYBE_CrashedTabs) {
Devlin Cronin14025c62019-06-27 17:56:14200 TestExtensionDir test_dir;
201 test_dir.WriteManifest(
202 R"({
203 "name": "test extension",
204 "manifest_version": 2,
205 "version": "0.1"
206 })");
207 test_dir.WriteFile(FILE_PATH_LITERAL("page.html"),
208 "<!doctype html><html><body>Hello world</body></html>");
209 scoped_refptr<const Extension> extension(
210 LoadExtension(test_dir.UnpackedPath()));
211 ASSERT_TRUE(extension);
212 const GURL page_url = extension->GetResourceURL("page.html");
213 ui_test_utils::NavigateToURLWithDisposition(
214 browser(), page_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
215 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
216
217 EXPECT_EQ(2, browser()->tab_strip_model()->count());
218
219 content::WebContents* active_tab =
220 browser()->tab_strip_model()->GetActiveWebContents();
221 EXPECT_EQ(page_url, active_tab->GetLastCommittedURL());
222
223 {
224 content::ScopedAllowRendererCrashes allow_renderer_crashes(
225 active_tab->GetMainFrame()->GetProcess());
226 ui_test_utils::NavigateToURLWithDisposition(
227 browser(), GURL("chrome://crash"), WindowOpenDisposition::CURRENT_TAB,
228 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
229 }
230
231 // There should still be two open tabs, but the active one is crashed.
232 EXPECT_EQ(2, browser()->tab_strip_model()->count());
233 EXPECT_TRUE(active_tab->IsCrashed());
234
235 // Even though the tab is crashed, it should still have the last committed
236 // URL of the extension page.
237 EXPECT_EQ(page_url, active_tab->GetLastCommittedURL());
238
239 // Unloading the extension should close the crashed tab, since its origin was
240 // still the extension's origin.
241 TestTabStripModelObserver test_tab_strip_model_observer(
242 browser()->tab_strip_model());
243 extension_service()->DisableExtension(extension->id(),
244 disable_reason::DISABLE_USER_ACTION);
245 test_tab_strip_model_observer.WaitForTabCount(1);
246
247 EXPECT_EQ(1, browser()->tab_strip_model()->count());
248 EXPECT_NE(extension->url().GetOrigin(), browser()
249 ->tab_strip_model()
250 ->GetActiveWebContents()
251 ->GetLastCommittedURL()
252 .GetOrigin());
253}
254
rdevlin.cronin235441342016-09-26 22:39:01255// TODO(devlin): Investigate what to do for embedded iframes.
256
257} // namespace extensions