diff options
author | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-17 04:55:08 +0000 |
---|---|---|
committer | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-17 04:55:08 +0000 |
commit | 3b3cc646c182ca1578e745c110ba47af34468b15 (patch) | |
tree | 94cc697f442b8032de6032ab41e771da15bca752 | |
parent | 44e13fcffd006cf0640145420f1f2d6f6e4355e3 (diff) | |
download | chromium_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
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> > ' + 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> |