1 // Copyright 2013 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 "chrome/browser/extensions/browser_action_test_util.h"
6 #include "chrome/browser/extensions/extension_action.h"
7 #include "chrome/browser/extensions/extension_action_manager.h"
8 #include "chrome/browser/extensions/extension_apitest.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_tab_util.h"
11 #include "chrome/browser/extensions/extension_test_message_listener.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_commands.h"
14 #include "chrome/browser/ui/browser_finder.h"
15 #include "chrome/browser/ui/browser_list.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/test/base/interactive_test_utils.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "content/public/browser/notification_service.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/common/permissions/permissions_data.h"
23
24 #if defined(OS_WIN)
25 #include "ui/views/win/hwnd_util.h"
26 #endif
27
28 namespace extensions {
29 namespace {
30
31 // chrome.browserAction API tests that interact with the UI in such a way that
32 // they cannot be run concurrently (i.e. openPopup API tests that require the
33 // window be focused/active).
34 class BrowserActionInteractiveTest : public ExtensionApiTest {
35 public:
BrowserActionInteractiveTest()36 BrowserActionInteractiveTest() {}
~BrowserActionInteractiveTest()37 virtual ~BrowserActionInteractiveTest() {}
38
39 protected:
40 // Function to control whether to run popup tests for the current platform.
41 // These tests require RunExtensionSubtest to work as expected and the browser
42 // window to able to be made active automatically. Returns false for platforms
43 // where these conditions are not met.
ShouldRunPopupTest()44 bool ShouldRunPopupTest() {
45 // TODO(justinlin): http://crbug.com/177163
46 #if defined(OS_WIN) && !defined(NDEBUG)
47 return false;
48 #elif defined(OS_MACOSX)
49 // TODO(justinlin): Browser window do not become active on Mac even when
50 // Activate() is called on them. Enable when/if it's possible to fix.
51 return false;
52 #else
53 return true;
54 #endif
55 }
56
57 // Open an extension popup via the chrome.browserAction.openPopup API.
OpenExtensionPopupViaAPI()58 void OpenExtensionPopupViaAPI() {
59 // Setup the notification observer to wait for the popup to finish loading.
60 content::WindowedNotificationObserver frame_observer(
61 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
62 content::NotificationService::AllSources());
63 // Show first popup in first window and expect it to have loaded.
64 ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup",
65 "open_popup_succeeds.html")) << message_;
66 frame_observer.Wait();
67 EXPECT_TRUE(BrowserActionTestUtil(browser()).HasPopup());
68 }
69 };
70
71 // Tests opening a popup using the chrome.browserAction.openPopup API. This test
72 // opens a popup in the starting window, closes the popup, creates a new window
73 // and opens a popup in the new window. Both popups should succeed in opening.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,TestOpenPopup)74 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, TestOpenPopup) {
75 if (!ShouldRunPopupTest())
76 return;
77
78 BrowserActionTestUtil browserActionBar = BrowserActionTestUtil(browser());
79 // Setup extension message listener to wait for javascript to finish running.
80 ExtensionTestMessageListener listener("ready", true);
81 {
82 OpenExtensionPopupViaAPI();
83 EXPECT_TRUE(browserActionBar.HasPopup());
84 browserActionBar.HidePopup();
85 }
86
87 EXPECT_TRUE(listener.WaitUntilSatisfied());
88 Browser* new_browser = NULL;
89 {
90 content::WindowedNotificationObserver frame_observer(
91 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
92 content::NotificationService::AllSources());
93 // Open a new window.
94 new_browser = chrome::FindBrowserWithWebContents(
95 browser()->OpenURL(content::OpenURLParams(
96 GURL("about:"), content::Referrer(), NEW_WINDOW,
97 content::PAGE_TRANSITION_TYPED, false)));
98 #if defined(OS_WIN)
99 // Hide all the buttons to test that it opens even when browser action is
100 // in the overflow bucket.
101 // TODO(justinlin): Implement for other platforms.
102 browserActionBar.SetIconVisibilityCount(0);
103 #endif
104 frame_observer.Wait();
105 }
106
107 EXPECT_TRUE(new_browser != NULL);
108
109 // Flaky on non-aura linux http://crbug.com/309749
110 #if !(defined(OS_LINUX) && !defined(USE_AURA))
111 ResultCatcher catcher;
112 {
113 content::WindowedNotificationObserver frame_observer(
114 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
115 content::NotificationService::AllSources());
116 // Show second popup in new window.
117 listener.Reply("");
118 frame_observer.Wait();
119 EXPECT_TRUE(BrowserActionTestUtil(new_browser).HasPopup());
120 }
121 ASSERT_TRUE(catcher.GetNextResult()) << message_;
122 #endif
123 }
124
125 // Tests opening a popup in an incognito window.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,TestOpenPopupIncognito)126 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, TestOpenPopupIncognito) {
127 if (!ShouldRunPopupTest())
128 return;
129
130 content::WindowedNotificationObserver frame_observer(
131 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
132 content::NotificationService::AllSources());
133 ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup",
134 "open_popup_succeeds.html",
135 kFlagEnableIncognito | kFlagUseIncognito))
136 << message_;
137 frame_observer.Wait();
138 // Non-Aura Linux uses a singleton for the popup, so it looks like all windows
139 // have popups if there is any popup open.
140 #if !(defined(OS_LINUX) && !defined(USE_AURA))
141 // Starting window does not have a popup.
142 EXPECT_FALSE(BrowserActionTestUtil(browser()).HasPopup());
143 #endif
144 // Incognito window should have a popup.
145 EXPECT_TRUE(BrowserActionTestUtil(BrowserList::GetInstance(
146 chrome::GetActiveDesktop())->GetLastActive()).HasPopup());
147 }
148
149 #if defined(OS_LINUX)
150 #define MAYBE_TestOpenPopupDoesNotCloseOtherPopups DISABLED_TestOpenPopupDoesNotCloseOtherPopups
151 #else
152 #define MAYBE_TestOpenPopupDoesNotCloseOtherPopups TestOpenPopupDoesNotCloseOtherPopups
153 #endif
154 // Tests if there is already a popup open (by a user click or otherwise), that
155 // the openPopup API does not override it.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,MAYBE_TestOpenPopupDoesNotCloseOtherPopups)156 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
157 MAYBE_TestOpenPopupDoesNotCloseOtherPopups) {
158 if (!ShouldRunPopupTest())
159 return;
160
161 // Load a first extension that can open a popup.
162 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(
163 "browser_action/popup")));
164 const Extension* extension = GetSingleLoadedExtension();
165 ASSERT_TRUE(extension) << message_;
166
167 ExtensionTestMessageListener listener("ready", true);
168 // Load the test extension which will do nothing except notifyPass() to
169 // return control here.
170 ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup",
171 "open_popup_fails.html")) << message_;
172 EXPECT_TRUE(listener.WaitUntilSatisfied());
173
174 content::WindowedNotificationObserver frame_observer(
175 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
176 content::NotificationService::AllSources());
177 // Open popup in the first extension.
178 BrowserActionTestUtil(browser()).Press(0);
179 frame_observer.Wait();
180 EXPECT_TRUE(BrowserActionTestUtil(browser()).HasPopup());
181
182 ResultCatcher catcher;
183 // Return control to javascript to validate that opening a popup fails now.
184 listener.Reply("");
185 ASSERT_TRUE(catcher.GetNextResult()) << message_;
186 }
187
188 // Test that openPopup does not grant tab permissions like for browser action
189 // clicks if the activeTab permission is set.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,TestOpenPopupDoesNotGrantTabPermissions)190 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,
191 TestOpenPopupDoesNotGrantTabPermissions) {
192 if (!ShouldRunPopupTest())
193 return;
194
195 OpenExtensionPopupViaAPI();
196 ExtensionService* service = extensions::ExtensionSystem::Get(
197 browser()->profile())->extension_service();
198 ASSERT_FALSE(
199 service->GetExtensionById(last_loaded_extension_id(), false)
200 ->permissions_data()
201 ->HasAPIPermissionForTab(
202 SessionID::IdForTab(
203 browser()->tab_strip_model()->GetActiveWebContents()),
204 APIPermission::kTab));
205 }
206
207 // Test that the extension popup is closed when the browser window is clicked.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,BrowserClickClosesPopup1)208 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, BrowserClickClosesPopup1) {
209 if (!ShouldRunPopupTest())
210 return;
211
212 // Open an extension popup via the chrome.browserAction.openPopup API.
213 content::WindowedNotificationObserver frame_observer(
214 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
215 content::NotificationService::AllSources());
216 ASSERT_TRUE(RunExtensionSubtest("browser_action/open_popup",
217 "open_popup_succeeds.html")) << message_;
218 frame_observer.Wait();
219 EXPECT_TRUE(BrowserActionTestUtil(browser()).HasPopup());
220
221 // Click on the omnibox to close the extension popup.
222 ui_test_utils::ClickOnView(browser(), VIEW_ID_OMNIBOX);
223 EXPECT_FALSE(BrowserActionTestUtil(browser()).HasPopup());
224 }
225
226 // Test that the extension popup is closed when the browser window is clicked.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,BrowserClickClosesPopup2)227 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, BrowserClickClosesPopup2) {
228 if (!ShouldRunPopupTest())
229 return;
230
231 // Load a first extension that can open a popup.
232 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(
233 "browser_action/popup")));
234 const Extension* extension = GetSingleLoadedExtension();
235 ASSERT_TRUE(extension) << message_;
236
237 // Open an extension popup by clicking the browser action button.
238 content::WindowedNotificationObserver frame_observer(
239 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
240 content::NotificationService::AllSources());
241 BrowserActionTestUtil(browser()).Press(0);
242 frame_observer.Wait();
243 EXPECT_TRUE(BrowserActionTestUtil(browser()).HasPopup());
244
245 // Click on the omnibox to close the extension popup.
246 ui_test_utils::ClickOnView(browser(), VIEW_ID_OMNIBOX);
247 EXPECT_FALSE(BrowserActionTestUtil(browser()).HasPopup());
248 }
249
250 // Test that the extension popup is closed on browser tab switches.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,TabSwitchClosesPopup)251 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, TabSwitchClosesPopup) {
252 if (!ShouldRunPopupTest())
253 return;
254
255 // Add a second tab to the browser and open an extension popup.
256 chrome::NewTab(browser());
257 ASSERT_EQ(2, browser()->tab_strip_model()->count());
258 OpenExtensionPopupViaAPI();
259
260 // Press CTRL+TAB to change active tabs, the extension popup should close.
261 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
262 browser(), ui::VKEY_TAB, true, false, false, false));
263 EXPECT_FALSE(BrowserActionTestUtil(browser()).HasPopup());
264 }
265
266 #if defined(TOOLKIT_VIEWS)
267 // Test closing the browser while inspecting an extension popup with dev tools.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,CloseBrowserWithDevTools)268 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, CloseBrowserWithDevTools) {
269 if (!ShouldRunPopupTest())
270 return;
271
272 // Load a first extension that can open a popup.
273 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII(
274 "browser_action/popup")));
275 const Extension* extension = GetSingleLoadedExtension();
276 ASSERT_TRUE(extension) << message_;
277
278 // Open an extension popup by clicking the browser action button.
279 content::WindowedNotificationObserver frame_observer(
280 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
281 content::NotificationService::AllSources());
282 BrowserActionTestUtil(browser()).InspectPopup(0);
283 frame_observer.Wait();
284 EXPECT_TRUE(BrowserActionTestUtil(browser()).HasPopup());
285
286 // Close the browser window, this should not cause a crash.
287 chrome::CloseWindow(browser());
288 }
289 #endif // TOOLKIT_VIEWS
290
291 #if defined(OS_WIN)
292 // Test that forcibly closing the browser and popup HWND does not cause a crash.
IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest,DestroyHWNDDoesNotCrash)293 IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveTest, DestroyHWNDDoesNotCrash) {
294 if (!ShouldRunPopupTest())
295 return;
296
297 OpenExtensionPopupViaAPI();
298 BrowserActionTestUtil test_util(browser());
299 const gfx::NativeView view = test_util.GetPopupNativeView();
300 EXPECT_NE(static_cast<gfx::NativeView>(NULL), view);
301 const HWND hwnd = views::HWNDForNativeView(view);
302 EXPECT_EQ(hwnd,
303 views::HWNDForNativeView(browser()->window()->GetNativeWindow()));
304 EXPECT_EQ(TRUE, ::IsWindow(hwnd));
305
306 // Create a new browser window to prevent the message loop from terminating.
307 Browser* new_browser = chrome::FindBrowserWithWebContents(
308 browser()->OpenURL(content::OpenURLParams(
309 GURL("about:"), content::Referrer(), NEW_WINDOW,
310 content::PAGE_TRANSITION_TYPED, false)));
311
312 // Forcibly closing the browser HWND should not cause a crash.
313 EXPECT_EQ(TRUE, ::CloseWindow(hwnd));
314 EXPECT_EQ(TRUE, ::DestroyWindow(hwnd));
315 EXPECT_EQ(FALSE, ::IsWindow(hwnd));
316 }
317 #endif // OS_WIN
318
319 } // namespace
320 } // namespace extensions
321