summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-17 04:55:08 +0000
committerrafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-17 04:55:08 +0000
commit3b3cc646c182ca1578e745c110ba47af34468b15 (patch)
tree94cc697f442b8032de6032ab41e771da15bca752
parent44e13fcffd006cf0640145420f1f2d6f6e4355e3 (diff)
downloadchromium_src-3b3cc646c182ca1578e745c110ba47af34468b15.zip
chromium_src-3b3cc646c182ca1578e745c110ba47af34468b15.tar.gz
chromium_src-3b3cc646c182ca1578e745c110ba47af34468b15.tar.bz2
added windows.getWindows() and tabs.moveTab()
Review URL: http://codereview.chromium.org/79026 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13919 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc2
-rw-r--r--chrome/browser/extensions/extension_tabs_module.cc146
-rw-r--r--chrome/browser/extensions/extension_tabs_module.h8
-rwxr-xr-xchrome/renderer/renderer_resources.grd2
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js41
-rw-r--r--chrome/test/data/extensions/test/TabsAPI/1/tabs_api.html180
6 files changed, 316 insertions, 63 deletions
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index a3625ab..e2b05f0 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -45,11 +45,13 @@ FactoryRegistry* FactoryRegistry::instance() {
FactoryRegistry::FactoryRegistry() {
// Register all functions here.
+ factories_["GetWindows"] = &NewExtensionFunction<GetWindowsFunction>;
factories_["GetTabsForWindow"] =
&NewExtensionFunction<GetTabsForWindowFunction>;
factories_["GetTab"] = &NewExtensionFunction<GetTabFunction>;
factories_["CreateTab"] = &NewExtensionFunction<CreateTabFunction>;
factories_["UpdateTab"] = &NewExtensionFunction<UpdateTabFunction>;
+ factories_["MoveTab"] = &NewExtensionFunction<MoveTabFunction>;
factories_["RemoveTab"] = &NewExtensionFunction<RemoveTabFunction>;
}
diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc
index 86fcf49..27cf982 100644
--- a/chrome/browser/extensions/extension_tabs_module.cc
+++ b/chrome/browser/extensions/extension_tabs_module.cc
@@ -11,12 +11,18 @@
#include "chrome/browser/tab_contents/navigation_entry.h"
// Forward declare static helper functions defined below.
+static DictionaryValue* CreateWindowValue(Browser* browser);
+static ListValue* CreateTabList(Browser* browser);
static DictionaryValue* CreateTabValue(TabStripModel* tab_strip_model,
int tab_index);
-
static bool GetIndexOfTabId(const TabStripModel* tab_strip, int tab_id,
int* tab_index);
+// ExtensionTabUtil
+int ExtensionTabUtil::GetWindowId(const Browser* browser) {
+ return browser->session_id().id();
+}
+
int ExtensionTabUtil::GetTabId(const TabContents* tab_contents) {
return tab_contents->controller()->session_id().id();
}
@@ -25,6 +31,47 @@ int ExtensionTabUtil::GetWindowIdOfTab(const TabContents* tab_contents) {
return tab_contents->controller()->window_id().id();
}
+bool GetWindowsFunction::RunImpl() {
+ std::set<int> window_ids;
+
+ // Look for |ids| named parameter as list of id's to fetch.
+ if (args_->IsType(Value::TYPE_DICTIONARY)) {
+ Value *ids_value;
+ if ((!static_cast<DictionaryValue*>(args_)->Get(L"ids", &ids_value)) ||
+ (!ids_value->IsType(Value::TYPE_LIST))) {
+ DCHECK(false);
+ return false;
+ }
+
+ ListValue *window_id_list = static_cast<ListValue*>(ids_value);
+ for (ListValue::iterator id = window_id_list->begin();
+ id != window_id_list->end(); ++id) {
+ int window_id;
+ if (!(*id)->GetAsInteger(&window_id)) {
+ DCHECK(false);
+ return false;
+ }
+
+ window_ids.insert(window_id);
+ }
+ }
+
+ // Default to all windows.
+ bool all_windows = (window_ids.size() == 0);
+
+ result_.reset(new ListValue());
+ for (BrowserList::const_iterator browser = BrowserList::begin();
+ browser != BrowserList::end(); ++browser) {
+ if (all_windows || (window_ids.find(ExtensionTabUtil::GetWindowId(*browser))
+ != window_ids.end())) {
+ static_cast<ListValue*>(result_.get())->Append(
+ CreateWindowValue(*browser));
+ }
+ }
+
+ return true;
+}
+
bool GetTabsForWindowFunction::RunImpl() {
if (!args_->IsType(Value::TYPE_NULL))
return false;
@@ -33,12 +80,7 @@ bool GetTabsForWindowFunction::RunImpl() {
if (!browser)
return false;
- TabStripModel* tab_strip = browser->tabstrip_model();
- result_.reset(new ListValue());
- for (int i = 0; i < tab_strip->count(); ++i) {
- static_cast<ListValue*>(result_.get())->Append(
- CreateTabValue(tab_strip, i));
- }
+ result_.reset(CreateTabList(browser));
return true;
}
@@ -53,7 +95,7 @@ bool CreateTabFunction::RunImpl() {
return false;
TabStripModel *tab_strip = browser->tabstrip_model();
- const DictionaryValue *args_hash = static_cast<const DictionaryValue*>(args_);
+ const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
// TODO(rafaelw): handle setting remaining tab properties:
// -windowId
@@ -61,17 +103,17 @@ bool CreateTabFunction::RunImpl() {
// -favIconUrl
std::string url;
- args_hash->GetString(L"url", &url);
+ args->GetString(L"url", &url);
// Default to foreground for the new tab. The presence of 'selected' property
// will override this default.
bool selected = true;
- args_hash->GetBoolean(L"selected", &selected);
+ args->GetBoolean(L"selected", &selected);
// If index is specified, honor the value, but keep it bound to
// 0 <= index <= tab_strip->count()
int index = -1;
- args_hash->GetInteger(L"index", &index);
+ args->GetInteger(L"index", &index);
if (index < 0) {
// Default insert behavior
index = -1;
@@ -125,8 +167,8 @@ bool UpdateTabFunction::RunImpl() {
return false;
int tab_id;
- const DictionaryValue *args_hash = static_cast<const DictionaryValue*>(args_);
- if (!args_hash->GetInteger(L"id", &tab_id))
+ const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
+ if (!args->GetInteger(L"id", &tab_id))
return false;
int tab_index;
@@ -140,14 +182,12 @@ bool UpdateTabFunction::RunImpl() {
DCHECK(controller);
// TODO(rafaelw): handle setting remaining tab properties:
- // -index
- // -windowId
// -title
// -favIconUrl
// Navigate the tab to a new location if the url different.
std::string url;
- if (args_hash->GetString(L"url", &url)) {
+ if (args->GetString(L"url", &url)) {
GURL new_gurl(url);
if (new_gurl.is_valid()) {
controller->LoadURL(new_gurl, GURL(), PageTransition::TYPED);
@@ -159,7 +199,7 @@ bool UpdateTabFunction::RunImpl() {
bool selected;
// TODO(rafaelw): Setting |selected| from js doesn't make much sense.
// Move tab selection management up to window.
- if (args_hash->GetBoolean(L"selected", &selected) &&
+ if (args->GetBoolean(L"selected", &selected) &&
selected &&
tab_strip->selected_index() != tab_index) {
tab_strip->SelectTabContentsAt(tab_index, false);
@@ -168,6 +208,51 @@ bool UpdateTabFunction::RunImpl() {
return true;
}
+bool MoveTabFunction::RunImpl() {
+ if (!args_->IsType(Value::TYPE_DICTIONARY))
+ return false;
+
+ Browser* browser = BrowserList::GetLastActive();
+ if (!browser)
+ return false;
+
+ int tab_id;
+ const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
+ if (!args->GetInteger(L"id", &tab_id))
+ return false;
+
+ int tab_index;
+ TabStripModel* tab_strip = browser->tabstrip_model();
+ // TODO(rafaelw): return an error if the tab is not found by |tab_id|
+ if (!GetIndexOfTabId(tab_strip, tab_id, &tab_index))
+ return false;
+
+ TabContents* tab_contents = tab_strip->GetTabContentsAt(tab_index);
+ NavigationController* controller = tab_contents->controller();
+ DCHECK(controller);
+
+ // TODO(rafaelw): support moving tabs between windows
+ // -windowId
+
+ int new_index;
+ DCHECK(args->GetInteger(L"index", &new_index));
+ if (new_index < 0) {
+ DCHECK(false);
+ return false;
+ }
+
+ // Clamp move location to the last position.
+ if (new_index >= tab_strip->count()) {
+ new_index = tab_strip->count() - 1;
+ }
+
+ if (new_index != tab_index) {
+ tab_strip->MoveTabContentsAt(tab_index, new_index, false);
+ }
+
+ return true;
+}
+
bool RemoveTabFunction::RunImpl() {
// TODO(rafaelw): This should have a callback, but it can't because it could
@@ -199,6 +284,33 @@ bool RemoveTabFunction::RunImpl() {
}
// static helpers
+static DictionaryValue* CreateWindowValue(Browser* browser) {
+ DictionaryValue* result = new DictionaryValue();
+ result->SetInteger(L"id", ExtensionTabUtil::GetWindowId(browser));
+ result->SetBoolean(L"focused", browser->window()->IsActive());
+ gfx::Rect bounds = browser->window()->GetNormalBounds();
+
+ // TODO(rafaelw): zIndex ?
+ result->SetInteger(L"left", bounds.x());
+ result->SetInteger(L"top", bounds.y());
+ result->SetInteger(L"width", bounds.width());
+ result->SetInteger(L"height", bounds.height());
+
+ result->Set(L"tabs", CreateTabList(browser));
+
+ return result;
+}
+
+static ListValue* CreateTabList(Browser* browser) {
+ ListValue *tab_list = new ListValue();
+ TabStripModel* tab_strip = browser->tabstrip_model();
+ for (int i = 0; i < tab_strip->count(); ++i) {
+ tab_list->Append(CreateTabValue(tab_strip, i));
+ }
+
+ return tab_list;
+}
+
static DictionaryValue* CreateTabValue(TabStripModel* tab_strip,
int tab_index) {
TabContents* contents = tab_strip->GetTabContentsAt(tab_index);
diff --git a/chrome/browser/extensions/extension_tabs_module.h b/chrome/browser/extensions/extension_tabs_module.h
index 3aa4520..5abfab8 100644
--- a/chrome/browser/extensions/extension_tabs_module.h
+++ b/chrome/browser/extensions/extension_tabs_module.h
@@ -7,14 +7,19 @@
#include "chrome/browser/extensions/extension_function.h"
+class Browser;
class TabContents;
class ExtensionTabUtil {
public:
+ static int GetWindowId(const Browser* browser);
static int GetTabId(const TabContents* tab_contents);
static int GetWindowIdOfTab(const TabContents* tab_contents);
};
+class GetWindowsFunction : public SyncExtensionFunction {
+ virtual bool RunImpl();
+};
class GetTabsForWindowFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
@@ -27,6 +32,9 @@ class GetTabFunction : public SyncExtensionFunction {
class UpdateTabFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
+class MoveTabFunction : public SyncExtensionFunction {
+ virtual bool RunImpl();
+};
class RemoveTabFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index c59e98c..cda253b 100755
--- a/chrome/renderer/renderer_resources.grd
+++ b/chrome/renderer/renderer_resources.grd
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This comment is only here because changes to resources are not picked up
-without changes to the corresponding grd file. -->
+without changes to the corresponding grd file. -->
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/renderer_resources.h" type="rc_header">
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index 7b74283..f533228 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -1,10 +1,12 @@
var chromium;
(function() {
native function GetNextCallbackId();
+ native function GetWindows();
native function GetTabsForWindow();
native function GetTab();
native function CreateTab();
native function UpdateTab();
+ native function MoveTab();
native function RemoveTab();
if (!chromium)
@@ -70,6 +72,27 @@ var chromium;
// Tabs
chromium.tabs = {};
+
+ chromium.tabs.getWindows = function(windowQuery, callback) {
+ validate(arguments, arguments.callee.params);
+ sendRequest(GetWindows, windowQuery, callback);
+ };
+ chromium.tabs.getWindows.params = [
+ {
+ type: "object",
+ properties: {
+ ids: {
+ type: "array",
+ items: chromium.types.pInt,
+ minItems: 1
+ }
+ },
+ optional: true,
+ additionalProperties: false
+ },
+ chromium.types.optFun
+ ];
+
// TODO(aa): This should eventually take an optional windowId param.
chromium.tabs.getTabsForWindow = function(callback) {
validate(arguments, arguments.callee.params);
@@ -88,7 +111,7 @@ var chromium;
chromium.types.optFun
];
- chromium.tabs.createTab = function(tab, callback) {
+ chromium.tabs.createTab = function(tab, callback) {
validate(arguments, arguments.callee.params);
sendRequest(CreateTab, tab, callback);
};
@@ -122,6 +145,22 @@ var chromium;
}
];
+ chromium.tabs.moveTab = function(tab) {
+ validate(arguments, arguments.callee.params);
+ sendRequest(MoveTab, tab);
+ };
+ chromium.tabs.moveTab.params = [
+ {
+ type: "object",
+ properties: {
+ id: chromium.types.pInt,
+ windowId: chromium.types.optPInt,
+ index: chromium.types.pInt
+ },
+ additionalProperties: false
+ }
+ ];
+
chromium.tabs.removeTab = function(tabId) {
validate(arguments, arguments.callee.params);
sendRequest(RemoveTab, tabId);
diff --git a/chrome/test/data/extensions/test/TabsAPI/1/tabs_api.html b/chrome/test/data/extensions/test/TabsAPI/1/tabs_api.html
index 948dea4..8f2c277 100644
--- a/chrome/test/data/extensions/test/TabsAPI/1/tabs_api.html
+++ b/chrome/test/data/extensions/test/TabsAPI/1/tabs_api.html
@@ -2,73 +2,165 @@
<head>
<script src="jstemplate_compiled.js" type="text/javascript"></script>
<script>
-var tabs = {};
-function loadTabList() {
- chromium.tabs.getTabsForWindow(function(tabList) {
- tabs = tabList;
+tabs = {};
+tabIds = [];
+
+function loadWindowList() {
+ chromium.tabs.getWindows(undefined, function(windowList) {
+ tabs = {};
+ tabIds = [];
+ for (var i = 0; i < windowList.length; i++) {
+ for (var j = 0; j < windowList[i].tabs.length; j++) {
+ tabIds[tabIds.length] = windowList[i].tabs[j].id;
+ tabs[windowList[i].tabs[j].id] = windowList[i].tabs[j];
+ }
+ }
- var input = new JsExprContext(tabList);
- var output = document.getElementById('tabList');
+ var input = new JsExprContext(windowList);
+ var output = document.getElementById('windowList');
jstProcess(input, output);
});
}
-function getTabData(id) {
+function updateTabData(id) {
+ var retval = {
+ id: id,
+ url: document.getElementById('url_' + id).value,
+ windowId: parseInt(document.getElementById('windowId_' + id).value),
+ selected: document.getElementById('selected_' + id).value ? true : false
+ }
+
+ return retval;
+}
+
+function updateTab(id){
+ chromium.tabs.updateTab(updateTabData(id));
+}
+
+function moveTabData(id) {
+ return {
+ 'id': id,
+ 'index': parseInt(document.getElementById('index_' + id).value),
+ 'windowId': parseInt(document.getElementById('windowId_' + id).value)
+ }
+}
+function moveTab(id) {
+ chromium.tabs.moveTab(moveTabData(id));
+}
+
+function createTabData(id) {
return {
- 'id': id,
- 'index': parseInt(document.getElementById('index_' + id).value),
- 'windowId': document.getElementById('windowId_' + id).value,
- 'title': document.getElementById('title_' + id).value,
+ 'windowId': parseInt(document.getElementById('windowId_' + id).value),
'url': document.getElementById('url_' + id).value,
'selected': document.getElementById('selected_' + id).value ? true : false
}
}
-function updateTab(id){
- chromium.tabs.updateTab(getTabData(id));
-}
-
function createTab() {
- chromium.tabs.createTab(getTabData('new'));
+ chromium.tabs.createTab(createTabData('new'));
}
function updateAll() {
- for (var i = 0; i < tabs.length; i++) {
- chromium.tabs.updateTab(getTabData(tabs[i].id));
+
+ for (var i = 0; i < tabIds.length; i++) {
+ chromium.tabs.updateTab(updateTabData(tabIds[i]));
+ }
+}
+
+function moveAll() {
+ appendToLog('moving all');
+ for (var i = 0; i < tabIds.length; i++) {
+ chromium.tabs.moveTab(moveTabData(tabIds[i]));
}
}
-chromium.tabs.onTabMoved.addListener(function(updateParams) {
- loadTabList();
+function appendToLog(logLine) {
+ var log = document.getElementById('log');
+ log.innerHTML = '<div> &gt;&nbsp;' + logLine + '</div>' + log.innerHTML;
+}
+
+function clearLog() {
+ document.getElementById('log').innerHTML = '';
+}
+
+chromium.tabs.onTabMoved.addListener(function(data) {
+ appendToLog('onTabMoved: ' + data.tabId + ' from ' + data.fromIndex + ' to ' + data.toIndex);
+ loadWindowList();
});
</script>
</head>
- <body onload="loadTabList();">
- <div id="tabList">
- <div style="background-color: #EEEEEE; margin: 8px; padding: 4px" jsselect="$this">
- <div jscontent="'TabId: ' + id"></div>
- <div> index: <input style="width: 90%" type="text" jsvalues="value:$this.index;id:'index_' + id" /></div>
- <div> windowId: <input style="width: 40px" type="text" jsvalues="value:windowId;id:'windowId_' + id" /></div>
- <div> title: <input style="width: 90%" type="text" jsvalues="value:title;id:'title_' + id" /></div>
- <div> url: <input style="width: 90%" type="text" jsvalues="value:url;id:'url_' + id" /></div>
- <div jscontent="url"></div>
- <div> <input type="checkbox" jsvalues="checked:selected; id:'selected_' + id" /> Selected</div>
- <button onclick="updateTab(this.jstdata)" jsvalues=".jstdata:id">Update Tab</button>
- <button onclick="chromium.tabs.removeTab(this.jstdata);" jsvalues=".jstdata:id">Close Tab</button>
- </div>
- </div>
- <div style="background-color: #9999EE; margin: 8px; padding: 4px">
- <div> index: <input style="width: 90%" type="text" id="index_new" /></div>
- <div> windowId: <input style="width: 40px" type="text" id="windowId_new" /></div>
- <div> title: <input style="width: 90%" type="text" id="title_new" /></div>
- <div> url: <input style="width: 90%" type="text" id="url_new" /></div>
- <div> <input type="checkbox" id="selected_new" /> Selected</div>
- <button onclick="createTab();">Create Tab</button>
- </div>
- <button onclick="loadTabList();">Refresh</button>
- <button onclick="updateAll();">Update All</button>
+ <body onload="loadWindowList();">
+ <div id="windowList">
+ <div style="background-color: #AAEEEE; margin: 4px; padding: 8px; margin: 20px" jsselect="$this">
+ <div style="font-style: italic; width: 80px; display: inline-block">
+ Window: <span jscontent="id"></span>
+ </div>
+ <div style="display: inline-block">
+ left: <input style="width: 60px" type="text" jsvalues="value:$this.left;id:'left_' + id" />
+ top: <input style="width: 60px" type="text" jsvalues="value:$this.top;id:'top_' + id" />
+ width: <input style="width: 60px" type="text" jsvalues="value:$this.width;id:'width_' + id" />
+ height: <input style="width: 60px" type="text" jsvalues="value:$this.height;id:'height_' + id" />
+ <input type="checkbox" jsvalues="checked:focused; id:'focused_' + id" /> Focused
+ </div>
+ <div id="tabList">
+ <div style="background-color: #EEEEEE; margin: 8px; padding: 4px" jsselect="tabs">
+ <div style="margin: 8px">
+ <div style="font-style: italic; width: 80px; display: inline-block" jscontent="'TabId: ' + id"></div>
+ <div style="width: 300px; display: inline-block">
+ index: <input style="width: 20px" type="text" jsvalues="value:$this.index;id:'index_' + id" />
+ windowId: <input style="width: 20px" type="text" jsvalues="value:windowId;id:'windowId_' + id" />
+ <button onclick="moveTab(this.jstdata);" jsvalues=".jstdata:id">Move</button>
+ </div>
+ </div>
+ <div style="margin: 8px">
+ <div>
+ <div style="width: 40px; display:inline-block">title:</div>
+ <input style="width: 90%" type="text" jsvalues="value:title;id:'title_' + id" />
+ </div>
+ <div>
+ <div style="width: 40px; display:inline-block">url:</div>
+ <input style="width: 90%" type="text" jsvalues="value:url;id:'url_' + id" />
+ </div>
+ <div><input type="checkbox" jsvalues="checked:selected; id:'selected_' + id" /> Selected</div>
+ </div>
+ <button onclick="updateTab(this.jstdata)" jsvalues=".jstdata:id">Update Tab</button>
+ <button onclick="chromium.tabs.removeTab(this.jstdata);" jsvalues=".jstdata:id">Close Tab</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div style="background-color: #EEEEAA; margin: 20px; padding: 8px">
+ <h3 style="text-align: center; margin: 8px"> Create Tab</h3>
+ <div style="margin: 8px">
+ <div style="width: 300px; display: inline-block">
+ index: <input style="width: 20px" type="text" id="index_new" />
+ windowId: <input style="width: 20px" type="text" id="windowId_new" />
+ <button onclick="moveTab(this.jstdata);" jsvalues=".jstdata:id">Move</button>
+ </div>
+ </div>
+ <div style="margin: 8px">
+ <div>
+ <div style="width: 40px; display:inline-block">title:</div>
+ <input style="width: 90%" type="text" id="title_new" />
+ </div>
+ <div>
+ <div style="width: 40px; display:inline-block">url:</div>
+ <input style="width: 90%" type="text" id="url_new" />
+ </div>
+ <div><input type="checkbox" id="selected_new" /> Selected</div>
+ </div>
+ <button onclick="createTab();">Create</button>
+ </div>
+ <div style="margin: 20px;">
+ <button onclick="loadWindowList();">Refresh</button>
+ <button onclick="updateAll();">Update All</button>
+ <button onclick="moveAll();">Move All</button>
+ <button onclick="clearLog();">-->Clear Log</button>
+ </div>
+ <div id="log" style="background-color: #EEAAEE; margin: 20px; padding: 8px">
+ </div>
</body>
</html>