diff options
author | ncj674@motorola.com <ncj674@motorola.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-10 15:09:57 +0000 |
---|---|---|
committer | ncj674@motorola.com <ncj674@motorola.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-10 15:09:57 +0000 |
commit | 37bb582bd18aece7075e55cc4b69ba3f73b725ae (patch) | |
tree | 367b9a78e17c753bb985444cebc55149233dd4c8 | |
parent | 70771b498462cca3a79d0b80810f9afa87e7ffb8 (diff) | |
download | chromium_src-37bb582bd18aece7075e55cc4b69ba3f73b725ae.zip chromium_src-37bb582bd18aece7075e55cc4b69ba3f73b725ae.tar.gz chromium_src-37bb582bd18aece7075e55cc4b69ba3f73b725ae.tar.bz2 |
Tabs Extension: Implementation of tabs.duplicate api.
BUG=36007
TEST=Manual: Tested with an extension with/without "tabs" permission.
Automated: unittest: ExtensionTabsTest.DuplicateTabNoPermission, ExtensionPermissionsTest.DefaultFunctionAccess, ExtensionTest.ApiPermissions
apitest: ExtensionApiTest.TabDuplicate
Review URL: https://chromiumcodereview.appspot.com/10697017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155731 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/extensions/api/tabs/tabs.cc | 27 | ||||
-rw-r--r-- | chrome/browser/extensions/api/tabs/tabs.h | 5 | ||||
-rw-r--r-- | chrome/browser/extensions/api/tabs/tabs_test.cc | 83 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_function_registry.cc | 15 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_function_test_utils.cc | 28 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_function_test_utils.h | 13 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_tabs_apitest.cc | 4 | ||||
-rw-r--r-- | chrome/browser/ui/browser_commands.cc | 3 | ||||
-rw-r--r-- | chrome/browser/ui/browser_commands.h | 2 | ||||
-rw-r--r-- | chrome/common/extensions/api/tabs.json | 26 | ||||
-rw-r--r-- | chrome/common/extensions/extension_unittest.cc | 1 | ||||
-rw-r--r-- | chrome/common/extensions/permissions/permission_set_unittest.cc | 1 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/tabs/basics/duplicate.html | 7 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/tabs/basics/duplicate.js | 32 |
14 files changed, 235 insertions, 12 deletions
diff --git a/chrome/browser/extensions/api/tabs/tabs.cc b/chrome/browser/extensions/api/tabs/tabs.cc index 76e34a9..1156651 100644 --- a/chrome/browser/extensions/api/tabs/tabs.cc +++ b/chrome/browser/extensions/api/tabs/tabs.cc @@ -1093,6 +1093,33 @@ bool CreateTabFunction::RunImpl() { return true; } +bool DuplicateTabFunction::RunImpl() { + int tab_id = -1; + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); + + Browser* browser = NULL; + TabStripModel* tab_strip = NULL; + TabContents* contents = NULL; + int tab_index = -1; + if (!GetTabById(tab_id, profile(), include_incognito(), + &browser, &tab_strip, &contents, &tab_index, &error_)) { + return false; + } + + TabContents* new_contents = chrome::DuplicateTabAt(browser, tab_index); + if (!has_callback()) + return true; + + int new_index = tab_strip->GetIndexOfTabContents(new_contents); + + // Return data about the newly created tab. + SetResult(ExtensionTabUtil::CreateTabValue( + new_contents->web_contents(), + tab_strip, new_index, GetExtension())); + + return true; +} + bool GetTabFunction::RunImpl() { int tab_id = -1; EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &tab_id)); diff --git a/chrome/browser/extensions/api/tabs/tabs.h b/chrome/browser/extensions/api/tabs/tabs.h index 26125ac..b808950 100644 --- a/chrome/browser/extensions/api/tabs/tabs.h +++ b/chrome/browser/extensions/api/tabs/tabs.h @@ -109,6 +109,11 @@ class CreateTabFunction : public SyncExtensionFunction { virtual bool RunImpl() OVERRIDE; DECLARE_EXTENSION_FUNCTION_NAME("tabs.create") }; +class DuplicateTabFunction : public SyncExtensionFunction { + virtual ~DuplicateTabFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION_NAME("tabs.duplicate") +}; class HighlightTabsFunction : public SyncExtensionFunction { virtual ~HighlightTabsFunction() {} virtual bool RunImpl() OVERRIDE; diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc index dfe8f62..e9c97488 100644 --- a/chrome/browser/extensions/api/tabs/tabs_test.cc +++ b/chrome/browser/extensions/api/tabs/tabs_test.cc @@ -604,3 +604,86 @@ IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, InvalidUpdateWindowState) { browser()), keys::kInvalidWindowStateError)); } + +IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DuplicateTab) { + static const char kNewBlankTabArgs[] ="about:blank"; + + content::OpenURLParams params(GURL(kNewBlankTabArgs), content::Referrer(), + NEW_FOREGROUND_TAB, + content::PAGE_TRANSITION_LINK, false); + content::WebContents* web_contents = browser()->OpenURL(params); + int tab_id = ExtensionTabUtil::GetTabId(web_contents); + int window_id = ExtensionTabUtil::GetWindowIdOfTab(web_contents); + int tab_index = -1; + TabStripModel* tab_strip; + ExtensionTabUtil::GetTabStripModel(web_contents, &tab_strip, &tab_index); + + scoped_refptr<DuplicateTabFunction> duplicate_tab_function( + new DuplicateTabFunction()); + scoped_ptr<base::DictionaryValue> test_extension_value( + utils::ParseDictionary( + "{\"name\": \"Test\", \"version\": \"1.0\", \"permissions\": [\"tabs\"]}" + )); + scoped_refptr<extensions::Extension> empty_tab_extension( + utils::CreateExtension(test_extension_value.get())); + duplicate_tab_function->set_extension(empty_tab_extension.get()); + duplicate_tab_function->set_has_callback(true); + + scoped_ptr<base::DictionaryValue> duplicate_result(utils::ToDictionary( + utils::RunFunctionAndReturnSingleResult( + duplicate_tab_function.get(), base::StringPrintf("[%u]", tab_id), + browser()))); + + int duplicate_tab_id = utils::GetInteger(duplicate_result.get(), "id"); + int duplicate_tab_window_id = utils::GetInteger(duplicate_result.get(), + "windowId"); + int duplicate_tab_index = utils::GetInteger(duplicate_result.get(), "index"); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, duplicate_result->GetType()); + // Duplicate tab id should be different from the original tab id. + EXPECT_NE(tab_id, duplicate_tab_id); + EXPECT_EQ(window_id, duplicate_tab_window_id); + EXPECT_EQ(tab_index + 1, duplicate_tab_index); + // The test empty tab extension has tabs permissions, therefore + // |duplicate_result| should contain url, title, and faviconUrl + // in the function result. + EXPECT_TRUE(utils::HasPrivacySensitiveFields(duplicate_result.get())); +} + +IN_PROC_BROWSER_TEST_F(ExtensionTabsTest, DuplicateTabNoPermission) { + static const char kNewBlankTabArgs[] ="about:blank"; + + content::OpenURLParams params(GURL(kNewBlankTabArgs), content::Referrer(), + NEW_FOREGROUND_TAB, + content::PAGE_TRANSITION_LINK, false); + content::WebContents* web_contents = browser()->OpenURL(params); + int tab_id = ExtensionTabUtil::GetTabId(web_contents); + int window_id = ExtensionTabUtil::GetWindowIdOfTab(web_contents); + int tab_index = -1; + TabStripModel* tab_strip; + ExtensionTabUtil::GetTabStripModel(web_contents, &tab_strip, &tab_index); + + scoped_refptr<DuplicateTabFunction> duplicate_tab_function( + new DuplicateTabFunction()); + scoped_refptr<extensions::Extension> empty_extension( + utils::CreateEmptyExtension()); + duplicate_tab_function->set_extension(empty_extension.get()); + duplicate_tab_function->set_has_callback(true); + + scoped_ptr<base::DictionaryValue> duplicate_result(utils::ToDictionary( + utils::RunFunctionAndReturnSingleResult( + duplicate_tab_function.get(), base::StringPrintf("[%u]", tab_id), + browser()))); + + int duplicate_tab_id = utils::GetInteger(duplicate_result.get(), "id"); + int duplicate_tab_window_id = utils::GetInteger(duplicate_result.get(), + "windowId"); + int duplicate_tab_index = utils::GetInteger(duplicate_result.get(), "index"); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, duplicate_result->GetType()); + // Duplicate tab id should be different from the original tab id. + EXPECT_NE(tab_id, duplicate_tab_id); + EXPECT_EQ(window_id, duplicate_tab_window_id); + EXPECT_EQ(tab_index + 1, duplicate_tab_index); + // The test empty extension has no permissions, therefore |duplicate_result| + // should not contain url, title, and faviconUrl in the function result. + EXPECT_FALSE(utils::HasPrivacySensitiveFields(duplicate_result.get())); +} diff --git a/chrome/browser/extensions/extension_function_registry.cc b/chrome/browser/extensions/extension_function_registry.cc index 0817326..dcca8d2 100644 --- a/chrome/browser/extensions/extension_function_registry.cc +++ b/chrome/browser/extensions/extension_function_registry.cc @@ -97,21 +97,22 @@ void ExtensionFunctionRegistry::ResetFunctions() { RegisterFunction<RemoveWindowFunction>(); // Tabs - RegisterFunction<GetTabFunction>(); + RegisterFunction<CaptureVisibleTabFunction>(); + RegisterFunction<CreateTabFunction>(); + RegisterFunction<DetectTabLanguageFunction>(); + RegisterFunction<DuplicateTabFunction>(); + RegisterFunction<GetAllTabsInWindowFunction>(); RegisterFunction<GetCurrentTabFunction>(); RegisterFunction<GetSelectedTabFunction>(); - RegisterFunction<GetAllTabsInWindowFunction>(); - RegisterFunction<QueryTabsFunction>(); + RegisterFunction<GetTabFunction>(); RegisterFunction<HighlightTabsFunction>(); - RegisterFunction<CreateTabFunction>(); - RegisterFunction<UpdateTabFunction>(); RegisterFunction<MoveTabsFunction>(); + RegisterFunction<QueryTabsFunction>(); RegisterFunction<ReloadTabFunction>(); RegisterFunction<RemoveTabsFunction>(); - RegisterFunction<DetectTabLanguageFunction>(); - RegisterFunction<CaptureVisibleTabFunction>(); RegisterFunction<TabsExecuteScriptFunction>(); RegisterFunction<TabsInsertCSSFunction>(); + RegisterFunction<UpdateTabFunction>(); // Page Actions. RegisterFunction<EnablePageActionsFunction>(); diff --git a/chrome/browser/extensions/extension_function_test_utils.cc b/chrome/browser/extensions/extension_function_test_utils.cc index 820fbde..2f8bfc6 100644 --- a/chrome/browser/extensions/extension_function_test_utils.cc +++ b/chrome/browser/extensions/extension_function_test_utils.cc @@ -9,6 +9,7 @@ #include "base/file_path.h" #include "base/json/json_reader.h" #include "base/values.h" +#include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" #include "chrome/browser/ui/browser.h" @@ -18,6 +19,7 @@ using content::WebContents; using extensions::Extension; +namespace keys = extensions::tabs_constants; namespace { @@ -105,20 +107,40 @@ scoped_refptr<Extension> CreateEmptyExtension() { scoped_refptr<Extension> CreateEmptyExtensionWithLocation( Extension::Location location) { - std::string error; - const FilePath test_extension_path; scoped_ptr<base::DictionaryValue> test_extension_value( ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}")); + return CreateExtension(location, test_extension_value.get()); +} + +scoped_refptr<Extension> CreateExtension( + base::DictionaryValue* test_extension_value) { + return CreateExtension(Extension::INTERNAL, test_extension_value); +} + +scoped_refptr<Extension> CreateExtension( + Extension::Location location, + base::DictionaryValue* test_extension_value) { + std::string error; + const FilePath test_extension_path; scoped_refptr<Extension> extension(Extension::Create( test_extension_path, location, - *test_extension_value.get(), + *test_extension_value, Extension::NO_FLAGS, &error)); EXPECT_TRUE(error.empty()) << "Could not parse test extension " << error; return extension; } +bool HasPrivacySensitiveFields(base::DictionaryValue* val) { + std::string result; + if (val->GetString(keys::kUrlKey, &result) || + val->GetString(keys::kTitleKey, &result) || + val->GetString(keys::kFaviconUrlKey, &result)) + return true; + return false; +} + std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function, const std::string& args, Browser* browser) { diff --git a/chrome/browser/extensions/extension_function_test_utils.h b/chrome/browser/extensions/extension_function_test_utils.h index 7bcf84d..cea6e78 100644 --- a/chrome/browser/extensions/extension_function_test_utils.h +++ b/chrome/browser/extensions/extension_function_test_utils.h @@ -54,6 +54,19 @@ scoped_refptr<extensions::Extension> CreateEmptyExtension(); scoped_refptr<extensions::Extension> CreateEmptyExtensionWithLocation( extensions::Extension::Location location); +// Creates an extension instance with a specified extension value that can be +// attached to an ExtensionFunction before running. +scoped_refptr<extensions::Extension> CreateExtension( + base::DictionaryValue* test_extension_value); + +scoped_refptr<extensions::Extension> CreateExtension( + extensions::Extension::Location location, + base::DictionaryValue* test_extension_value); + +// Returns true if |val| contains privacy information, e.g. url, +// title, and faviconUrl. +bool HasPrivacySensitiveFields(base::DictionaryValue* val); + enum RunFunctionFlags { NONE = 0, INCLUDE_INCOGNITO = 1 << 0 diff --git a/chrome/browser/extensions/extension_tabs_apitest.cc b/chrome/browser/extensions/extension_tabs_apitest.cc index 4c921aa..5395466 100644 --- a/chrome/browser/extensions/extension_tabs_apitest.cc +++ b/chrome/browser/extensions/extension_tabs_apitest.cc @@ -79,6 +79,10 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Tabs2) { ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "crud2.html")) << message_; } +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabDuplicate) { + ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "duplicate.html")) << message_; +} + IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabUpdate) { ASSERT_TRUE(RunExtensionSubtest("tabs/basics", "update.html")) << message_; } diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc index 5b166f5..3d88794 100644 --- a/chrome/browser/ui/browser_commands.cc +++ b/chrome/browser/ui/browser_commands.cc @@ -491,7 +491,7 @@ bool CanDuplicateTab(const Browser* browser) { return contents && contents->GetController().GetLastCommittedEntry(); } -void DuplicateTabAt(Browser* browser, int index) { +TabContents* DuplicateTabAt(Browser* browser, int index) { TabContents* contents = GetTabContentsAt(browser, index); CHECK(contents); TabContents* contents_dupe = @@ -541,6 +541,7 @@ void DuplicateTabAt(Browser* browser, int index) { SessionServiceFactory::GetForProfileIfExisting(browser->profile()); if (session_service) session_service->TabRestored(contents_dupe, pinned); + return contents_dupe; } bool CanDuplicateTabAt(Browser* browser, int index) { diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h index 5ac5396..b69f508 100644 --- a/chrome/browser/ui/browser_commands.h +++ b/chrome/browser/ui/browser_commands.h @@ -82,7 +82,7 @@ void SelectNumberedTab(Browser* browser, int index); void SelectLastTab(Browser* browser); void DuplicateTab(Browser* browser); bool CanDuplicateTab(const Browser* browser); -void DuplicateTabAt(Browser* browser, int index); +TabContents* DuplicateTabAt(Browser* browser, int index); bool CanDuplicateTabAt(Browser* browser, int index); void ConvertPopupToTabbedBrowser(Browser* browser); void Exit(); diff --git a/chrome/common/extensions/api/tabs.json b/chrome/common/extensions/api/tabs.json index bacd2c9..7f88a83 100644 --- a/chrome/common/extensions/api/tabs.json +++ b/chrome/common/extensions/api/tabs.json @@ -274,6 +274,32 @@ ] }, { + "name": "duplicate", + "type": "function", + "description": "Duplicates a tab. Note: This function can be used without requesting the 'tabs' permission in the manifest.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "description": "The ID of the tab which is to be duplicated." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + { + "name": "tab", + "optional": true, + "description": "Details about the duplicated tab. The Tab object doesn't contain url, title and faviconUrl if the 'tabs' permission has not been requested.", + "$ref": "Tab" + } + ] + } + ] + }, + { "name": "query", "type": "function", "description": "Gets all tabs that have the specified properties, or all tabs if no properties are specified.", diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index ebe75be..72d04c3 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -602,6 +602,7 @@ TEST(ExtensionTest, ApiPermissions) { { "browserAction.abcd.onClick", true }, // Test Tabs functions. { "tabs.create", true}, + { "tabs.duplicate", true}, { "tabs.onRemoved", true}, { "tabs.remove", true}, { "tabs.update", true}, diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index a884bd0..b87a8b3 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc @@ -770,6 +770,7 @@ TEST(PermissionsTest, DefaultFunctionAccess) { { "browserAction.abcd.onClick", true }, // Test Tabs functions. { "tabs.create", true}, + { "tabs.duplicate", true}, { "tabs.update", true}, { "tabs.getSelected", true}, { "tabs.onUpdated", true }, diff --git a/chrome/test/data/extensions/api_test/tabs/basics/duplicate.html b/chrome/test/data/extensions/api_test/tabs/basics/duplicate.html new file mode 100644 index 0000000..37facd6 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/duplicate.html @@ -0,0 +1,7 @@ +<!-- + * Copyright (c) 2011 The Chromium Authors. All rights reserved. Use of this + * source code is governed by a BSD-style license that can be found in the + * LICENSE file. +--> +<script src="tabs_util.js"></script> +<script src="duplicate.js"></script> diff --git a/chrome/test/data/extensions/api_test/tabs/basics/duplicate.js b/chrome/test/data/extensions/api_test/tabs/basics/duplicate.js new file mode 100644 index 0000000..78bd2e9 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tabs/basics/duplicate.js @@ -0,0 +1,32 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var firstTabId; +var firstWindowId; + +chrome.test.runTests([ + function setupWindow() { + createWindow([pageUrl("a")], {}, + pass(function(winId, tabIds) { + firstWindowId = winId; + firstTabId = tabIds[0]; + })); + }, + + function duplicateTab() { + chrome.tabs.duplicate(firstTabId, pass(function(tab) { + assertEq(pageUrl("a"), tab.url); + assertEq(1, tab.index); + })); + }, + + function totalTab() { + chrome.tabs.getAllInWindow(firstWindowId, + pass(function(tabs) { + assertEq(tabs.length, 2); + assertEq(tabs[0].url, tabs[1].url); + assertEq(tabs[0].index + 1, tabs[1].index); + })); + } +]); |