1 // Copyright (c) 2012 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.
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/values.h"
14 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
15 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
16 #include "chrome/browser/extensions/extension_function_test_utils.h"
17 #include "chrome/browser/extensions/extension_tab_util.h"
18 #include "chrome/browser/prefs/incognito_mode_prefs.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_commands.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "chrome/test/base/in_process_browser_test.h"
25 #include "chrome/test/base/ui_test_utils.h"
26 #include "content/public/common/page_zoom.h"
27 #include "content/public/common/url_constants.h"
28 #include "extensions/common/manifest_constants.h"
29 #include "extensions/common/test_util.h"
30 #include "net/test/spawned_test_server/spawned_test_server.h"
31 #include "ui/gfx/rect.h"
33 namespace extensions {
35 namespace keys = tabs_constants;
36 namespace utils = extension_function_test_utils;
40 class ExtensionTabsTest : public InProcessBrowserTest {
45 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetWindow) {
46 int window_id = ExtensionTabUtil::GetWindowId(browser());
48 // Invalid window ID error.
49 scoped_refptr<WindowsGetFunction> function = new WindowsGetFunction();
50 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
51 function->set_extension(extension.get());
52 EXPECT_TRUE(MatchPattern(
53 utils::RunFunctionAndReturnError(
55 base::StringPrintf("[%u]", window_id + 1),
57 keys::kWindowNotFoundError));
59 // Basic window details.
61 if (browser()->window()->IsMinimized())
62 bounds = browser()->window()->GetRestoredBounds();
64 bounds = browser()->window()->GetBounds();
66 function = new WindowsGetFunction();
67 function->set_extension(extension.get());
68 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
69 utils::RunFunctionAndReturnSingleResult(
71 base::StringPrintf("[%u]", window_id),
73 EXPECT_EQ(window_id, utils::GetInteger(result.get(), "id"));
74 EXPECT_FALSE(utils::GetBoolean(result.get(), "incognito"));
75 EXPECT_EQ("normal", utils::GetString(result.get(), "type"));
76 EXPECT_EQ(bounds.x(), utils::GetInteger(result.get(), "left"));
77 EXPECT_EQ(bounds.y(), utils::GetInteger(result.get(), "top"));
78 EXPECT_EQ(bounds.width(), utils::GetInteger(result.get(), "width"));
79 EXPECT_EQ(bounds.height(), utils::GetInteger(result.get(), "height"));
81 // With "populate" enabled.
82 function = new WindowsGetFunction();
83 function->set_extension(extension.get());
84 result.reset(utils::ToDictionary(
85 utils::RunFunctionAndReturnSingleResult(
87 base::StringPrintf("[%u, {\"populate\": true}]", window_id),
90 EXPECT_EQ(window_id, utils::GetInteger(result.get(), "id"));
91 // "populate" was enabled so tabs should be populated.
92 base::ListValue* tabs = NULL;
93 EXPECT_TRUE(result.get()->GetList(keys::kTabsKey, &tabs));
95 // TODO(aa): Can't assume window is focused. On mac, calling Activate() from a
96 // browser test doesn't seem to do anything, so can't test the opposite
98 EXPECT_EQ(browser()->window()->IsActive(),
99 utils::GetBoolean(result.get(), "focused"));
101 // TODO(aa): Minimized and maximized dimensions. Is there a way to set
102 // minimize/maximize programmatically?
105 Browser* popup_browser = new Browser(
106 Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(),
107 browser()->host_desktop_type()));
108 function = new WindowsGetFunction();
109 function->set_extension(extension.get());
110 result.reset(utils::ToDictionary(
111 utils::RunFunctionAndReturnSingleResult(
114 "[%u]", ExtensionTabUtil::GetWindowId(popup_browser)),
116 EXPECT_EQ("popup", utils::GetString(result.get(), "type"));
119 Browser* incognito_browser = CreateIncognitoBrowser();
120 int incognito_window_id = ExtensionTabUtil::GetWindowId(incognito_browser);
122 // Without "include_incognito".
123 function = new WindowsGetFunction();
124 function->set_extension(extension.get());
125 EXPECT_TRUE(MatchPattern(
126 utils::RunFunctionAndReturnError(
128 base::StringPrintf("[%u]", incognito_window_id),
130 keys::kWindowNotFoundError));
132 // With "include_incognito".
133 function = new WindowsGetFunction();
134 function->set_extension(extension.get());
135 result.reset(utils::ToDictionary(
136 utils::RunFunctionAndReturnSingleResult(
138 base::StringPrintf("[%u]", incognito_window_id),
140 utils::INCLUDE_INCOGNITO)));
141 EXPECT_TRUE(utils::GetBoolean(result.get(), "incognito"));
144 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetCurrentWindow) {
145 int window_id = ExtensionTabUtil::GetWindowId(browser());
146 Browser* new_browser = CreateBrowser(browser()->profile());
147 int new_id = ExtensionTabUtil::GetWindowId(new_browser);
149 // Get the current window using new_browser.
150 scoped_refptr<WindowsGetCurrentFunction> function =
151 new WindowsGetCurrentFunction();
152 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
153 function->set_extension(extension.get());
154 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
155 utils::RunFunctionAndReturnSingleResult(function.get(),
159 // The id should match the window id of the browser instance that was passed
160 // to RunFunctionAndReturnSingleResult.
161 EXPECT_EQ(new_id, utils::GetInteger(result.get(), "id"));
162 base::ListValue* tabs = NULL;
163 EXPECT_FALSE(result.get()->GetList(keys::kTabsKey, &tabs));
165 // Get the current window using the old window and make the tabs populated.
166 function = new WindowsGetCurrentFunction();
167 function->set_extension(extension.get());
168 result.reset(utils::ToDictionary(
169 utils::RunFunctionAndReturnSingleResult(function.get(),
170 "[{\"populate\": true}]",
173 // The id should match the window id of the browser instance that was passed
174 // to RunFunctionAndReturnSingleResult.
175 EXPECT_EQ(window_id, utils::GetInteger(result.get(), "id"));
176 // "populate" was enabled so tabs should be populated.
177 EXPECT_TRUE(result.get()->GetList(keys::kTabsKey, &tabs));
180 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, GetAllWindows) {
181 const size_t NUM_WINDOWS = 5;
182 std::set<int> window_ids;
183 std::set<int> result_ids;
184 window_ids.insert(ExtensionTabUtil::GetWindowId(browser()));
186 for (size_t i = 0; i < NUM_WINDOWS - 1; ++i) {
187 Browser* new_browser = CreateBrowser(browser()->profile());
188 window_ids.insert(ExtensionTabUtil::GetWindowId(new_browser));
191 scoped_refptr<WindowsGetAllFunction> function = new WindowsGetAllFunction();
192 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
193 function->set_extension(extension.get());
194 scoped_ptr<base::ListValue> result(utils::ToList(
195 utils::RunFunctionAndReturnSingleResult(function.get(),
199 base::ListValue* windows = result.get();
200 EXPECT_EQ(NUM_WINDOWS, windows->GetSize());
201 for (size_t i = 0; i < NUM_WINDOWS; ++i) {
202 base::DictionaryValue* result_window = NULL;
203 EXPECT_TRUE(windows->GetDictionary(i, &result_window));
204 result_ids.insert(utils::GetInteger(result_window, "id"));
206 // "populate" was not passed in so tabs are not populated.
207 base::ListValue* tabs = NULL;
208 EXPECT_FALSE(result_window->GetList(keys::kTabsKey, &tabs));
210 // The returned ids should contain all the current browser instance ids.
211 EXPECT_EQ(window_ids, result_ids);
214 function = new WindowsGetAllFunction();
215 function->set_extension(extension.get());
216 result.reset(utils::ToList(
217 utils::RunFunctionAndReturnSingleResult(function.get(),
218 "[{\"populate\": true}]",
221 windows = result.get();
222 EXPECT_EQ(NUM_WINDOWS, windows->GetSize());
223 for (size_t i = 0; i < windows->GetSize(); ++i) {
224 base::DictionaryValue* result_window = NULL;
225 EXPECT_TRUE(windows->GetDictionary(i, &result_window));
226 result_ids.insert(utils::GetInteger(result_window, "id"));
228 // "populate" was enabled so tabs should be populated.
229 base::ListValue* tabs = NULL;
230 EXPECT_TRUE(result_window->GetList(keys::kTabsKey, &tabs));
232 // The returned ids should contain all the current browser instance ids.
233 EXPECT_EQ(window_ids, result_ids);
236 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, UpdateNoPermissions) {
237 // The test empty extension has no permissions, therefore it should not get
238 // tab data in the function result.
239 scoped_refptr<TabsUpdateFunction> update_tab_function(
240 new TabsUpdateFunction());
241 scoped_refptr<Extension> empty_extension(test_util::CreateEmptyExtension());
242 update_tab_function->set_extension(empty_extension.get());
243 // Without a callback the function will not generate a result.
244 update_tab_function->set_has_callback(true);
246 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
247 utils::RunFunctionAndReturnSingleResult(
248 update_tab_function.get(),
249 "[null, {\"url\": \"about:blank\", \"pinned\": true}]",
251 // The url is stripped since the extension does not have tab permissions.
252 EXPECT_FALSE(result->HasKey("url"));
253 EXPECT_TRUE(utils::GetBoolean(result.get(), "pinned"));
256 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest,
257 DefaultToIncognitoWhenItIsForced) {
258 static const char kArgsWithoutExplicitIncognitoParam[] =
259 "[{\"url\": \"about:blank\"}]";
260 // Force Incognito mode.
261 IncognitoModePrefs::SetAvailability(browser()->profile()->GetPrefs(),
262 IncognitoModePrefs::FORCED);
263 // Run without an explicit "incognito" param.
264 scoped_refptr<WindowsCreateFunction> function(new WindowsCreateFunction());
265 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
266 function->set_extension(extension.get());
267 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
268 utils::RunFunctionAndReturnSingleResult(
270 kArgsWithoutExplicitIncognitoParam,
272 utils::INCLUDE_INCOGNITO)));
274 // Make sure it is a new(different) window.
275 EXPECT_NE(ExtensionTabUtil::GetWindowId(browser()),
276 utils::GetInteger(result.get(), "id"));
277 // ... and it is incognito.
278 EXPECT_TRUE(utils::GetBoolean(result.get(), "incognito"));
280 // Now try creating a window from incognito window.
281 Browser* incognito_browser = CreateIncognitoBrowser();
282 // Run without an explicit "incognito" param.
283 function = new WindowsCreateFunction();
284 function->set_extension(extension.get());
285 result.reset(utils::ToDictionary(
286 utils::RunFunctionAndReturnSingleResult(
288 kArgsWithoutExplicitIncognitoParam,
290 utils::INCLUDE_INCOGNITO)));
291 // Make sure it is a new(different) window.
292 EXPECT_NE(ExtensionTabUtil::GetWindowId(incognito_browser),
293 utils::GetInteger(result.get(), "id"));
294 // ... and it is incognito.
295 EXPECT_TRUE(utils::GetBoolean(result.get(), "incognito"));
298 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest,
299 DefaultToIncognitoWhenItIsForcedAndNoArgs) {
300 static const char kEmptyArgs[] = "[]";
301 // Force Incognito mode.
302 IncognitoModePrefs::SetAvailability(browser()->profile()->GetPrefs(),
303 IncognitoModePrefs::FORCED);
304 // Run without an explicit "incognito" param.
305 scoped_refptr<WindowsCreateFunction> function = new WindowsCreateFunction();
306 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
307 function->set_extension(extension.get());
308 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
309 utils::RunFunctionAndReturnSingleResult(function.get(),
312 utils::INCLUDE_INCOGNITO)));
314 // Make sure it is a new(different) window.
315 EXPECT_NE(ExtensionTabUtil::GetWindowId(browser()),
316 utils::GetInteger(result.get(), "id"));
317 // ... and it is incognito.
318 EXPECT_TRUE(utils::GetBoolean(result.get(), "incognito"));
320 // Now try creating a window from incognito window.
321 Browser* incognito_browser = CreateIncognitoBrowser();
322 // Run without an explicit "incognito" param.
323 function = new WindowsCreateFunction();
324 function->set_extension(extension.get());
325 result.reset(utils::ToDictionary(
326 utils::RunFunctionAndReturnSingleResult(function.get(),
329 utils::INCLUDE_INCOGNITO)));
330 // Make sure it is a new(different) window.
331 EXPECT_NE(ExtensionTabUtil::GetWindowId(incognito_browser),
332 utils::GetInteger(result.get(), "id"));
333 // ... and it is incognito.
334 EXPECT_TRUE(utils::GetBoolean(result.get(), "incognito"));
337 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest,
338 DontCreateNormalWindowWhenIncognitoForced) {
339 static const char kArgsWithExplicitIncognitoParam[] =
340 "[{\"url\": \"about:blank\", \"incognito\": false }]";
341 // Force Incognito mode.
342 IncognitoModePrefs::SetAvailability(browser()->profile()->GetPrefs(),
343 IncognitoModePrefs::FORCED);
345 // Run with an explicit "incognito" param.
346 scoped_refptr<WindowsCreateFunction> function = new WindowsCreateFunction();
347 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
348 function->set_extension(extension.get());
349 EXPECT_TRUE(MatchPattern(
350 utils::RunFunctionAndReturnError(function.get(),
351 kArgsWithExplicitIncognitoParam,
353 keys::kIncognitoModeIsForced));
355 // Now try opening a normal window from incognito window.
356 Browser* incognito_browser = CreateIncognitoBrowser();
357 // Run with an explicit "incognito" param.
358 function = new WindowsCreateFunction();
359 function->set_extension(extension.get());
360 EXPECT_TRUE(MatchPattern(
361 utils::RunFunctionAndReturnError(function.get(),
362 kArgsWithExplicitIncognitoParam,
364 keys::kIncognitoModeIsForced));
367 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest,
368 DontCreateIncognitoWindowWhenIncognitoDisabled) {
369 static const char kArgs[] =
370 "[{\"url\": \"about:blank\", \"incognito\": true }]";
372 Browser* incognito_browser = CreateIncognitoBrowser();
373 // Disable Incognito mode.
374 IncognitoModePrefs::SetAvailability(browser()->profile()->GetPrefs(),
375 IncognitoModePrefs::DISABLED);
376 // Run in normal window.
377 scoped_refptr<WindowsCreateFunction> function = new WindowsCreateFunction();
378 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
379 function->set_extension(extension.get());
380 EXPECT_TRUE(MatchPattern(
381 utils::RunFunctionAndReturnError(function.get(),
384 keys::kIncognitoModeIsDisabled));
386 // Run in incognito window.
387 function = new WindowsCreateFunction();
388 function->set_extension(extension.get());
389 EXPECT_TRUE(MatchPattern(
390 utils::RunFunctionAndReturnError(function.get(),
393 keys::kIncognitoModeIsDisabled));
396 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, QueryCurrentWindowTabs) {
397 const size_t kExtraWindows = 3;
398 for (size_t i = 0; i < kExtraWindows; ++i)
399 CreateBrowser(browser()->profile());
401 GURL url(url::kAboutBlankURL);
402 AddTabAtIndexToBrowser(browser(), 0, url, ui::PAGE_TRANSITION_LINK);
403 int window_id = ExtensionTabUtil::GetWindowId(browser());
405 // Get tabs in the 'current' window called from non-focused browser.
406 scoped_refptr<TabsQueryFunction> function = new TabsQueryFunction();
407 function->set_extension(test_util::CreateEmptyExtension().get());
408 scoped_ptr<base::ListValue> result(utils::ToList(
409 utils::RunFunctionAndReturnSingleResult(function.get(),
410 "[{\"currentWindow\":true}]",
413 base::ListValue* result_tabs = result.get();
414 // We should have one initial tab and one added tab.
415 EXPECT_EQ(2u, result_tabs->GetSize());
416 for (size_t i = 0; i < result_tabs->GetSize(); ++i) {
417 base::DictionaryValue* result_tab = NULL;
418 EXPECT_TRUE(result_tabs->GetDictionary(i, &result_tab));
419 EXPECT_EQ(window_id, utils::GetInteger(result_tab, keys::kWindowIdKey));
422 // Get tabs NOT in the 'current' window called from non-focused browser.
423 function = new TabsQueryFunction();
424 function->set_extension(test_util::CreateEmptyExtension().get());
425 result.reset(utils::ToList(
426 utils::RunFunctionAndReturnSingleResult(function.get(),
427 "[{\"currentWindow\":false}]",
430 result_tabs = result.get();
431 // We should have one tab for each extra window.
432 EXPECT_EQ(kExtraWindows, result_tabs->GetSize());
433 for (size_t i = 0; i < kExtraWindows; ++i) {
434 base::DictionaryValue* result_tab = NULL;
435 EXPECT_TRUE(result_tabs->GetDictionary(i, &result_tab));
436 EXPECT_NE(window_id, utils::GetInteger(result_tab, keys::kWindowIdKey));
440 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DontCreateTabInClosingPopupWindow) {
441 // Test creates new popup window, closes it right away and then tries to open
442 // a new tab in it. Tab should not be opened in the popup window, but in a
443 // tabbed browser window.
444 Browser* popup_browser = new Browser(
445 Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(),
446 browser()->host_desktop_type()));
447 int window_id = ExtensionTabUtil::GetWindowId(popup_browser);
448 chrome::CloseWindow(popup_browser);
450 scoped_refptr<TabsCreateFunction> create_tab_function(
451 new TabsCreateFunction());
452 create_tab_function->set_extension(test_util::CreateEmptyExtension().get());
453 // Without a callback the function will not generate a result.
454 create_tab_function->set_has_callback(true);
456 static const char kNewBlankTabArgs[] =
457 "[{\"url\": \"about:blank\", \"windowId\": %u}]";
459 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
460 utils::RunFunctionAndReturnSingleResult(
461 create_tab_function.get(),
462 base::StringPrintf(kNewBlankTabArgs, window_id),
465 EXPECT_NE(window_id, utils::GetInteger(result.get(), "windowId"));
468 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) {
469 int window_id = ExtensionTabUtil::GetWindowId(browser());
471 static const char kArgsMinimizedWithFocus[] =
472 "[%u, {\"state\": \"minimized\", \"focused\": true}]";
473 scoped_refptr<WindowsUpdateFunction> function = new WindowsUpdateFunction();
474 scoped_refptr<Extension> extension(test_util::CreateEmptyExtension());
475 function->set_extension(extension.get());
476 EXPECT_TRUE(MatchPattern(
477 utils::RunFunctionAndReturnError(
479 base::StringPrintf(kArgsMinimizedWithFocus, window_id),
481 keys::kInvalidWindowStateError));
483 static const char kArgsMaximizedWithoutFocus[] =
484 "[%u, {\"state\": \"maximized\", \"focused\": false}]";
485 function = new WindowsUpdateFunction();
486 function->set_extension(extension.get());
487 EXPECT_TRUE(MatchPattern(
488 utils::RunFunctionAndReturnError(
490 base::StringPrintf(kArgsMaximizedWithoutFocus, window_id),
492 keys::kInvalidWindowStateError));
494 static const char kArgsMinimizedWithBounds[] =
495 "[%u, {\"state\": \"minimized\", \"width\": 500}]";
496 function = new WindowsUpdateFunction();
497 function->set_extension(extension.get());
498 EXPECT_TRUE(MatchPattern(
499 utils::RunFunctionAndReturnError(
501 base::StringPrintf(kArgsMinimizedWithBounds, window_id),
503 keys::kInvalidWindowStateError));
505 static const char kArgsMaximizedWithBounds[] =
506 "[%u, {\"state\": \"maximized\", \"width\": 500}]";
507 function = new WindowsUpdateFunction();
508 function->set_extension(extension.get());
509 EXPECT_TRUE(MatchPattern(
510 utils::RunFunctionAndReturnError(
512 base::StringPrintf(kArgsMaximizedWithBounds, window_id),
514 keys::kInvalidWindowStateError));
517 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DuplicateTab) {
518 content::OpenURLParams params(GURL(url::kAboutBlankURL),
521 ui::PAGE_TRANSITION_LINK,
523 content::WebContents* web_contents = browser()->OpenURL(params);
524 int tab_id = ExtensionTabUtil::GetTabId(web_contents);
525 int window_id = ExtensionTabUtil::GetWindowIdOfTab(web_contents);
527 TabStripModel* tab_strip;
528 ExtensionTabUtil::GetTabStripModel(web_contents, &tab_strip, &tab_index);
530 scoped_refptr<TabsDuplicateFunction> duplicate_tab_function(
531 new TabsDuplicateFunction());
532 scoped_ptr<base::DictionaryValue> test_extension_value(
533 utils::ParseDictionary(
534 "{\"name\": \"Test\", \"version\": \"1.0\", \"permissions\": [\"tabs\"]}"
536 scoped_refptr<Extension> empty_tab_extension(
537 utils::CreateExtension(test_extension_value.get()));
538 duplicate_tab_function->set_extension(empty_tab_extension.get());
539 duplicate_tab_function->set_has_callback(true);
541 scoped_ptr<base::DictionaryValue> duplicate_result(utils::ToDictionary(
542 utils::RunFunctionAndReturnSingleResult(
543 duplicate_tab_function.get(), base::StringPrintf("[%u]", tab_id),
546 int duplicate_tab_id = utils::GetInteger(duplicate_result.get(), "id");
547 int duplicate_tab_window_id = utils::GetInteger(duplicate_result.get(),
549 int duplicate_tab_index = utils::GetInteger(duplicate_result.get(), "index");
550 EXPECT_EQ(base::Value::TYPE_DICTIONARY, duplicate_result->GetType());
551 // Duplicate tab id should be different from the original tab id.
552 EXPECT_NE(tab_id, duplicate_tab_id);
553 EXPECT_EQ(window_id, duplicate_tab_window_id);
554 EXPECT_EQ(tab_index + 1, duplicate_tab_index);
555 // The test empty tab extension has tabs permissions, therefore
556 // |duplicate_result| should contain url, title, and faviconUrl
557 // in the function result.
558 EXPECT_TRUE(utils::HasPrivacySensitiveFields(duplicate_result.get()));
561 IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DuplicateTabNoPermission) {
562 content::OpenURLParams params(GURL(url::kAboutBlankURL),
565 ui::PAGE_TRANSITION_LINK,
567 content::WebContents* web_contents = browser()->OpenURL(params);
568 int tab_id = ExtensionTabUtil::GetTabId(web_contents);
569 int window_id = ExtensionTabUtil::GetWindowIdOfTab(web_contents);
571 TabStripModel* tab_strip;
572 ExtensionTabUtil::GetTabStripModel(web_contents, &tab_strip, &tab_index);
574 scoped_refptr<TabsDuplicateFunction> duplicate_tab_function(
575 new TabsDuplicateFunction());
576 scoped_refptr<Extension> empty_extension(test_util::CreateEmptyExtension());
577 duplicate_tab_function->set_extension(empty_extension.get());
578 duplicate_tab_function->set_has_callback(true);
580 scoped_ptr<base::DictionaryValue> duplicate_result(utils::ToDictionary(
581 utils::RunFunctionAndReturnSingleResult(
582 duplicate_tab_function.get(), base::StringPrintf("[%u]", tab_id),
585 int duplicate_tab_id = utils::GetInteger(duplicate_result.get(), "id");
586 int duplicate_tab_window_id = utils::GetInteger(duplicate_result.get(),
588 int duplicate_tab_index = utils::GetInteger(duplicate_result.get(), "index");
589 EXPECT_EQ(base::Value::TYPE_DICTIONARY, duplicate_result->GetType());
590 // Duplicate tab id should be different from the original tab id.
591 EXPECT_NE(tab_id, duplicate_tab_id);
592 EXPECT_EQ(window_id, duplicate_tab_window_id);
593 EXPECT_EQ(tab_index + 1, duplicate_tab_index);
594 // The test empty extension has no permissions, therefore |duplicate_result|
595 // should not contain url, title, and faviconUrl in the function result.
596 EXPECT_FALSE(utils::HasPrivacySensitiveFields(duplicate_result.get()));
599 // Tester class for the tabs.zoom* api functions.
600 class ExtensionTabsZoomTest : public ExtensionTabsTest {
602 void SetUpOnMainThread() override;
604 // Runs chrome.tabs.setZoom().
605 bool RunSetZoom(int tab_id, double zoom_factor);
607 // Runs chrome.tabs.getZoom().
608 testing::AssertionResult RunGetZoom(int tab_id, double* zoom_factor);
610 // Runs chrome.tabs.setZoomSettings().
611 bool RunSetZoomSettings(int tab_id, const char* mode, const char* scope);
613 // Runs chrome.tabs.getZoomSettings().
614 testing::AssertionResult RunGetZoomSettings(int tab_id,
618 // Runs chrome.tabs.setZoom(), expecting an error.
619 std::string RunSetZoomExpectError(int tab_id,
622 // Runs chrome.tabs.setZoomSettings(), expecting an error.
623 std::string RunSetZoomSettingsExpectError(int tab_id,
627 content::WebContents* OpenUrlAndWaitForLoad(const GURL& url);
630 scoped_refptr<Extension> extension_;
633 void ExtensionTabsZoomTest::SetUpOnMainThread() {
634 ExtensionTabsTest::SetUpOnMainThread();
635 extension_ = test_util::CreateEmptyExtension();
638 bool ExtensionTabsZoomTest::RunSetZoom(int tab_id, double zoom_factor) {
639 scoped_refptr<TabsSetZoomFunction> set_zoom_function(
640 new TabsSetZoomFunction());
641 set_zoom_function->set_extension(extension_.get());
642 set_zoom_function->set_has_callback(true);
644 return utils::RunFunction(
645 set_zoom_function.get(),
646 base::StringPrintf("[%u, %lf]", tab_id, zoom_factor),
648 extension_function_test_utils::NONE);
651 testing::AssertionResult ExtensionTabsZoomTest::RunGetZoom(
653 double* zoom_factor) {
654 scoped_refptr<TabsGetZoomFunction> get_zoom_function(
655 new TabsGetZoomFunction());
656 get_zoom_function->set_extension(extension_.get());
657 get_zoom_function->set_has_callback(true);
659 scoped_ptr<base::Value> get_zoom_result(
660 utils::RunFunctionAndReturnSingleResult(
661 get_zoom_function.get(),
662 base::StringPrintf("[%u]", tab_id),
665 if (!get_zoom_result)
666 return testing::AssertionFailure() << "no result";
667 if (!get_zoom_result->GetAsDouble(zoom_factor))
668 return testing::AssertionFailure() << "result was not a double";
670 return testing::AssertionSuccess();
673 bool ExtensionTabsZoomTest::RunSetZoomSettings(int tab_id,
676 scoped_refptr<TabsSetZoomSettingsFunction> set_zoom_settings_function(
677 new TabsSetZoomSettingsFunction());
678 set_zoom_settings_function->set_extension(extension_.get());
682 args = base::StringPrintf("[%u, {\"mode\": \"%s\", \"scope\": \"%s\"}]",
683 tab_id, mode, scope);
685 args = base::StringPrintf("[%u, {\"mode\": \"%s\"}]", tab_id, mode);
688 return utils::RunFunction(set_zoom_settings_function.get(),
691 extension_function_test_utils::NONE);
694 testing::AssertionResult ExtensionTabsZoomTest::RunGetZoomSettings(
697 std::string* scope) {
700 scoped_refptr<TabsGetZoomSettingsFunction> get_zoom_settings_function(
701 new TabsGetZoomSettingsFunction());
702 get_zoom_settings_function->set_extension(extension_.get());
703 get_zoom_settings_function->set_has_callback(true);
705 scoped_ptr<base::DictionaryValue> get_zoom_settings_result(
706 utils::ToDictionary(utils::RunFunctionAndReturnSingleResult(
707 get_zoom_settings_function.get(),
708 base::StringPrintf("[%u]", tab_id),
711 if (!get_zoom_settings_result)
712 return testing::AssertionFailure() << "no result";
714 *mode = utils::GetString(get_zoom_settings_result.get(), "mode");
715 *scope = utils::GetString(get_zoom_settings_result.get(), "scope");
717 return testing::AssertionSuccess();
720 std::string ExtensionTabsZoomTest::RunSetZoomExpectError(int tab_id,
721 double zoom_factor) {
722 scoped_refptr<TabsSetZoomFunction> set_zoom_function(
723 new TabsSetZoomFunction());
724 set_zoom_function->set_extension(extension_.get());
725 set_zoom_function->set_has_callback(true);
727 return utils::RunFunctionAndReturnError(
728 set_zoom_function.get(),
729 base::StringPrintf("[%u, %lf]", tab_id, zoom_factor),
733 std::string ExtensionTabsZoomTest::RunSetZoomSettingsExpectError(
737 scoped_refptr<TabsSetZoomSettingsFunction> set_zoom_settings_function(
738 new TabsSetZoomSettingsFunction());
739 set_zoom_settings_function->set_extension(extension_.get());
741 return utils::RunFunctionAndReturnError(set_zoom_settings_function.get(),
743 "[%u, {\"mode\": \"%s\", "
744 "\"scope\": \"%s\"}]",
751 content::WebContents* ExtensionTabsZoomTest::OpenUrlAndWaitForLoad(
753 ui_test_utils::NavigateToURLWithDisposition(
757 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
758 return browser()->tab_strip_model()->GetActiveWebContents();
763 double GetZoomLevel(const content::WebContents* web_contents) {
764 return ZoomController::FromWebContents(web_contents)->GetZoomLevel();
767 content::OpenURLParams GetOpenParams(const char* url) {
768 return content::OpenURLParams(GURL(url),
771 ui::PAGE_TRANSITION_LINK,
777 IN_PROC_BROWSER_TEST_F(ExtensionTabsZoomTest, SetAndGetZoom) {
778 content::OpenURLParams params(GetOpenParams(url::kAboutBlankURL));
779 content::WebContents* web_contents = OpenUrlAndWaitForLoad(params.url);
780 int tab_id = ExtensionTabUtil::GetTabId(web_contents);
782 // Test default values before we set anything.
783 double zoom_factor = -1;
784 EXPECT_TRUE(RunGetZoom(tab_id, &zoom_factor));
785 EXPECT_EQ(1.0, zoom_factor);
787 // Test chrome.tabs.setZoom().
788 const double kZoomLevel = 0.8;
789 EXPECT_TRUE(RunSetZoom(tab_id, kZoomLevel));
790 EXPECT_EQ(kZoomLevel,
791 content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents)));
793 // Test chrome.tabs.getZoom().
795 EXPECT_TRUE(RunGetZoom(tab_id, &zoom_factor));
796 EXPECT_EQ(kZoomLevel, zoom_factor);
799 IN_PROC_BROWSER_TEST_F(ExtensionTabsZoomTest, ZoomSettings) {
800 // In this test we need two URLs that (1) represent real pages (i.e. they
801 // load without causing an error page load), (2) have different domains, and
802 // (3) are zoomable by the extension API (this last condition rules out
803 // chrome:// urls). We achieve this by noting that about:blank meets these
804 // requirements, allowing us to spin up a spawned http server on localhost to
805 // get the other domain.
806 net::SpawnedTestServer http_server(
807 net::SpawnedTestServer::TYPE_HTTP,
808 net::SpawnedTestServer::kLocalhost,
809 base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
810 ASSERT_TRUE(http_server.Start());
812 GURL url_A = http_server.GetURL("files/simple.html");
813 GURL url_B("about:blank");
815 // Tabs A1 and A2 are navigated to the same origin, while B is navigated
816 // to a different one.
817 content::WebContents* web_contents_A1 = OpenUrlAndWaitForLoad(url_A);
818 content::WebContents* web_contents_A2 = OpenUrlAndWaitForLoad(url_A);
819 content::WebContents* web_contents_B = OpenUrlAndWaitForLoad(url_B);
821 int tab_id_A1 = ExtensionTabUtil::GetTabId(web_contents_A1);
822 int tab_id_A2 = ExtensionTabUtil::GetTabId(web_contents_A2);
823 int tab_id_B = ExtensionTabUtil::GetTabId(web_contents_B);
826 1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
828 1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
830 1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_B)));
832 // Test per-origin automatic zoom settings.
833 EXPECT_TRUE(RunSetZoom(tab_id_B, 1.f));
834 EXPECT_TRUE(RunSetZoom(tab_id_A2, 1.1f));
836 1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
838 1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
840 content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_B)));
842 // Test per-tab automatic zoom settings.
843 EXPECT_TRUE(RunSetZoomSettings(tab_id_A1, "automatic", "per-tab"));
844 EXPECT_TRUE(RunSetZoom(tab_id_A1, 1.2f));
846 1.2f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
848 1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
850 // Test 'manual' mode.
851 EXPECT_TRUE(RunSetZoomSettings(tab_id_A1, "manual", NULL));
852 EXPECT_TRUE(RunSetZoom(tab_id_A1, 1.3f));
854 1.3f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
856 1.1f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
858 // Test 'disabled' mode, which will reset A1's zoom to 1.f.
859 EXPECT_TRUE(RunSetZoomSettings(tab_id_A1, "disabled", NULL));
860 std::string error = RunSetZoomExpectError(tab_id_A1, 1.4f);
861 EXPECT_TRUE(MatchPattern(error, keys::kCannotZoomDisabledTabError));
863 1.f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A1)));
864 // We should still be able to zoom A2 though.
865 EXPECT_TRUE(RunSetZoom(tab_id_A2, 1.4f));
867 1.4f, content::ZoomLevelToZoomFactor(GetZoomLevel(web_contents_A2)));
870 IN_PROC_BROWSER_TEST_F(ExtensionTabsZoomTest, GetZoomSettings) {
871 content::OpenURLParams params(GetOpenParams(url::kAboutBlankURL));
872 content::WebContents* web_contents = OpenUrlAndWaitForLoad(params.url);
873 int tab_id = ExtensionTabUtil::GetTabId(web_contents);
878 EXPECT_TRUE(RunGetZoomSettings(tab_id, &mode, &scope));
879 EXPECT_EQ("automatic", mode);
880 EXPECT_EQ("per-origin", scope);
882 EXPECT_TRUE(RunSetZoomSettings(tab_id, "automatic", "per-tab"));
883 EXPECT_TRUE(RunGetZoomSettings(tab_id, &mode, &scope));
885 EXPECT_EQ("automatic", mode);
886 EXPECT_EQ("per-tab", scope);
889 RunSetZoomSettingsExpectError(tab_id, "manual", "per-origin");
890 EXPECT_TRUE(MatchPattern(error,
891 keys::kPerOriginOnlyInAutomaticError));
893 RunSetZoomSettingsExpectError(tab_id, "disabled", "per-origin");
894 EXPECT_TRUE(MatchPattern(error,
895 keys::kPerOriginOnlyInAutomaticError));
898 IN_PROC_BROWSER_TEST_F(ExtensionTabsZoomTest, CannotZoomInvalidTab) {
899 content::OpenURLParams params(GetOpenParams(url::kAboutBlankURL));
900 content::WebContents* web_contents = OpenUrlAndWaitForLoad(params.url);
901 int tab_id = ExtensionTabUtil::GetTabId(web_contents);
903 int bogus_id = tab_id + 100;
904 std::string error = RunSetZoomExpectError(bogus_id, 3.14159);
905 EXPECT_TRUE(MatchPattern(error, keys::kTabNotFoundError));
907 error = RunSetZoomSettingsExpectError(bogus_id, "manual", "per-tab");
908 EXPECT_TRUE(MatchPattern(error, keys::kTabNotFoundError));
910 const char kNewTestTabArgs[] = "chrome://version";
911 params = GetOpenParams(kNewTestTabArgs);
912 web_contents = browser()->OpenURL(params);
913 tab_id = ExtensionTabUtil::GetTabId(web_contents);
915 // Test chrome.tabs.setZoom().
916 error = RunSetZoomExpectError(tab_id, 3.14159);
917 EXPECT_TRUE(MatchPattern(error, manifest_errors::kCannotAccessChromeUrl));
919 // chrome.tabs.setZoomSettings().
920 error = RunSetZoomSettingsExpectError(tab_id, "manual", "per-tab");
921 EXPECT_TRUE(MatchPattern(error, manifest_errors::kCannotAccessChromeUrl));
924 } // namespace extensions