summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorncj674@motorola.com <ncj674@motorola.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-10 15:09:57 +0000
committerncj674@motorola.com <ncj674@motorola.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-10 15:09:57 +0000
commit37bb582bd18aece7075e55cc4b69ba3f73b725ae (patch)
tree367b9a78e17c753bb985444cebc55149233dd4c8
parent70771b498462cca3a79d0b80810f9afa87e7ffb8 (diff)
downloadchromium_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.cc27
-rw-r--r--chrome/browser/extensions/api/tabs/tabs.h5
-rw-r--r--chrome/browser/extensions/api/tabs/tabs_test.cc83
-rw-r--r--chrome/browser/extensions/extension_function_registry.cc15
-rw-r--r--chrome/browser/extensions/extension_function_test_utils.cc28
-rw-r--r--chrome/browser/extensions/extension_function_test_utils.h13
-rw-r--r--chrome/browser/extensions/extension_tabs_apitest.cc4
-rw-r--r--chrome/browser/ui/browser_commands.cc3
-rw-r--r--chrome/browser/ui/browser_commands.h2
-rw-r--r--chrome/common/extensions/api/tabs.json26
-rw-r--r--chrome/common/extensions/extension_unittest.cc1
-rw-r--r--chrome/common/extensions/permissions/permission_set_unittest.cc1
-rw-r--r--chrome/test/data/extensions/api_test/tabs/basics/duplicate.html7
-rw-r--r--chrome/test/data/extensions/api_test/tabs/basics/duplicate.js32
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);
+ }));
+ }
+]);