summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-05 03:05:00 +0000
committerrafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-05 03:05:00 +0000
commite515f5de833d9298682570fd2f70abb16d032912 (patch)
tree05b4a10956097b7e173ebbc8153c5b557180bac8
parent126f4120902008da93759d7256c87e1e6d3ae70b (diff)
downloadchromium_src-e515f5de833d9298682570fd2f70abb16d032912.zip
chromium_src-e515f5de833d9298682570fd2f70abb16d032912.tar.gz
chromium_src-e515f5de833d9298682570fd2f70abb16d032912.tar.bz2
implemented API style/convention changes, including:
-tabs.update() -tabs.move() -tabs.remove() -tabs.update() -tabs.create() -tabs.get() -all tab events, except onUpdated implemented -tabs.getSelected() -tabs.getAllInWindow() -windows.getCurrent() -windows.getFocused() -windows.getAll(populate) -windows.onFocusedChanged() -ExtensionBrowserEventRouter now uses BrowserList::Observer Review URL: http://codereview.chromium.org/100345 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15271 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_browser_event_router.cc106
-rw-r--r--chrome/browser/extensions/extension_browser_event_router.h16
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc16
-rw-r--r--chrome/browser/extensions/extension_tabs_module.cc306
-rw-r--r--chrome/browser/extensions/extension_tabs_module.h23
-rwxr-xr-xchrome/renderer/extensions/extension_api_client_unittest.cc198
-rwxr-xr-xchrome/renderer/renderer_resources.grd2
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js148
-rw-r--r--chrome/test/data/extensions/samples/gmail/gmail_checker.html2
-rw-r--r--chrome/test/data/extensions/samples/subscribe/toolstrip.html2
-rw-r--r--chrome/test/data/extensions/samples/tabs/tabs_api.html203
11 files changed, 725 insertions, 297 deletions
diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc
index fb3d354..f4853ea 100644
--- a/chrome/browser/extensions/extension_browser_event_router.cc
+++ b/chrome/browser/extensions/extension_browser_event_router.cc
@@ -22,6 +22,7 @@ const char* kOnTabDetached = "tab-detached";
const char* kOnTabRemoved = "tab-removed";
const char* kOnWindowCreated = "window-created";
const char* kOnWindowRemoved = "window-removed";
+const char* kOnWindowFocusedChanged = "window-focus-changed";
ExtensionBrowserEventRouter* ExtensionBrowserEventRouter::GetInstance() {
return Singleton<ExtensionBrowserEventRouter>::get();
@@ -34,14 +35,22 @@ static void DispatchEvent(Profile *profile,
DispatchEventToRenderers(event_name, json_args);
}
+static void DispatchSimpleBrowserEvent(Profile *profile, const int window_id,
+ const char* event_name) {
+ ListValue args;
+ args.Append(Value::CreateIntegerValue(window_id));
+
+ std::string json_args;
+ JSONWriter::Write(&args, false, &json_args);
+
+ DispatchEvent(profile, event_name, json_args);
+}
+
void ExtensionBrowserEventRouter::Init() {
if (initialized_)
return;
- NotificationService::current()->AddObserver(this,
- NotificationType::BROWSER_OPENED, NotificationService::AllSources());
- NotificationService::current()->AddObserver(this,
- NotificationType::BROWSER_CLOSED, NotificationService::AllSources());
+ BrowserList::AddObserver(this);
initialized_ = true;
}
@@ -49,75 +58,67 @@ void ExtensionBrowserEventRouter::Init() {
ExtensionBrowserEventRouter::ExtensionBrowserEventRouter()
: initialized_(false) { }
-// NotificationObserver
-void ExtensionBrowserEventRouter::Observe(NotificationType type,
- const NotificationSource& source, const NotificationDetails& details) {
- Browser* browser;
-
- switch (type.value) {
- case(NotificationType::BROWSER_OPENED) :
- browser = Source<Browser>(source).ptr();
- browser->tabstrip_model()->AddObserver(this);
- BrowserOpened(browser);
- break;
- case(NotificationType::BROWSER_CLOSED) :
- browser = Source<Browser>(source).ptr();
- browser->tabstrip_model()->RemoveObserver(this);
- BrowserClosed(browser);
- break;
- default:
- NOTREACHED();
- break;
- }
+void ExtensionBrowserEventRouter::OnBrowserAdded(const Browser* browser) {
+ // Start listening to TabStripModel events for this browser.
+ browser->tabstrip_model()->AddObserver(this);
+
+ DispatchSimpleBrowserEvent(browser->profile(),
+ ExtensionTabUtil::GetWindowId(browser),
+ kOnWindowCreated);
}
-void ExtensionBrowserEventRouter::BrowserOpened(Browser* browser) {
- int window_id = ExtensionTabUtil::GetWindowId(browser);
-
- ListValue args;
- args.Append(Value::CreateIntegerValue(window_id));
-
- std::string json_args;
- JSONWriter::Write(&args, false, &json_args);
+void ExtensionBrowserEventRouter::OnBrowserRemoving(const Browser* browser) {
+ // Stop listening to TabStripModel events for this browser.
+ browser->tabstrip_model()->RemoveObserver(this);
- DispatchEvent(browser->profile(), kOnWindowCreated, json_args);
+ DispatchSimpleBrowserEvent(browser->profile(),
+ ExtensionTabUtil::GetWindowId(browser),
+ kOnWindowRemoved);
}
-void ExtensionBrowserEventRouter::BrowserClosed(Browser* browser) {
- int window_id = ExtensionTabUtil::GetWindowId(browser);
+void ExtensionBrowserEventRouter::OnBrowserSetLastActive(
+ const Browser* browser) {
+ DispatchSimpleBrowserEvent(browser->profile(),
+ ExtensionTabUtil::GetWindowId(browser),
+ kOnWindowFocusedChanged);
+}
+void ExtensionBrowserEventRouter::TabCreatedAt(TabContents* contents,
+ int index,
+ bool foreground) {
ListValue args;
- args.Append(Value::CreateIntegerValue(window_id));
-
+ args.Append(ExtensionTabUtil::CreateTabValue(contents));
std::string json_args;
JSONWriter::Write(&args, false, &json_args);
- DispatchEvent(browser->profile(), kOnWindowRemoved, json_args);
+ DispatchEvent(contents->profile(), kOnTabCreated, json_args);
}
void ExtensionBrowserEventRouter::TabInsertedAt(TabContents* contents,
int index,
bool foreground) {
- const char* event_name = kOnTabAttached;
// If tab is new, send tab-created event.
int tab_id = ExtensionTabUtil::GetTabId(contents);
if (tab_ids_.find(tab_id) == tab_ids_.end()) {
tab_ids_.insert(tab_id);
- event_name = kOnTabCreated;
+
+ TabCreatedAt(contents, index, foreground);
+ return;
}
ListValue args;
+ args.Append(Value::CreateIntegerValue(tab_id));
+
DictionaryValue *object_args = new DictionaryValue();
- object_args->Set(L"tabId", Value::CreateIntegerValue(tab_id));
- object_args->Set(L"windowId", Value::CreateIntegerValue(
+ object_args->Set(L"newWindowId", Value::CreateIntegerValue(
ExtensionTabUtil::GetWindowIdOfTab(contents)));
- object_args->Set(L"index", Value::CreateIntegerValue(index));
+ object_args->Set(L"newPosition", Value::CreateIntegerValue(index));
args.Append(object_args);
std::string json_args;
JSONWriter::Write(&args, false, &json_args);
- DispatchEvent(contents->profile(), event_name, json_args);
+ DispatchEvent(contents->profile(), kOnTabAttached, json_args);
}
void ExtensionBrowserEventRouter::TabDetachedAt(TabContents* contents,
@@ -129,11 +130,12 @@ void ExtensionBrowserEventRouter::TabDetachedAt(TabContents* contents,
}
ListValue args;
+ args.Append(Value::CreateIntegerValue(tab_id));
+
DictionaryValue *object_args = new DictionaryValue();
- object_args->Set(L"tabId", Value::CreateIntegerValue(tab_id));
- object_args->Set(L"windowId", Value::CreateIntegerValue(
+ object_args->Set(L"oldWindowId", Value::CreateIntegerValue(
ExtensionTabUtil::GetWindowIdOfTab(contents)));
- object_args->Set(L"index", Value::CreateIntegerValue(index));
+ object_args->Set(L"oldPosition", Value::CreateIntegerValue(index));
args.Append(object_args);
std::string json_args;
@@ -163,12 +165,12 @@ void ExtensionBrowserEventRouter::TabSelectedAt(TabContents* old_contents,
int index,
bool user_gesture) {
ListValue args;
- DictionaryValue *object_args = new DictionaryValue();
- object_args->Set(L"tabId", Value::CreateIntegerValue(
+ args.Append(Value::CreateIntegerValue(
ExtensionTabUtil::GetTabId(new_contents)));
+
+ DictionaryValue *object_args = new DictionaryValue();
object_args->Set(L"windowId", Value::CreateIntegerValue(
ExtensionTabUtil::GetWindowIdOfTab(new_contents)));
- object_args->Set(L"index", Value::CreateIntegerValue(index));
args.Append(object_args);
std::string json_args;
@@ -181,9 +183,9 @@ void ExtensionBrowserEventRouter::TabMoved(TabContents* contents,
int from_index,
int to_index) {
ListValue args;
+ args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents)));
+
DictionaryValue *object_args = new DictionaryValue();
- object_args->Set(L"tabId", Value::CreateIntegerValue(
- ExtensionTabUtil::GetTabId(contents)));
object_args->Set(L"windowId", Value::CreateIntegerValue(
ExtensionTabUtil::GetWindowIdOfTab(contents)));
object_args->Set(L"fromIndex", Value::CreateIntegerValue(from_index));
diff --git a/chrome/browser/extensions/extension_browser_event_router.h b/chrome/browser/extensions/extension_browser_event_router.h
index 51099fd..afebdd3 100644
--- a/chrome/browser/extensions/extension_browser_event_router.h
+++ b/chrome/browser/extensions/extension_browser_event_router.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "base/singleton.h"
+#include "chrome/browser/browser_list.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/common/notification_observer.h"
@@ -20,7 +21,7 @@
// events from windows/tabs within a profile to extension processes in the same
// profile.
class ExtensionBrowserEventRouter : public TabStripModelObserver,
- public NotificationObserver {
+ public BrowserList::Observer {
public:
// Get Browser-Global instance.
static ExtensionBrowserEventRouter* GetInstance();
@@ -28,7 +29,12 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver,
// Must be called once. Subsequent calls have no effect.
void Init();
- // TabStripModelObserver.
+ // BrowserList::Observer
+ virtual void OnBrowserAdded(const Browser* browser);
+ virtual void OnBrowserRemoving(const Browser* browser);
+ virtual void OnBrowserSetLastActive(const Browser* browser);
+
+ // TabStripModelObserver
void TabInsertedAt(TabContents* contents, int index, bool foreground);
void TabClosingAt(TabContents* contents, int index);
void TabDetachedAt(TabContents* contents, int index);
@@ -51,10 +57,8 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver,
const NotificationSource& source,
const NotificationDetails& details);
private:
- // Construct and dispatch windows.onCreated event.
- void BrowserOpened(Browser* browser);
- // Construct and dispatch windows.onRemoved event.
- void BrowserClosed(Browser* browser);
+ // "Synthetic" event. Called from TabInsertedAt if new tab is detected.
+ void TabCreatedAt(TabContents* contents, int index, bool foreground);
ExtensionBrowserEventRouter();
friend struct DefaultSingletonTraits<ExtensionBrowserEventRouter>;
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 8843ee8..bb093de 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -51,13 +51,21 @@ FactoryRegistry* FactoryRegistry::instance() {
FactoryRegistry::FactoryRegistry() {
// Register all functions here.
- // Tabs.
- factories_["GetWindows"] = &NewExtensionFunction<GetWindowsFunction>;
+ // Windows
+ factories_["GetWindow"] = &NewExtensionFunction<GetWindowFunction>;
+ factories_["GetCurrentWindow"] =
+ &NewExtensionFunction<GetCurrentWindowFunction>;
+ factories_["GetFocusedWindow"] =
+ &NewExtensionFunction<GetFocusedWindowFunction>;
+ factories_["GetAllWindows"] = &NewExtensionFunction<GetAllWindowsFunction>;
factories_["CreateWindow"] = &NewExtensionFunction<CreateWindowFunction>;
factories_["RemoveWindow"] = &NewExtensionFunction<RemoveWindowFunction>;
- factories_["GetTabsForWindow"] =
- &NewExtensionFunction<GetTabsForWindowFunction>;
+
+ // Tabs
factories_["GetTab"] = &NewExtensionFunction<GetTabFunction>;
+ factories_["GetSelectedTab"] = &NewExtensionFunction<GetSelectedTabFunction>;
+ factories_["GetAllTabsInWindow"] =
+ &NewExtensionFunction<GetAllTabsInWindowFunction>;
factories_["CreateTab"] = &NewExtensionFunction<CreateTabFunction>;
factories_["UpdateTab"] = &NewExtensionFunction<UpdateTabFunction>;
factories_["MoveTab"] = &NewExtensionFunction<MoveTabFunction>;
diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc
index 41663dd..d219152 100644
--- a/chrome/browser/extensions/extension_tabs_module.cc
+++ b/chrome/browser/extensions/extension_tabs_module.cc
@@ -18,11 +18,30 @@
#include "chrome/common/temp_scaffolding_stubs.h"
#endif
+namespace {
+// keys
+const wchar_t* kIdKey = L"id";
+const wchar_t* kIndexKey = L"index";
+const wchar_t* kWindowIdKey = L"windowId";
+const wchar_t* kUrlKey = L"url";
+const wchar_t* kTitleKey = L"title";
+const wchar_t* kSelectedKey = L"selected";
+const wchar_t* kFocusedKey = L"focused";
+const wchar_t* kFavIconUrlKey = L"favIconUrl";
+const wchar_t* kLeftKey = L"left";
+const wchar_t* kTopKey = L"top";
+const wchar_t* kWidthKey = L"width";
+const wchar_t* kHeightKey = L"height";
+const wchar_t* kTabsKey = L"tabs";
+}
+
// Forward declare static helper functions defined below.
-static DictionaryValue* CreateWindowValue(Browser* browser);
+static DictionaryValue* CreateWindowValue(Browser* browser, bool populate_tabs);
static ListValue* CreateTabList(Browser* browser);
static bool GetIndexOfTabId(const TabStripModel* tab_strip, int tab_id,
int* tab_index);
+static Browser *GetBrowserInProfileWithId(Profile* profile,
+ const int window_id);
// ExtensionTabUtil
int ExtensionTabUtil::GetWindowId(const Browser* browser) {
@@ -56,53 +75,68 @@ DictionaryValue* ExtensionTabUtil::CreateTabValue(
DictionaryValue* ExtensionTabUtil::CreateTabValue(
const TabContents* contents, TabStripModel* tab_strip, int tab_index) {
DictionaryValue* result = new DictionaryValue();
- result->SetInteger(L"id", ExtensionTabUtil::GetTabId(contents));
- result->SetInteger(L"index", tab_index);
- result->SetInteger(L"windowId", ExtensionTabUtil::GetWindowIdOfTab(contents));
- result->SetString(L"url", contents->GetURL().spec());
- result->SetString(L"title", UTF16ToWide(contents->GetTitle()));
- result->SetBoolean(L"selected",
+ result->SetInteger(kIdKey, ExtensionTabUtil::GetTabId(contents));
+ result->SetInteger(kIndexKey, tab_index);
+ result->SetInteger(kWindowIdKey,
+ ExtensionTabUtil::GetWindowIdOfTab(contents));
+ result->SetString(kUrlKey, contents->GetURL().spec());
+ result->SetString(kTitleKey, UTF16ToWide(contents->GetTitle()));
+ result->SetBoolean(kSelectedKey,
tab_strip && tab_index == tab_strip->selected_index());
NavigationEntry* entry = contents->controller().GetActiveEntry();
if (entry) {
if (entry->favicon().is_valid())
- result->SetString(L"favIconUrl", entry->favicon().url().spec());
+ result->SetString(kFavIconUrlKey, entry->favicon().url().spec());
}
return result;
}
-bool GetWindowsFunction::RunImpl() {
- std::set<int> window_ids;
+static bool GetWindowFunctionHelper(Browser *browser, Profile* profile,
+ scoped_ptr<Value>* result) {
+ // TODO(rafaelw): need "not found" error message.
+ if (browser == NULL || browser->profile() != profile)
+ return false;
- // Look for |ids| named parameter as list of id's to fetch.
- if (args_->IsType(Value::TYPE_DICTIONARY)) {
- ListValue *window_id_list;
- const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
- EXTENSION_FUNCTION_VALIDATE(args->GetList(L"ids", &window_id_list));
- for (ListValue::iterator id = window_id_list->begin();
- id != window_id_list->end(); ++id) {
- int window_id;
- EXTENSION_FUNCTION_VALIDATE((*id)->GetAsInteger(&window_id));
- window_ids.insert(window_id);
- }
- }
+ result->reset(CreateWindowValue(browser, false));
+
+ return true;
+}
+
+// Windows ---------------------------------------------------------------------
- // Default to all windows.
- bool all_windows = (window_ids.size() == 0);
+bool GetWindowFunction::RunImpl() {
+ int window_id;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetAsInteger(&window_id));
+
+ Browser *target = GetBrowserInProfileWithId(profile(), window_id);
+ return GetWindowFunctionHelper(target, profile(), &result_);
+}
+
+bool GetCurrentWindowFunction::RunImpl() {
+ return GetWindowFunctionHelper(dispatcher_->browser(), profile(), &result_);
+}
+
+bool GetFocusedWindowFunction::RunImpl() {
+ return GetWindowFunctionHelper(BrowserList::GetLastActive(), profile(),
+ &result_);
+}
+
+bool GetAllWindowsFunction::RunImpl() {
+ bool populate_tabs = false;
+ if (!args_->IsType(Value::TYPE_NULL)) {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetAsBoolean(&populate_tabs));
+ }
result_.reset(new ListValue());
for (BrowserList::const_iterator browser = BrowserList::begin();
- browser != BrowserList::end(); ++browser) {
- // Only examine browsers in the current profile.
- if ((*browser)->profile() == profile()) {
- if (all_windows || (window_ids.find(ExtensionTabUtil::
- GetWindowId(*browser)) != window_ids.end())) {
- static_cast<ListValue*>(result_.get())->Append(
- CreateWindowValue(*browser));
+ browser != BrowserList::end(); ++browser) {
+ // Only examine browsers in the current profile.
+ if ((*browser)->profile() == profile()) {
+ static_cast<ListValue*>(result_.get())->
+ Append(CreateWindowValue(*browser, populate_tabs));
}
- }
}
return true;
@@ -112,10 +146,12 @@ bool CreateWindowFunction::RunImpl() {
scoped_ptr<GURL> url(new GURL());
// Look for optional url.
- if (args_->IsType(Value::TYPE_DICTIONARY)) {
+ if (!args_->IsType(Value::TYPE_NULL)) {
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
std::string url_input;
- if (args->GetString(L"url", &url_input)) {
+ if (args->HasKey(kUrlKey)) {
+ EXTENSION_FUNCTION_VALIDATE(args->GetString(kUrlKey, &url_input));
url.reset(new GURL(url_input));
if (!url->is_valid()) {
// TODO(rafaelw): need error message/callback here
@@ -143,17 +179,25 @@ bool CreateWindowFunction::RunImpl() {
if (args_->IsType(Value::TYPE_DICTIONARY)) {
const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
int bounds_val;
- if (args->GetInteger(L"left", &bounds_val))
+ if (args->HasKey(kLeftKey)) {
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(kLeftKey, &bounds_val));
bounds.set_x(bounds_val);
+ }
- if (args->GetInteger(L"top", &bounds_val))
+ if (args->HasKey(kTopKey)) {
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(kTopKey, &bounds_val));
bounds.set_y(bounds_val);
+ }
- if (args->GetInteger(L"width", &bounds_val))
+ if (args->HasKey(kWidthKey)) {
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(kWidthKey, &bounds_val));
bounds.set_width(bounds_val);
+ }
- if (args->GetInteger(L"height", &bounds_val))
+ if (args->HasKey(kHeightKey)) {
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(kHeightKey, &bounds_val));
bounds.set_height(bounds_val);
+ }
}
Browser *new_window = Browser::Create(dispatcher_->profile());
@@ -169,7 +213,7 @@ bool CreateWindowFunction::RunImpl() {
// TODO(rafaelw): support |focused|, |zIndex|
- result_.reset(CreateWindowValue(new_window));
+ result_.reset(CreateWindowValue(new_window, false));
return true;
}
@@ -200,12 +244,44 @@ bool RemoveWindowFunction::RunImpl() {
return true;
}
+// Tabs ---------------------------------------------------------------------
-bool GetTabsForWindowFunction::RunImpl() {
- EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_NULL));
+bool GetSelectedTabFunction::RunImpl() {
+ int window_id;
+ Browser *browser;
+ // windowId defaults to "current" window.
+ if (!args_->IsType(Value::TYPE_NULL)) {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetAsInteger(&window_id));
+ browser = GetBrowserInProfileWithId(profile(), window_id);
+ } else {
+ browser = dispatcher_->browser();
+ }
- Browser* browser = dispatcher_->browser();
if (!browser)
+ // TODO(rafaelw): return a "no 'current' browser" error.
+ return false;
+
+ TabStripModel* tab_strip = browser->tabstrip_model();
+ result_.reset(ExtensionTabUtil::CreateTabValue(
+ tab_strip->GetSelectedTabContents(),
+ tab_strip,
+ tab_strip->selected_index()));
+ return true;
+}
+
+bool GetAllTabsInWindowFunction::RunImpl() {
+ int window_id;
+ Browser *browser;
+ // windowId defaults to "current" window.
+ if (!args_->IsType(Value::TYPE_NULL)) {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetAsInteger(&window_id));
+ browser = GetBrowserInProfileWithId(profile(), window_id);
+ } else {
+ browser = dispatcher_->browser();
+ }
+
+ if (!browser)
+ // TODO(rafaelw): return a "no 'current' browser" error.
return false;
result_.reset(CreateTabList(browser));
@@ -217,8 +293,18 @@ bool CreateTabFunction::RunImpl() {
EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
- Browser* browser = BrowserList::GetLastActive();
+ int window_id;
+ Browser *browser;
+ // windowId defaults to "current" window.
+ if (args->HasKey(kWindowIdKey)) {
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(kWindowIdKey, &window_id));
+ browser = GetBrowserInProfileWithId(profile(), window_id);
+ } else {
+ browser = dispatcher_->browser();
+ }
+
if (!browser)
+ // TODO(rafaelw): return a "no 'current' browser" error.
return false;
TabStripModel *tab_strip = browser->tabstrip_model();
@@ -228,18 +314,29 @@ bool CreateTabFunction::RunImpl() {
// -title
// -favIconUrl
- std::string url;
- args->GetString(L"url", &url);
+ std::string url_string;
+ scoped_ptr<GURL> url;
+ if (args->HasKey(kUrlKey)) {
+ EXTENSION_FUNCTION_VALIDATE(args->GetString(kUrlKey, &url_string));
+ url.reset(new GURL(url_string));
+
+ // TODO(rafaelw): return an "invalid url" error.
+ if (!url->is_valid())
+ return false;
+ }
// Default to foreground for the new tab. The presence of 'selected' property
// will override this default.
bool selected = true;
- args->GetBoolean(L"selected", &selected);
+ if (args->HasKey(kSelectedKey))
+ EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(kSelectedKey, &selected));
// If index is specified, honor the value, but keep it bound to
// 0 <= index <= tab_strip->count()
int index = -1;
- args->GetInteger(L"index", &index);
+ if (args->HasKey(kIndexKey))
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(kIndexKey, &index));
+
if (index < 0) {
// Default insert behavior
index = -1;
@@ -248,7 +345,7 @@ bool CreateTabFunction::RunImpl() {
index = tab_strip->count();
}
- TabContents* contents = browser->AddTabWithURL(GURL(url), GURL(),
+ TabContents* contents = browser->AddTabWithURL(*(url.get()), GURL(),
PageTransition::LINK, selected, index, NULL);
index = tab_strip->GetIndexOfTabContents(contents);
@@ -263,27 +360,41 @@ bool GetTabFunction::RunImpl() {
int tab_id;
EXTENSION_FUNCTION_VALIDATE(args_->GetAsInteger(&tab_id));
- Browser* browser = BrowserList::GetLastActive();
- if (!browser)
- return false;
+ int tab_index = -1;
+ Browser *target = NULL;
+ TabStripModel *tab_strip = NULL;
+ for (BrowserList::const_iterator browser = BrowserList::begin();
+ browser != BrowserList::end(); ++browser) {
+ if ((*browser)->profile() == profile()) {
+ target = *browser;
+ tab_strip = (*browser)->tabstrip_model();
+ if (GetIndexOfTabId(tab_strip, tab_id, &tab_index)) {
+ break;
+ }
+ }
+ }
- 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))
+ if (target == NULL || tab_index == -1) {
+ // TODO(rafaelw): need "not found" error message.
return false;
+ }
- result_.reset(ExtensionTabUtil::CreateTabValue(
- tab_strip->GetTabContentsAt(tab_index), tab_strip, tab_index));
+ TabContents *contents = target->tabstrip_model()->GetTabContentsAt(tab_index);
+ result_.reset(ExtensionTabUtil::CreateTabValue(contents, tab_strip,
+ tab_index));
return true;
}
bool UpdateTabFunction::RunImpl() {
int tab_id;
- EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
- const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
- EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"id", &tab_id));
-
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST));
+ const ListValue *args = static_cast<const ListValue*>(args_);
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(0, &tab_id));
+ DictionaryValue *update_props;
+ EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(1, &update_props));
+
+ // TODO(rafaelw): Need to search for appropriate browser that contains
+ // |tab_id|.
Browser* browser = BrowserList::GetLastActive();
if (!browser)
return false;
@@ -303,22 +414,26 @@ bool UpdateTabFunction::RunImpl() {
// Navigate the tab to a new location if the url different.
std::string url;
- if (args->GetString(L"url", &url)) {
+ if (update_props->HasKey(kUrlKey)) {
+ EXTENSION_FUNCTION_VALIDATE(update_props->GetString(kUrlKey, &url));
GURL new_gurl(url);
- if (new_gurl.is_valid()) {
- controller.LoadURL(new_gurl, GURL(), PageTransition::LINK);
- } else {
- // TODO(rafaelw): return some reasonable error?
- }
+
+ // TODO(rafaelw): return "invalid url" here.
+ if (!new_gurl.is_valid())
+ return false;
+
+ controller.LoadURL(new_gurl, GURL(), PageTransition::LINK);
}
- bool selected;
+ bool selected = false;
// TODO(rafaelw): Setting |selected| from js doesn't make much sense.
// Move tab selection management up to window.
- if (args->GetBoolean(L"selected", &selected) &&
- selected &&
- tab_strip->selected_index() != tab_index) {
- tab_strip->SelectTabContentsAt(tab_index, false);
+ if (update_props->HasKey(kSelectedKey)) {
+ EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(kSelectedKey,
+ &selected));
+ if (selected && tab_strip->selected_index() != tab_index) {
+ tab_strip->SelectTabContentsAt(tab_index, false);
+ }
}
return true;
@@ -326,20 +441,23 @@ bool UpdateTabFunction::RunImpl() {
bool MoveTabFunction::RunImpl() {
int tab_id;
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST));
+ const ListValue *args = static_cast<const ListValue*>(args_);
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(0, &tab_id));
+ DictionaryValue *update_props;
+ EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(1, &update_props));
+
int new_index;
- EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
- const DictionaryValue *args = static_cast<const DictionaryValue*>(args_);
- EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"id", &tab_id));
- EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"index", &new_index));
+ EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(kIndexKey, &new_index));
EXTENSION_FUNCTION_VALIDATE(new_index >= 0);
+ // TODO(rafaelw): need to support finding the tab by id in any browser,
+ // not just the currently active browser.
Browser* browser = BrowserList::GetLastActive();
if (!browser)
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;
@@ -380,19 +498,24 @@ bool RemoveTabFunction::RunImpl() {
}
// static helpers
-static DictionaryValue* CreateWindowValue(Browser* browser) {
+// if |populate| is true, each window gets a list property |tabs| which contains
+// fully populated tab objects.
+static DictionaryValue* CreateWindowValue(Browser* browser,
+ bool populate_tabs) {
DictionaryValue* result = new DictionaryValue();
- result->SetInteger(L"id", ExtensionTabUtil::GetWindowId(browser));
- result->SetBoolean(L"focused", browser->window()->IsActive());
+ result->SetInteger(kIdKey, ExtensionTabUtil::GetWindowId(browser));
+ result->SetBoolean(kFocusedKey, 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->SetInteger(kLeftKey, bounds.x());
+ result->SetInteger(kTopKey, bounds.y());
+ result->SetInteger(kWidthKey, bounds.width());
+ result->SetInteger(kHeightKey, bounds.height());
- result->Set(L"tabs", CreateTabList(browser));
+ if (populate_tabs) {
+ result->Set(kTabsKey, CreateTabList(browser));
+ }
return result;
}
@@ -419,3 +542,14 @@ static bool GetIndexOfTabId(const TabStripModel* tab_strip, int tab_id,
}
return false;
}
+
+static Browser *GetBrowserInProfileWithId(Profile* profile,
+ const int window_id) {
+ for (BrowserList::const_iterator browser = BrowserList::begin();
+ browser != BrowserList::end(); ++browser) {
+ if ((*browser)->profile() == profile &&
+ ExtensionTabUtil::GetWindowId(*browser) == window_id)
+ return *browser;
+ }
+ return NULL;
+}
diff --git a/chrome/browser/extensions/extension_tabs_module.h b/chrome/browser/extensions/extension_tabs_module.h
index c139f6a..35f59b42 100644
--- a/chrome/browser/extensions/extension_tabs_module.h
+++ b/chrome/browser/extensions/extension_tabs_module.h
@@ -22,7 +22,17 @@ class ExtensionTabUtil {
const TabContents* tab_contents, TabStripModel* tab_strip, int tab_index);
};
-class GetWindowsFunction : public SyncExtensionFunction {
+// Windows
+class GetWindowFunction : public SyncExtensionFunction {
+ virtual bool RunImpl();
+};
+class GetCurrentWindowFunction : public SyncExtensionFunction {
+ virtual bool RunImpl();
+};
+class GetFocusedWindowFunction : public SyncExtensionFunction {
+ virtual bool RunImpl();
+};
+class GetAllWindowsFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
class CreateWindowFunction : public SyncExtensionFunction {
@@ -31,13 +41,18 @@ class CreateWindowFunction : public SyncExtensionFunction {
class RemoveWindowFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
-class GetTabsForWindowFunction : public SyncExtensionFunction {
+
+// Tabs
+class GetTabFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
-class CreateTabFunction : public SyncExtensionFunction {
+class GetSelectedTabFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
-class GetTabFunction : public SyncExtensionFunction {
+class GetAllTabsInWindowFunction : public SyncExtensionFunction {
+ virtual bool RunImpl();
+};
+class CreateTabFunction : public SyncExtensionFunction {
virtual bool RunImpl();
};
class UpdateTabFunction : public SyncExtensionFunction {
diff --git a/chrome/renderer/extensions/extension_api_client_unittest.cc b/chrome/renderer/extensions/extension_api_client_unittest.cc
index 172263a..6233dfb 100755
--- a/chrome/renderer/extensions/extension_api_client_unittest.cc
+++ b/chrome/renderer/extensions/extension_api_client_unittest.cc
@@ -75,7 +75,7 @@ TEST_F(ExtensionAPIClientTest, CallbackDispatching) {
" 'incorrect result');"
" console.log('pass')"
"}"
- "chromium.tabs.createTab({}, callback);"
+ "chromium.tabs.create({}, callback);"
);
EXPECT_EQ("", GetConsoleMessage());
@@ -102,72 +102,210 @@ TEST_F(ExtensionAPIClientTest, CallbackDispatching) {
// extension functions. We test both error and success conditions, but do not
// test errors exhaustively as json schema code is well tested by itself.
-TEST_F(ExtensionAPIClientTest, GetTabsForWindow) {
- ExpectJsFail("chromium.tabs.getTabsForWindow(42, function(){});",
+// Window API tests
+
+TEST_F(ExtensionAPIClientTest, GetWindow) {
+ ExpectJsFail("chromium.windows.get(32, function(){}, 20);",
"Uncaught Error: Too many arguments.");
- ExpectJsPass("chromium.tabs.getTabsForWindow(function(){})",
- "GetTabsForWindow", "null");
+ ExpectJsFail("chromium.windows.get(32);",
+ "Uncaught Error: Parameter 1 is required.");
+
+ ExpectJsFail("chromium.windows.get('abc', function(){});",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'integer' but got 'string'.");
+
+ ExpectJsFail("chromium.windows.get(1, 1);",
+ "Uncaught Error: Invalid value for argument 1. "
+ "Expected 'function' but got 'integer'.");
+
+ ExpectJsPass("chromium.windows.get(2, function(){})",
+ "GetWindow", "2");
}
-TEST_F(ExtensionAPIClientTest, GetTab) {
- ExpectJsFail("chromium.tabs.getTab(null, function(){});",
+TEST_F(ExtensionAPIClientTest, GetCurentWindow) {
+ ExpectJsFail("chromium.windows.getCurrent(function(){}, 20);",
+ "Uncaught Error: Too many arguments.");
+
+ ExpectJsFail("chromium.windows.getCurrent();",
+ "Uncaught Error: Parameter 0 is required.");
+
+ ExpectJsFail("chromium.windows.getCurrent('abc');",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'function' but got 'string'.");
+
+ ExpectJsPass("chromium.windows.getCurrent(function(){})",
+ "GetCurrentWindow", "null");
+}
+
+TEST_F(ExtensionAPIClientTest, GetFocusedWindow) {
+ ExpectJsFail("chromium.windows.getFocused(function(){}, 20);",
+ "Uncaught Error: Too many arguments.");
+
+ ExpectJsFail("chromium.windows.getFocused();",
"Uncaught Error: Parameter 0 is required.");
- ExpectJsPass("chromium.tabs.getTab(42)", "GetTab", "42");
+ ExpectJsFail("chromium.windows.getFocused('abc');",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'function' but got 'string'.");
+
+ ExpectJsPass("chromium.windows.getFocused(function(){})",
+ "GetFocusedWindow", "null");
+}
+
+TEST_F(ExtensionAPIClientTest, GetAllWindows) {
+ ExpectJsFail("chromium.windows.getAll(true, function(){}, 20);",
+ "Uncaught Error: Too many arguments.");
+
+ ExpectJsFail("chromium.windows.getAll(1, function(){});",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'boolean' but got 'integer'.");
+
+ ExpectJsPass("chromium.windows.getAll(true, function(){})",
+ "GetAllWindows", "true");
+
+ ExpectJsPass("chromium.windows.getAll(null, function(){})",
+ "GetAllWindows", "null");
+
+ ExpectJsPass("chromium.windows.getAll(undefined, function(){})",
+ "GetAllWindows", "null");
+}
+
+// Tab API tests
+
+TEST_F(ExtensionAPIClientTest, GetTab) {
+ ExpectJsFail("chromium.tabs.get(32, function(){}, 20);",
+ "Uncaught Error: Too many arguments.");
+
+ ExpectJsFail("chromium.tabs.get(32);",
+ "Uncaught Error: Parameter 1 is required.");
+
+ ExpectJsFail("chromium.tabs.get('abc', function(){});",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'integer' but got 'string'.");
+
+ ExpectJsFail("chromium.tabs.get(1, 1);",
+ "Uncaught Error: Invalid value for argument 1. "
+ "Expected 'function' but got 'integer'.");
+
+ ExpectJsPass("chromium.tabs.get(2, function(){})",
+ "GetTab", "2");
+}
+
+TEST_F(ExtensionAPIClientTest, GetSelectedTab) {
+ ExpectJsFail("chromium.tabs.getSelected(32, function(){}, 20);",
+ "Uncaught Error: Too many arguments.");
+
+ ExpectJsFail("chromium.tabs.getSelected(32);",
+ "Uncaught Error: Parameter 1 is required.");
+
+ ExpectJsFail("chromium.tabs.getSelected('abc', function(){});",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'integer' but got 'string'.");
+
+ ExpectJsFail("chromium.tabs.getSelected(1, 1);",
+ "Uncaught Error: Invalid value for argument 1. "
+ "Expected 'function' but got 'integer'.");
+
+ ExpectJsPass("chromium.tabs.getSelected(2, function(){})",
+ "GetSelectedTab", "2");
+
+ ExpectJsPass("chromium.tabs.getSelected(null, function(){})",
+ "GetSelectedTab", "null");
+}
+
+
+TEST_F(ExtensionAPIClientTest, GetAllTabsInWindow) {
+ ExpectJsFail("chromium.tabs.getAllInWindow(42, function(){}, 'asd');",
+ "Uncaught Error: Too many arguments.");
+
+ ExpectJsFail("chromium.tabs.getAllInWindow(32);",
+ "Uncaught Error: Parameter 1 is required.");
+
+ ExpectJsFail("chromium.tabs.getAllInWindow(1, 1);",
+ "Uncaught Error: Invalid value for argument 1. "
+ "Expected 'function' but got 'integer'.");
+
+ ExpectJsFail("chromium.tabs.getAllInWindow('asd', function(){});",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'integer' but got 'string'.");
+
+ ExpectJsPass("chromium.tabs.getAllInWindow(32, function(){})",
+ "GetAllTabsInWindow", "32");
+
+ ExpectJsPass("chromium.tabs.getAllInWindow(undefined, function(){})",
+ "GetAllTabsInWindow", "null");
}
TEST_F(ExtensionAPIClientTest, CreateTab) {
- ExpectJsFail("chromium.tabs.createTab({windowId: 'foo'}, function(){});",
+ ExpectJsFail("chromium.tabs.create({windowId: 'foo'}, function(){});",
"Uncaught Error: Invalid value for argument 0. Property "
"'windowId': Expected 'integer' but got 'string'.");
- ExpectJsFail("chromium.tabs.createTab({url: 42}, function(){});",
+ ExpectJsFail("chromium.tabs.create({url: 42}, function(){});",
"Uncaught Error: Invalid value for argument 0. Property "
"'url': Expected 'string' but got 'integer'.");
- ExpectJsFail("chromium.tabs.createTab({foo: 42}, function(){});",
+ ExpectJsFail("chromium.tabs.create({foo: 42}, function(){});",
"Uncaught Error: Invalid value for argument 0. Property "
"'foo': Unexpected property.");
- ExpectJsPass("chromium.tabs.createTab({"
+ ExpectJsPass("chromium.tabs.create({"
" url:'http://www.google.com/',"
" selected:true,"
+ " index: 2,"
" windowId:4"
"})",
"CreateTab",
"{\"url\":\"http://www.google.com/\","
"\"selected\":true,"
+ "\"index\":2,"
"\"windowId\":4}");
}
TEST_F(ExtensionAPIClientTest, UpdateTab) {
- ExpectJsFail("chromium.tabs.updateTab({id: null});",
- "Uncaught Error: Invalid value for argument 0. Property "
- "'id': Property is required.");
- ExpectJsFail("chromium.tabs.updateTab({id: 42, windowId: 'foo'});",
- "Uncaught Error: Invalid value for argument 0. Property "
- "'windowId': Expected 'integer' but got 'string'.");
- ExpectJsFail("chromium.tabs.updateTab({id: 42, url: 42});",
- "Uncaught Error: Invalid value for argument 0. Property "
+ ExpectJsFail("chromium.tabs.update(null);",
+ "Uncaught Error: Parameter 0 is required.");
+ ExpectJsFail("chromium.tabs.update(42, {selected: 'foo'});",
+ "Uncaught Error: Invalid value for argument 1. Property "
+ "'selected': Expected 'boolean' but got 'string'.");
+ ExpectJsFail("chromium.tabs.update(42, {url: 42});",
+ "Uncaught Error: Invalid value for argument 1. Property "
"'url': Expected 'string' but got 'integer'.");
- ExpectJsPass("chromium.tabs.updateTab({"
- " id:42,"
+ ExpectJsPass("chromium.tabs.update(42, {"
" url:'http://www.google.com/',"
- " selected:true,"
- " windowId:4"
+ " selected:true"
"})",
"UpdateTab",
- "{\"id\":42,"
- "\"url\":\"http://www.google.com/\","
- "\"selected\":true,"
- "\"windowId\":4}");
+ "[42,"
+ "{\"url\":\"http://www.google.com/\","
+ "\"selected\":true}]");
+}
+
+TEST_F(ExtensionAPIClientTest, MoveTab) {
+ ExpectJsFail("chromium.tabs.move(null);",
+ "Uncaught Error: Parameter 0 is required.");
+ ExpectJsFail("chromium.tabs.move(42, {index: 'foo'});",
+ "Uncaught Error: Invalid value for argument 1. Property "
+ "'index': Expected 'integer' but got 'string'.");
+ ExpectJsFail("chromium.tabs.move(42, {index: 3, windowId: 'foo'});",
+ "Uncaught Error: Invalid value for argument 1. Property "
+ "'windowId': Expected 'integer' but got 'string'.");
+
+ ExpectJsPass("chromium.tabs.move(42, {"
+ " index:3,"
+ " windowId:21"
+ "})",
+ "MoveTab",
+ "[42,"
+ "{\"index\":3,"
+ "\"windowId\":21}]");
}
TEST_F(ExtensionAPIClientTest, RemoveTab) {
- ExpectJsFail("chromium.tabs.removeTab('foobar', function(){});",
+ ExpectJsFail("chromium.tabs.remove('foobar', function(){});",
"Uncaught Error: Too many arguments.");
- ExpectJsPass("chromium.tabs.removeTab(21)", "RemoveTab", "21");
+ ExpectJsPass("chromium.tabs.remove(21)", "RemoveTab", "21");
}
// Bookmark API tests
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index cda253b..c59e98c 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 00bb0fa..d1907d8 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -10,11 +10,15 @@
var chromium;
(function() {
native function GetNextCallbackId();
+ native function GetWindow();
+ native function GetCurrentWindow();
+ native function GetFocusedWindow();
native function CreateWindow();
native function RemoveWindow();
- native function GetWindows();
- native function GetTabsForWindow();
+ native function GetAllWindows();
native function GetTab();
+ native function GetSelectedTab();
+ native function GetAllTabsInWindow();
native function CreateTab();
native function UpdateTab();
native function MoveTab();
@@ -95,24 +99,42 @@ var chromium;
// Windows.
chromium.windows = {};
- chromium.windows.getWindows = function(windowQuery, callback) {
+ chromium.windows.get = function(windowId, callback) {
validate(arguments, arguments.callee.params);
- sendRequest(GetWindows, windowQuery, callback);
+ sendRequest(GetWindow, windowId, callback);
};
- chromium.windows.getWindows.params = [
- {
- type: "object",
- properties: {
- ids: {
- type: "array",
- items: chromium.types.pInt,
- minItems: 1
- }
- },
- optional: true
- },
- chromium.types.optFun
+ chromium.windows.get.params = [
+ chromium.types.pInt,
+ chromium.types.fun
+ ];
+
+ chromium.windows.getCurrent = function(callback) {
+ validate(arguments, arguments.callee.params);
+ sendRequest(GetCurrentWindow, null, callback);
+ };
+
+ chromium.windows.getCurrent.params = [
+ chromium.types.fun
+ ];
+
+ chromium.windows.getFocused = function(callback) {
+ validate(arguments, arguments.callee.params);
+ sendRequest(GetFocusedWindow, null, callback);
+ };
+
+ chromium.windows.getFocused.params = [
+ chromium.types.fun
+ ];
+
+ chromium.windows.getAll = function(populate, callback) {
+ validate(arguments, arguments.callee.params);
+ sendRequest(GetAllWindows, populate, callback);
+ };
+
+ chromium.windows.getAll.params = [
+ chromium.types.optBool,
+ chromium.types.fun
];
chromium.windows.createWindow = function(createData, callback) {
@@ -146,49 +168,64 @@ var chromium;
// sends (windowId).
// *WILL* be followed by tab-attached AND then tab-selection-changed.
- chromium.windows.onWindowCreated = new chromium.Event("window-created");
+ chromium.windows.onCreated = new chromium.Event("window-created");
// sends (windowId).
// *WILL* be preceded by sequences of tab-removed AND then
// tab-selection-changed -- one for each tab that was contained in the window
// that closed
- chromium.windows.onWindowRemoved = new chromium.Event("window-removed");
+ chromium.windows.onRemoved = new chromium.Event("window-removed");
+
+ // sends (windowId).
+ chromium.windows.onFocusChanged =
+ new chromium.Event("window-focus-changed");
//----------------------------------------------------------------------------
// Tabs
chromium.tabs = {};
- // TODO(aa): This should eventually take an optional windowId param.
- chromium.tabs.getTabsForWindow = function(callback) {
+ chromium.tabs.get = function(tabId, callback) {
validate(arguments, arguments.callee.params);
- sendRequest(GetTabsForWindow, null, callback);
+ sendRequest(GetTab, tabId, callback);
};
- chromium.tabs.getTabsForWindow.params = [
- chromium.types.optFun
+ chromium.tabs.get.params = [
+ chromium.types.pInt,
+ chromium.types.fun
+ ];
+
+ chromium.tabs.getSelected = function(windowId, callback) {
+ validate(arguments, arguments.callee.params);
+ sendRequest(GetSelectedTab, windowId, callback);
+ };
+
+ chromium.tabs.getSelected.params = [
+ chromium.types.optPInt,
+ chromium.types.fun
];
- chromium.tabs.getTab = function(tabId, callback) {
+ chromium.tabs.getAllInWindow = function(windowId, callback) {
validate(arguments, arguments.callee.params);
- sendRequest(GetTab, tabId, callback);
+ sendRequest(GetAllTabsInWindow, windowId, callback);
};
- chromium.tabs.getTab.params = [
- chromium.types.pInt,
- chromium.types.optFun
+ chromium.tabs.getAllInWindow.params = [
+ chromium.types.optPInt,
+ chromium.types.fun
];
- chromium.tabs.createTab = function(tab, callback) {
+ chromium.tabs.create = function(tab, callback) {
validate(arguments, arguments.callee.params);
sendRequest(CreateTab, tab, callback);
};
- chromium.tabs.createTab.params = [
+ chromium.tabs.create.params = [
{
type: "object",
properties: {
windowId: chromium.types.optPInt,
+ index: chromium.types.optPInt,
url: chromium.types.optStr,
selected: chromium.types.optBool
}
@@ -196,73 +233,74 @@ var chromium;
chromium.types.optFun
];
- chromium.tabs.updateTab = function(tab) {
+ chromium.tabs.update = function(tabId, updates, callback) {
validate(arguments, arguments.callee.params);
- sendRequest(UpdateTab, tab);
+ sendRequest(UpdateTab, [tabId, updates], callback);
};
- chromium.tabs.updateTab.params = [
+ chromium.tabs.update.params = [
+ chromium.types.pInt,
{
type: "object",
properties: {
- id: chromium.types.pInt,
- windowId: chromium.types.optPInt,
url: chromium.types.optStr,
selected: chromium.types.optBool
}
- }
+ },
+ chromium.types.optFun
];
- chromium.tabs.moveTab = function(tab) {
+ chromium.tabs.move = function(tabId, moveProps, callback) {
validate(arguments, arguments.callee.params);
- sendRequest(MoveTab, tab);
+ sendRequest(MoveTab, [tabId, moveProps], callback);
};
- chromium.tabs.moveTab.params = [
+ chromium.tabs.move.params = [
+ chromium.types.pInt,
{
type: "object",
properties: {
- id: chromium.types.pInt,
windowId: chromium.types.optPInt,
index: chromium.types.pInt
}
- }
+ },
+ chromium.types.optFun
];
- chromium.tabs.removeTab = function(tabId) {
+ chromium.tabs.remove = function(tabId) {
validate(arguments, arguments.callee.params);
sendRequest(RemoveTab, tabId);
};
- chromium.tabs.removeTab.params = [
+ chromium.tabs.remove.params = [
chromium.types.pInt
];
- // Sends ({tabId, windowId, index}).
+ // Sends ({Tab}).
// Will *NOT* be followed by tab-attached - it is implied.
// *MAY* be followed by tab-selection-changed.
- chromium.tabs.onTabCreated = new chromium.Event("tab-created");
+ chromium.tabs.onCreated = new chromium.Event("tab-created");
- // Wends ({tabId, windowId, fromIndex, toIndex}).
+ // Sends (tabId, {windowId, fromIndex, toIndex}).
// Tabs can only "move" within a window.
- chromium.tabs.onTabMoved = new chromium.Event("tab-moved");
+ chromium.tabs.onMoved = new chromium.Event("tab-moved");
- // Sends ({tabId, windowId, index}).
- chromium.tabs.onTabSelectionChanged =
+ // Sends (tabId, {windowId}).
+ chromium.tabs.onSelectionChanged =
new chromium.Event("tab-selection-changed");
- // Sends ({tabId, windowId, index}).
+ // Sends (tabId, {newWindowId, newPosition}).
// *MAY* be followed by tab-selection-changed.
- chromium.tabs.onTabAttached = new chromium.Event("tab-attached");
+ chromium.tabs.onAttached = new chromium.Event("tab-attached");
- // Sends ({tabId, windowId, index}).
+ // Sends (tabId, {oldWindowId, oldPosition}).
// *WILL* be followed by tab-selection-changed.
- chromium.tabs.onTabDetached = new chromium.Event("tab-detached");
+ chromium.tabs.onDetached = new chromium.Event("tab-detached");
// Sends (tabId).
// *WILL* be followed by tab-selection-changed.
// Will *NOT* be followed or preceded by tab-detached.
- chromium.tabs.onTabRemoved = new chromium.Event("tab-removed");
+ chromium.tabs.onRemoved = new chromium.Event("tab-removed");
//----------------------------------------------------------------------------
diff --git a/chrome/test/data/extensions/samples/gmail/gmail_checker.html b/chrome/test/data/extensions/samples/gmail/gmail_checker.html
index e52d78d..f754ad7 100644
--- a/chrome/test/data/extensions/samples/gmail/gmail_checker.html
+++ b/chrome/test/data/extensions/samples/gmail/gmail_checker.html
@@ -86,7 +86,7 @@ function requestUnreadFeed() {
}
function goToInbox() {
- chromium.tabs.createTab({url:"http://www.gmail.com/"});
+ chromium.tabs.create({url:"http://www.gmail.com/"});
}
</script>
diff --git a/chrome/test/data/extensions/samples/subscribe/toolstrip.html b/chrome/test/data/extensions/samples/subscribe/toolstrip.html
index 6822215..cc6ca3a 100644
--- a/chrome/test/data/extensions/samples/subscribe/toolstrip.html
+++ b/chrome/test/data/extensions/samples/subscribe/toolstrip.html
@@ -45,7 +45,7 @@
function handleClick() {
if (enabled) {
var url = "http://www.google.com/reader/view/feed/" + feedUrl;
- chromium.tabs.createTab({url: url});
+ chromium.tabs.create({url: url});
}
}
</script>
diff --git a/chrome/test/data/extensions/samples/tabs/tabs_api.html b/chrome/test/data/extensions/samples/tabs/tabs_api.html
index 022d1a7..abcf802 100644
--- a/chrome/test/data/extensions/samples/tabs/tabs_api.html
+++ b/chrome/test/data/extensions/samples/tabs/tabs_api.html
@@ -6,16 +6,32 @@
tabs = {};
tabIds = [];
+focusedWindowId = undefined;
+currentWindowId = undefined;
+
+function bootStrap() {
+ chromium.windows.getCurrent(function(currentWindow) {
+ currentWindowId = currentWindow.id;
+ chromium.windows.getFocused(function(focusedWindow) {
+ focusedWindowId = focusedWindow.id;
+ loadWindowList();
+ });
+ });
+}
+
function loadWindowList() {
- chromium.windows.getWindows(undefined, function(windowList) {
+ chromium.windows.getAll(true, 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];
- }
- }
+ windowList[i].current = (windowList[i].id == currentWindowId);
+ windowList[i].focused = (windowList[i].id == focusedWindowId);
+
+ 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(windowList);
var output = document.getElementById('windowList');
@@ -25,9 +41,7 @@ function loadWindowList() {
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
}
@@ -35,22 +49,31 @@ function updateTabData(id) {
}
function updateTab(id){
- chromium.tabs.updateTab(updateTabData(id));
+ try {
+ chromium.tabs.update(id, updateTabData(id));
+ } catch (e) {
+ alert(e);
+ }
}
+
function moveTabData(id) {
return {
- 'id': id,
- 'index': parseInt(document.getElementById('index_' + id).value),
- 'windowId': parseInt(document.getElementById('windowId_' + id).value)
+ 'index': parseInt(document.getElementById('index_' + id).value),
+ 'windowId': parseInt(document.getElementById('windowId_' + id).value)
}
}
function moveTab(id) {
- chromium.tabs.moveTab(moveTabData(id));
+ try {
+ chromium.tabs.move(id, moveTabData(id));
+ } catch (e) {
+ alert(e);
+ }
}
function createTabData(id) {
return {
+ 'index': parseInt(document.getElementById('index_' + id).value),
'windowId': parseInt(document.getElementById('windowId_' + id).value),
'url': document.getElementById('url_' + id).value,
'selected': document.getElementById('selected_' + id).value ? true : false
@@ -58,20 +81,38 @@ function createTabData(id) {
}
function createTab() {
- chromium.tabs.createTab(createTabData('new'));
+ var args = createTabData('new')
+
+ if (!isInt(args.windowId))
+ delete args.windowId;
+ if (!isInt(args.index))
+ delete args.index;
+
+ try {
+ chromium.tabs.create(args);
+ } catch (e) {
+ alert(e);
+ }
}
function updateAll() {
-
- for (var i = 0; i < tabIds.length; i++) {
- chromium.tabs.updateTab(updateTabData(tabIds[i]));
+ try {
+ for (var i = 0; i < tabIds.length; i++) {
+ chromium.tabs.update(tabIds[i], updateTabData(tabIds[i]));
+ }
+ } catch(e) {
+ alert(e);
}
}
function moveAll() {
appendToLog('moving all');
- for (var i = 0; i < tabIds.length; i++) {
- chromium.tabs.moveTab(moveTabData(tabIds[i]));
+ try {
+ for (var i = 0; i < tabIds.length; i++) {
+ chromium.tabs.move(tabIds[i], moveTabData(tabIds[i]));
+ }
+ } catch(e) {
+ alert(e);
}
}
@@ -84,43 +125,49 @@ function clearLog() {
document.getElementById('log').innerHTML = '';
}
-chromium.windows.onWindowCreated.addListener(function(windowId) {
- appendToLog('onWindowCreated -- window: ' + windowId);
+chromium.windows.onCreated.addListener(function(windowId) {
+ appendToLog('windows.onCreated -- window: ' + windowId);
loadWindowList();
});
-chromium.windows.onWindowRemoved.addListener(function(windowId) {
- appendToLog('onWindowRemoved -- window: ' + windowId);
+chromium.windows.onFocusChanged.addListener(function(windowId) {
+ focusedWindowId = windowId;
+ appendToLog('windows.onFocusChanged -- window: ' + windowId);
loadWindowList();
});
-chromium.tabs.onTabCreated.addListener(function(data) {
- appendToLog('onTabCreated -- window: ' + data.windowId + ' tab: ' + data.tabId + ' index ' + data.index);
+chromium.windows.onRemoved.addListener(function(windowId) {
+ appendToLog('windows.onRemoved -- window: ' + windowId);
loadWindowList();
});
-chromium.tabs.onTabAttached.addListener(function(data) {
- appendToLog('onTabAttached -- window: ' + data.windowId + ' tab: ' + data.tabId + ' index ' + data.index);
+chromium.tabs.onCreated.addListener(function(tab) {
+ appendToLog('tabs.onCreated -- window: ' + tab.windowId + ' tab: ' + tab.id + ' index ' + tab.index + ' url ' + tab.url);
loadWindowList();
});
-chromium.tabs.onTabMoved.addListener(function(data) {
- appendToLog('onTabMoved -- window: ' + data.windowId + ' tab: ' + data.tabId + ' from ' + data.fromIndex + ' to ' + data.toIndex);
+chromium.tabs.onAttached.addListener(function(tabId, props) {
+ appendToLog('tabs.onAttached -- window: ' + props.newWindowId + ' tab: ' + tabId + ' index ' + props.newPosition);
loadWindowList();
});
-chromium.tabs.onTabDetached.addListener(function(data) {
- appendToLog('onTabDetached -- window: ' + data.windowId + ' tab: ' + data.tabId + ' index ' + data.index);
+chromium.tabs.onMoved.addListener(function(tabId, props) {
+ appendToLog('tabs.onMoved -- window: ' + props.windowId + ' tab: ' + tabId + ' from ' + props.fromIndex + ' to ' + props.toIndex);
loadWindowList();
});
-chromium.tabs.onTabSelectionChanged.addListener(function(data) {
- appendToLog('onTabSelectionChanged -- window: ' + data.windowId + ' tab: ' + data.tabId + ' index ' + data.index);
+chromium.tabs.onDetached.addListener(function(tabId, props) {
+ appendToLog('tabs.onDetached -- window: ' + props.oldWindowId + ' tab: ' + tabId + ' index ' + props.oldPosition);
loadWindowList();
});
-chromium.tabs.onTabRemoved.addListener(function(tabId) {
- appendToLog('onTabRemoved -- tab: ' + tabId);
+chromium.tabs.onSelectionChanged.addListener(function(tabId, props) {
+ appendToLog('tabs.onSelectionChanged -- window: ' + props.windowId + ' tab: ' + tabId);
+ loadWindowList();
+});
+
+chromium.tabs.onRemoved.addListener(function(tabId) {
+ appendToLog('tabs.onRemoved -- tab: ' + tabId);
loadWindowList();
});
@@ -147,15 +194,51 @@ function createWindow() {
delete args.height;
if (!args.url)
delete args.url;
+
+ try {
+ chromium.windows.createWindow(args);
+ } catch(e) {
+ alert(e);
+ }
+}
+
+function refreshWindow(windowId) {
+ chromium.windows.get(windowId, function(window) {
+ chromium.tabs.getAllInWindow(window.id, function(tabList) {
+ window.tabs = tabList;
+ var input = new JsExprContext(window);
+ var output = document.getElementById('window_' + window.id);
+ jstProcess(input, output);
+
+ appendToLog('window refreshed -- windowId: ' + window.id + ' tab count:' + window.tabs.length);
+ });
+ });
+}
- chromium.windows.createWindow(args);
+function refreshTab(tabId) {
+ chromium.tabs.get(tabId, function(tab) {
+ var input = new JsExprContext(tab);
+ var output = document.getElementById('tab_' + tab.id);
+ jstProcess(input, output);
+ appendToLog('tab refreshed -- tabId: ' + tab.id + ' url:' + tab.url);
+ });
+}
+
+function refreshSelectedTab(windowId) {
+ chromium.tabs.getSelected(windowId, function(tab) {
+ var input = new JsExprContext(tab);
+ var output = document.getElementById('tab_' + tab.id);
+ jstProcess(input, output);
+ appendToLog('selected tab refreshed -- tabId: ' + tab.id + ' url:' + tab.url);
+ });
}
</script>
</head>
- <body onload="loadWindowList();">
+ <body onload="bootStrap();">
<div id="windowList">
- <div style="background-color: #AAEEEE; margin: 4px; padding: 8px; margin: 20px" jsselect="$this">
+ <div style="background-color: #AAEEEE; margin: 4px; padding: 8px; margin: 20px" jsselect="$this"
+ jsvalues="id:'window_' + id">
<div style="font-style: italic; width: 80px; display: inline-block">
Window: <span jscontent="id"></span>
</div>
@@ -165,33 +248,39 @@ function createWindow() {
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
+ <input type="checkbox" jsvalues="checked:current; id:'current_' + id" /> Current
+ <button onclick="refreshWindow(this.jstdata);" jsvalues=".jstdata:id">Refresh</button>
</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 jsselect="tabs">
+ <div style="background-color: #EEEEEE; margin: 8px; padding: 4px" jsvalues="id:'tab_' + id">
+ <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>
+ <button onclick="refreshTab(this.jstdata);" jsvalues=".jstdata:id">Refresh</button>
+ </div>
</div>
- <div>
- <div style="width: 40px; display:inline-block">url:</div>
- <input style="width: 90%" type="text" jsvalues="value:url;id:'url_' + id" />
+ <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>
- <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.remove(this.jstdata);" jsvalues=".jstdata:id">Close Tab</button>
</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>
<button onclick="chromium.windows.removeWindow(this.jstdata);" jsvalues=".jstdata:id">Close Window</button>
+ <button onclick="refreshSelectedTab(this.jstdata);" jsvalues=".jstdata:id">Refresh Selected Tab</button>
</div>
</div>
<div style="background-color: #EEEEBB; margin: 20px; padding: 8px">