summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-04 16:44:43 +0000
committererikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-04 16:44:43 +0000
commit9c45b718e0b24664945c98d7a6315e4666fe7c22 (patch)
tree8697f8b5890b4a0f6121a76adf17a00045943068
parent79ae821680fd8d84cc567063ead475072b799bc9 (diff)
downloadchromium_src-9c45b718e0b24664945c98d7a6315e4666fe7c22.zip
chromium_src-9c45b718e0b24664945c98d7a6315e4666fe7c22.tar.gz
chromium_src-9c45b718e0b24664945c98d7a6315e4666fe7c22.tar.bz2
mole API - take 2
originally reviewed / committed as http://codereview.chromium.org/160276 TBR=mpcomplete,phadjan.jr Review URL: http://codereview.chromium.org/159854 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22382 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/browser.cc4
-rw-r--r--chrome/browser/browser.h7
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc18
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.h6
-rw-r--r--chrome/browser/extensions/extension_host.cc11
-rw-r--r--chrome/browser/extensions/extension_host.h1
-rw-r--r--chrome/browser/extensions/extension_shelf_model.cc71
-rw-r--r--chrome/browser/extensions/extension_shelf_model.h99
-rw-r--r--chrome/browser/extensions/extension_shelf_model_unittest.cc10
-rw-r--r--chrome/browser/extensions/extension_toolstrip_api.cc114
-rw-r--r--chrome/browser/extensions/extension_toolstrip_api.h33
-rw-r--r--chrome/browser/gtk/browser_window_gtk.cc18
-rw-r--r--chrome/browser/gtk/extension_shelf_gtk.cc25
-rw-r--r--chrome/browser/gtk/extension_shelf_gtk.h5
-rw-r--r--chrome/browser/views/extensions/extension_shelf.cc148
-rw-r--r--chrome/browser/views/extensions/extension_shelf.h14
-rw-r--r--chrome/browser/views/frame/browser_view.cc18
-rw-r--r--chrome/chrome.gyp2
-rw-r--r--chrome/common/common_resources.grd2
-rwxr-xr-xchrome/common/extensions/api/extension_api.json32
-rw-r--r--chrome/common/extensions/docs/toolstrip.html46
-rw-r--r--chrome/renderer/extensions/extension_api_client_unittest.cc41
-rw-r--r--chrome/renderer/renderer_resources.grd2
-rwxr-xr-xchrome/test/data/extensions/samples/mappy/manifest.json11
-rwxr-xr-xchrome/test/data/extensions/samples/mappy/mappy_content_script.js55
-rwxr-xr-xchrome/test/data/extensions/samples/mappy/mappy_toolstrip.html68
26 files changed, 726 insertions, 135 deletions
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 78eedaa..dd2f07b 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -272,6 +272,10 @@ Browser* Browser::CreateForApp(const std::wstring& app_name,
void Browser::CreateBrowserWindow() {
DCHECK(!window_);
+
+ if (SupportsWindowFeature(FEATURE_EXTENSIONSHELF))
+ extension_shelf_model_.reset(new ExtensionShelfModel(this));
+
window_ = BrowserWindow::CreateBrowserWindow(this);
// Show the First Run information bubble if we've been told to.
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index 6afac8a..5044810 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -13,6 +13,7 @@
#include "base/scoped_ptr.h"
#include "base/task.h"
#include "chrome/browser/command_updater.h"
+#include "chrome/browser/extensions/extension_shelf_model.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/shell_dialogs.h"
#include "chrome/browser/tabs/tab_strip_model.h"
@@ -133,6 +134,9 @@ class Browser : public TabStripModelDelegate,
const SessionID& session_id() const { return session_id_; }
CommandUpdater* command_updater() { return &command_updater_; }
FindBarController* find_bar() { return find_bar_controller_.get(); }
+ ExtensionShelfModel* extension_shelf_model() {
+ return extension_shelf_model_.get();
+ }
// Setters /////////////////////////////////////////////////////////////////
@@ -703,6 +707,9 @@ class Browser : public TabStripModelDelegate,
// The model for the toolbar view.
BrowserToolbarModel toolbar_model_;
+ // The model for the extension shelf.
+ scoped_ptr<ExtensionShelfModel> extension_shelf_model_;
+
// UI update coalescing and handling ////////////////////////////////////////
// Tracks invalidates to the UI, see the declaration in the .cc file.
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 40c5996..5d03e1f 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -16,10 +16,12 @@
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_tabs_module.h"
#include "chrome/browser/extensions/extension_tabs_module_constants.h"
+#include "chrome/browser/extensions/extension_toolstrip_api.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/common/result_codes.h"
+#include "chrome/common/url_constants.h"
// FactoryRegistry -------------------------------------------------------------
@@ -67,6 +69,7 @@ void FactoryRegistry::ResetFunctions() {
namespace tabs = extension_tabs_module_constants;
namespace page_actions = extension_page_actions_module_constants;
namespace bookmarks = extension_bookmarks_module_constants;
+ namespace toolstrip = extension_toolstrip_api_functions;
// Windows
factories_[tabs::kGetWindowFunction] =
@@ -127,6 +130,12 @@ void FactoryRegistry::ResetFunctions() {
&NewExtensionFunction<MoveBookmarkFunction>;
factories_[bookmarks::kSetBookmarkTitleFunction] =
&NewExtensionFunction<SetBookmarkTitleFunction>;
+
+ // Toolstrips.
+ factories_[toolstrip::kExpandFunction] =
+ &NewExtensionFunction<ToolstripExpandFunction>;
+ factories_[toolstrip::kCollapseFunction] =
+ &NewExtensionFunction<ToolstripCollapseFunction>;
}
void FactoryRegistry::GetAllNames(std::vector<std::string>* names) {
@@ -187,6 +196,10 @@ ExtensionFunctionDispatcher::ExtensionFunctionDispatcher(
delegate_(delegate),
url_(url),
ALLOW_THIS_IN_INITIALIZER_LIST(peer_(new Peer(this))) {
+ // TODO(erikkay) should we do something for these errors in Release?
+ DCHECK(url.SchemeIs(chrome::kExtensionScheme));
+ DCHECK(profile()->GetExtensionsService()->GetExtensionByURL(url));
+
all_instances()->insert(this);
// Notify the ExtensionProcessManager that the view was created.
@@ -207,6 +220,11 @@ Browser* ExtensionFunctionDispatcher::GetBrowser() {
return retval;
}
+ExtensionHost* ExtensionFunctionDispatcher::GetExtensionHost() {
+ DCHECK(delegate_);
+ return delegate_->GetExtensionHost();
+}
+
void ExtensionFunctionDispatcher::HandleRequest(const std::string& name,
const std::string& args,
int request_id,
diff --git a/chrome/browser/extensions/extension_function_dispatcher.h b/chrome/browser/extensions/extension_function_dispatcher.h
index 0e79882..7a47b3f 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.h
+++ b/chrome/browser/extensions/extension_function_dispatcher.h
@@ -14,6 +14,7 @@
class Browser;
class ExtensionFunction;
+class ExtensionHost;
class Profile;
class RenderViewHost;
class RenderViewHostDelegate;
@@ -29,6 +30,7 @@ class ExtensionFunctionDispatcher {
class Delegate {
public:
virtual Browser* GetBrowser() = 0;
+ virtual ExtensionHost* GetExtensionHost() { return NULL; }
};
// The peer object allows us to notify ExtensionFunctions when we are
@@ -68,6 +70,10 @@ class ExtensionFunctionDispatcher {
// example, for positioning windows, or alert boxes, or creating tabs.
Browser* GetBrowser();
+ // Gets the ExtensionHost associated with this object. In the case of
+ // tab hosted extension pages, this will return NULL.
+ ExtensionHost* GetExtensionHost();
+
// Handle a malformed message. Possibly the result of an attack, so kill
// the renderer.
void HandleBadMessage(ExtensionFunction* api);
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index cbd63a7..f393517 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -26,6 +26,7 @@
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "chrome/common/render_messages.h"
+#include "chrome/common/url_constants.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
@@ -209,6 +210,10 @@ void ExtensionHost::DidNavigate(RenderViewHost* render_view_host,
}
url_ = params.url;
+ if (!url_.SchemeIs(chrome::kExtensionScheme)) {
+ extension_function_dispatcher_.reset(NULL);
+ return;
+ }
extension_function_dispatcher_.reset(
new ExtensionFunctionDispatcher(render_view_host_, this, url_));
}
@@ -266,8 +271,10 @@ void ExtensionHost::ProcessDOMUIMessage(const std::string& message,
const std::string& content,
int request_id,
bool has_callback) {
- extension_function_dispatcher_->HandleRequest(message, content, request_id,
- has_callback);
+ if (extension_function_dispatcher_.get()) {
+ extension_function_dispatcher_->HandleRequest(message, content, request_id,
+ has_callback);
+ }
}
void ExtensionHost::DidInsertCSS() {
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index 8a0c860..1a9ffed 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -139,6 +139,7 @@ class ExtensionHost : public RenderViewHostDelegate,
// part of. If this is a global background page, we use the active Browser
// instead.
virtual Browser* GetBrowser();
+ virtual ExtensionHost* GetExtensionHost() { return this; }
// Returns true if we're hosting a background page.
// This isn't valid until CreateRenderView is called.
diff --git a/chrome/browser/extensions/extension_shelf_model.cc b/chrome/browser/extensions/extension_shelf_model.cc
index 1449d97..6b7bd0c 100644
--- a/chrome/browser/extensions/extension_shelf_model.cc
+++ b/chrome/browser/extensions/extension_shelf_model.cc
@@ -39,11 +39,13 @@ ExtensionShelfModel::ExtensionShelfModel(Browser* browser)
}
ExtensionShelfModel::~ExtensionShelfModel() {
+ FOR_EACH_OBSERVER(ExtensionShelfModelObserver, observers_,
+ ShelfModelDeleting());
+
while (observers_.size())
observers_.RemoveObserver(observers_.GetElementAt(0));
- ExtensionToolstrips::iterator t;
- for (t = toolstrips_.begin(); t != toolstrips_.end(); ++t)
+ for (iterator t = toolstrips_.begin(); t != toolstrips_.end(); ++t)
delete t->host;
toolstrips_.clear();
}
@@ -71,7 +73,7 @@ void ExtensionShelfModel::InsertToolstripAt(int index,
}
void ExtensionShelfModel::RemoveToolstripAt(int index) {
- ExtensionHost* host = ToolstripAt(index);
+ ExtensionHost* host = ToolstripAt(index).host;
FOR_EACH_OBSERVER(ExtensionShelfModelObserver, observers_,
ToolstripRemovingAt(host, index));
toolstrips_.erase(toolstrips_.begin() + index);
@@ -94,28 +96,27 @@ void ExtensionShelfModel::MoveToolstripAt(int index, int to_index) {
UpdatePrefs();
}
-int ExtensionShelfModel::IndexOfToolstrip(ExtensionHost* toolstrip) {
- ExtensionToolstrips::iterator i;
- for (i = toolstrips_.begin(); i != toolstrips_.end(); ++i) {
- if (i->host == toolstrip)
+int ExtensionShelfModel::IndexOfHost(ExtensionHost* host) {
+ for (iterator i = toolstrips_.begin(); i != toolstrips_.end(); ++i) {
+ if (i->host == host)
return i - toolstrips_.begin();
}
return -1;
}
-ExtensionHost* ExtensionShelfModel::ToolstripAt(int index) {
- DCHECK(index >= 0);
- return toolstrips_[index].host;
-}
-
-Extension::ToolstripInfo& ExtensionShelfModel::ToolstripInfoAt(int index) {
- DCHECK(index >= 0);
- return toolstrips_[index].info;
+ExtensionShelfModel::iterator ExtensionShelfModel::ToolstripForHost(
+ ExtensionHost* host) {
+ for (iterator i = toolstrips_.begin(); i != toolstrips_.end(); ++i) {
+ if (i->host == host)
+ return i;
+ }
+ return toolstrips_.end();
}
-void* ExtensionShelfModel::ToolstripDataAt(int index) {
+const ExtensionShelfModel::ToolstripItem& ExtensionShelfModel::ToolstripAt(
+ int index) {
DCHECK(index >= 0);
- return toolstrips_[index].data;
+ return toolstrips_[index];
}
void ExtensionShelfModel::SetToolstripDataAt(int index, void* data) {
@@ -123,6 +124,26 @@ void ExtensionShelfModel::SetToolstripDataAt(int index, void* data) {
toolstrips_[index].data = data;
}
+void ExtensionShelfModel::ExpandToolstrip(iterator toolstrip,
+ const GURL& url, int height) {
+ if (toolstrip == end())
+ return;
+ toolstrip->height = height;
+ toolstrip->url = url;
+ FOR_EACH_OBSERVER(ExtensionShelfModelObserver, observers_,
+ ToolstripChanged(toolstrip));
+}
+
+void ExtensionShelfModel::CollapseToolstrip(iterator toolstrip,
+ const GURL& url) {
+ if (toolstrip == end())
+ return;
+ toolstrip->height = 0;
+ toolstrip->url = url;
+ FOR_EACH_OBSERVER(ExtensionShelfModelObserver, observers_,
+ ToolstripChanged(toolstrip));
+}
+
void ExtensionShelfModel::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -136,7 +157,8 @@ void ExtensionShelfModel::Observe(NotificationType type,
break;
case NotificationType::EXTENSIONS_READY:
if (browser_->profile()->GetExtensionsService()) {
- AddExtensions(browser_->profile()->GetExtensionsService()->extensions());
+ AddExtensions(
+ browser_->profile()->GetExtensionsService()->extensions());
SortToolstrips();
}
ready_ = true;
@@ -167,6 +189,7 @@ void ExtensionShelfModel::AddExtension(Extension* extension) {
item.host = manager->CreateView(extension, url, browser_);
item.info = *toolstrip;
item.data = NULL;
+ item.height = 0;
AppendToolstrip(item);
}
}
@@ -182,7 +205,7 @@ void ExtensionShelfModel::AddExtensions(const ExtensionList* extensions) {
void ExtensionShelfModel::RemoveExtension(Extension* extension) {
bool changed = false;
for (int i = count() - 1; i >= 0; --i) {
- ExtensionHost* t = ToolstripAt(i);
+ ExtensionHost* t = ToolstripAt(i).host;
if (t->extension()->id() == extension->id()) {
changed = true;
RemoveToolstripAt(i);
@@ -202,7 +225,7 @@ void ExtensionShelfModel::UpdatePrefs() {
// It's easiest to just rebuild the list each time.
ExtensionPrefs::URLList urls;
for (int i = 0; i < count(); ++i)
- urls.push_back(ToolstripAt(i)->GetURL());
+ urls.push_back(ToolstripAt(i).host->GetURL());
prefs_->SetShelfToolstripOrder(urls);
NotificationService::current()->Notify(
@@ -213,15 +236,15 @@ void ExtensionShelfModel::UpdatePrefs() {
void ExtensionShelfModel::SortToolstrips() {
ExtensionPrefs::URLList urls = prefs_->GetShelfToolstripOrder();
- ExtensionToolstrips copy =
- ExtensionToolstrips(toolstrips_.begin(), toolstrips_.end());
+ ToolstripList copy =
+ ToolstripList(toolstrips_.begin(), toolstrips_.end());
toolstrips_.clear();
// Go through the urls and find the matching toolstrip, re-adding it to the
// new list in the proper order.
for (size_t i = 0; i < urls.size(); ++i) {
GURL& url = urls[i];
- for (ExtensionToolstrips::iterator toolstrip = copy.begin();
+ for (iterator toolstrip = copy.begin();
toolstrip != copy.end(); ++toolstrip) {
if (url == toolstrip->host->GetURL()) {
// Note that it's technically possible for the same URL to appear in
@@ -238,7 +261,7 @@ void ExtensionShelfModel::SortToolstrips() {
// Any toolstrips remaining in |copy| were somehow missing from the prefs,
// so just append them to the end.
- for (ExtensionToolstrips::iterator toolstrip = copy.begin();
+ for (iterator toolstrip = copy.begin();
toolstrip != copy.end(); ++toolstrip) {
toolstrips_.push_back(*toolstrip);
}
diff --git a/chrome/browser/extensions/extension_shelf_model.h b/chrome/browser/extensions/extension_shelf_model.h
index 28fb00b..29f6592 100644
--- a/chrome/browser/extensions/extension_shelf_model.h
+++ b/chrome/browser/extensions/extension_shelf_model.h
@@ -16,36 +16,7 @@
class Browser;
class ExtensionPrefs;
-
-// Objects implement this interface when they wish to be notified of changes to
-// the ExtensionShelfModel.
-//
-// Register your ExtensionShelfModelObserver with the ExtensionShelfModel using
-// Add/RemoveObserver methods.
-class ExtensionShelfModelObserver {
- public:
- // A new toolstrip was inserted into ExtensionShelfModel at |index|.
- virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index) {}
-
- // The specified toolstrip is being removed and destroyed.
- virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index) {}
-
- // |toolstrip| moved from |from_index| to |to_index|.
- virtual void ToolstripMoved(ExtensionHost* toolstrip,
- int from_index,
- int to_index) {}
-
- // The specified toolstrip changed in some way (currently only size changes)
- virtual void ToolstripChangedAt(ExtensionHost* toolstrip, int index) {}
-
- // There are no more toolstrips in the model.
- virtual void ExtensionShelfEmpty() {}
-
- // The entire model may have changed.
- virtual void ShelfModelReloaded() {}
-
- // TODO(erikkay) - any more?
-};
+class ExtensionShelfModelObserver;
// The model representing the toolstrips on an ExtensionShelf. The order of
// the toolstrips is common across all of the models for a given Profile,
@@ -60,8 +31,13 @@ class ExtensionShelfModel : public NotificationObserver {
ExtensionHost* host;
Extension::ToolstripInfo info;
void* data;
+ int height;
+ GURL url;
};
+ typedef std::vector<ToolstripItem> ToolstripList;
+ typedef ToolstripList::iterator iterator;
+
// Add and remove observers to changes within this ExtensionShelfModel.
void AddObserver(ExtensionShelfModelObserver* observer);
void RemoveObserver(ExtensionShelfModelObserver* observer);
@@ -70,6 +46,10 @@ class ExtensionShelfModel : public NotificationObserver {
int count() const { return static_cast<int>(toolstrips_.size()); }
bool empty() const { return toolstrips_.empty(); }
+ // Iterators for the toolstrips in the model.
+ iterator begin() { return toolstrips_.begin(); }
+ ExtensionShelfModel::iterator end() { return toolstrips_.end(); }
+
// Add |toolstrip| to the end of the shelf.
void AppendToolstrip(const ToolstripItem& toolstrip);
@@ -82,18 +62,28 @@ class ExtensionShelfModel : public NotificationObserver {
// Move the toolstrip at |index| to |to_index|.
void MoveToolstripAt(int index, int to_index);
- // Lookup the index of |toolstrip|. Returns -1 if not present.
- int IndexOfToolstrip(ExtensionHost* toolstrip);
+ // Lookup the index of |host|. Returns -1 if not present.
+ int IndexOfHost(ExtensionHost* host);
// Return the toolstrip at |index|.
- ExtensionHost* ToolstripAt(int index);
+ const ToolstripItem& ToolstripAt(int index);
- // Return the ToolstripInfo at |index|.
- Extension::ToolstripInfo& ToolstripInfoAt(int index);
+ // Return the ToolstripItem associated with |host| or NULL if it's not
+ // present.
+ ToolstripList::iterator ToolstripForHost(ExtensionHost* host);
- // Get/Set some arbitrary data associated with a particular toolstrip.
+ // Set some arbitrary data associated with a particular toolstrip.
void SetToolstripDataAt(int index, void* data);
- void* ToolstripDataAt(int index);
+
+ // Update the ToolstripItem for |toolstrip| to set its |url| and |height|
+ // and then call ToolstripChanged for all observers.
+ // If |url| is empty, no navigation is requested.
+ void ExpandToolstrip(iterator toolstrip, const GURL& url, int height);
+
+ // Update the ToolstripItem for |toolstrip| to set its |url| and its height
+ // to 0, and then call ToolstripChanged for all observers.
+ // If |url| is empty, no navigation is requested.
+ void CollapseToolstrip(iterator toolstrip, const GURL& url);
// NotificationObserver
virtual void Observe(NotificationType type,
@@ -126,8 +116,7 @@ class ExtensionShelfModel : public NotificationObserver {
NotificationRegistrar registrar_;
// The Toolstrips loaded in this model. The model owns these objects.
- typedef std::vector<ToolstripItem> ExtensionToolstrips;
- ExtensionToolstrips toolstrips_;
+ ToolstripList toolstrips_;
// Our observers.
typedef ObserverList<ExtensionShelfModelObserver>
@@ -140,4 +129,36 @@ class ExtensionShelfModel : public NotificationObserver {
DISALLOW_COPY_AND_ASSIGN(ExtensionShelfModel);
};
+// Objects implement this interface when they wish to be notified of changes to
+// the ExtensionShelfModel.
+//
+// Register your ExtensionShelfModelObserver with the ExtensionShelfModel using
+// Add/RemoveObserver methods.
+class ExtensionShelfModelObserver {
+ public:
+ // A new toolstrip was inserted into ExtensionShelfModel at |index|.
+ virtual void ToolstripInsertedAt(ExtensionHost* toolstrip, int index) {}
+
+ // The specified toolstrip is being removed and destroyed.
+ virtual void ToolstripRemovingAt(ExtensionHost* toolstrip, int index) {}
+
+ // |toolstrip| moved from |from_index| to |to_index|.
+ virtual void ToolstripMoved(ExtensionHost* toolstrip,
+ int from_index,
+ int to_index) {}
+
+ // The specified toolstrip changed in some way (currently only size changes)
+ virtual void ToolstripChanged(ExtensionShelfModel::iterator toolstrip) {}
+
+ // There are no more toolstrips in the model.
+ virtual void ExtensionShelfEmpty() {}
+
+ // The entire model may have changed.
+ virtual void ShelfModelReloaded() {}
+
+ // The model is being destroyed.
+ virtual void ShelfModelDeleting() {}
+};
+
+
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SHELF_MODEL_H_
diff --git a/chrome/browser/extensions/extension_shelf_model_unittest.cc b/chrome/browser/extensions/extension_shelf_model_unittest.cc
index d1e2217..59cf318 100644
--- a/chrome/browser/extensions/extension_shelf_model_unittest.cc
+++ b/chrome/browser/extensions/extension_shelf_model_unittest.cc
@@ -80,18 +80,18 @@ IN_PROC_BROWSER_TEST_F(ExtensionShelfModelTest, Basic) {
// extension1 has two toolstrips
EXPECT_EQ(inserted_count_, 2);
- ExtensionHost* one = model_->ToolstripAt(0);
- ExtensionHost* two = model_->ToolstripAt(1);
+ ExtensionHost* one = model_->ToolstripAt(0).host;
+ ExtensionHost* two = model_->ToolstripAt(1).host;
EXPECT_EQ(one->GetURL().path(), "/toolstrip1.html");
EXPECT_EQ(two->GetURL().path(), "/toolstrip2.html");
model_->MoveToolstripAt(0, 1);
- EXPECT_EQ(two, model_->ToolstripAt(0));
- EXPECT_EQ(one, model_->ToolstripAt(1));
+ EXPECT_EQ(two, model_->ToolstripAt(0).host);
+ EXPECT_EQ(one, model_->ToolstripAt(1).host);
EXPECT_EQ(moved_count_, 1);
model_->RemoveToolstripAt(0);
- EXPECT_EQ(one, model_->ToolstripAt(0));
+ EXPECT_EQ(one, model_->ToolstripAt(0).host);
EXPECT_EQ(1, model_->count());
EXPECT_EQ(removed_count_, 1);
}
diff --git a/chrome/browser/extensions/extension_toolstrip_api.cc b/chrome/browser/extensions/extension_toolstrip_api.cc
new file mode 100644
index 0000000..248eedb
--- /dev/null
+++ b/chrome/browser/extensions/extension_toolstrip_api.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_toolstrip_api.h"
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_shelf_model.h"
+
+namespace extension_toolstrip_api_functions {
+const char kExpandFunction[] = "toolstrip.expand";
+const char kCollapseFunction[] = "toolstrip.collapse";
+}; // namespace extension_toolstrip_api_functions
+
+namespace {
+// Errors.
+const char kNotAToolstripError[] = "This page is not a toolstrip.";
+const char kAlreadyExpandedError[] = "This toolstrip is already expanded.";
+const char kAlreadyCollapsedError[] = "This toolstrip is already collapsed.";
+const char kInvalidURLError[] = "Invalid URL";
+const char kBadHeightError[] = "Bad height.";
+
+// TODO(erikkay) what are good values here?
+const int kMinHeight = 50;
+const int kMaxHeight = 1000;
+}; // namespace
+
+bool ToolstripFunction::RunImpl() {
+ ExtensionHost* host = dispatcher()->GetExtensionHost();
+ if (!host) {
+ error_ = kNotAToolstripError;
+ return false;
+ }
+ Browser* browser = dispatcher()->GetBrowser();
+ if (!browser) {
+ error_ = kNotAToolstripError;
+ return false;
+ }
+ model_ = browser->extension_shelf_model();
+ if (!model_) {
+ error_ = kNotAToolstripError;
+ return false;
+ }
+ toolstrip_ = model_->ToolstripForHost(host);
+ if (toolstrip_ == model_->end()) {
+ error_ = kNotAToolstripError;
+ return false;
+ }
+ return true;
+}
+
+bool ToolstripExpandFunction::RunImpl() {
+ if (!ToolstripFunction::RunImpl())
+ return false;
+ if (toolstrip_->height != 0) {
+ error_ = kAlreadyExpandedError;
+ return false;
+ }
+
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST));
+ const ListValue* args = static_cast<const ListValue*>(args_);
+ EXTENSION_FUNCTION_VALIDATE(args->GetSize() <= 2);
+
+ int height;
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(0, &height));
+ EXTENSION_FUNCTION_VALIDATE(height >= 0);
+ if (height < kMinHeight || height > kMaxHeight) {
+ error_ = kBadHeightError;
+ return false;
+ }
+
+ GURL url;
+ if (args->GetSize() == 2) {
+ Value* url_val;
+ EXTENSION_FUNCTION_VALIDATE(args->Get(1, &url_val));
+ if (url_val->GetType() != Value::TYPE_NULL) {
+ std::string url_str;
+ EXTENSION_FUNCTION_VALIDATE(url_val->GetAsString(&url_str));
+ url = GURL(url_str);
+ if (!url.is_valid() && !url.is_empty()) {
+ error_ = kInvalidURLError;
+ return false;
+ }
+ }
+ }
+
+ model_->ExpandToolstrip(toolstrip_, url, height);
+ return true;
+}
+
+bool ToolstripCollapseFunction::RunImpl() {
+ if (!ToolstripFunction::RunImpl())
+ return false;
+
+ if (toolstrip_->height == 0) {
+ error_ = kAlreadyCollapsedError;
+ return false;
+ }
+
+ GURL url;
+ if (args_->GetType() != Value::TYPE_NULL) {
+ std::string url_str;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetAsString(&url_str));
+ url = GURL(url_str);
+ if (!url.is_valid() && !url.is_empty()) {
+ error_ = kInvalidURLError;
+ return false;
+ }
+ }
+
+ model_->CollapseToolstrip(toolstrip_, url);
+ return true;
+}
diff --git a/chrome/browser/extensions/extension_toolstrip_api.h b/chrome/browser/extensions/extension_toolstrip_api.h
new file mode 100644
index 0000000..127c952
--- /dev/null
+++ b/chrome/browser/extensions/extension_toolstrip_api.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLSTRIP_API_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLSTRIP_API_H_
+
+#include "chrome/browser/extensions/extension_function.h"
+#include "chrome/browser/extensions/extension_shelf_model.h"
+
+// Function names.
+namespace extension_toolstrip_api_functions {
+ extern const char kExpandFunction[];
+ extern const char kCollapseFunction[];
+}; // namespace extension_toolstrip_api_functions
+
+class ToolstripFunction : public SyncExtensionFunction {
+ protected:
+ virtual bool RunImpl();
+
+ ExtensionShelfModel* model_;
+ ExtensionShelfModel::iterator toolstrip_;
+};
+
+class ToolstripExpandFunction : public ToolstripFunction {
+ virtual bool RunImpl();
+};
+
+class ToolstripCollapseFunction : public ToolstripFunction {
+ virtual bool RunImpl();
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_TOOLSTRIP_API_H_
diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc
index 9366776..d6bfe1a 100644
--- a/chrome/browser/gtk/browser_window_gtk.cc
+++ b/chrome/browser/gtk/browser_window_gtk.cc
@@ -945,7 +945,8 @@ void BrowserWindowGtk::MaybeShowBookmarkBar(TabContents* contents,
}
void BrowserWindowGtk::MaybeShowExtensionShelf() {
- extension_shelf_->Show();
+ if (extension_shelf_.get())
+ extension_shelf_->Show();
}
void BrowserWindowGtk::UpdateDevToolsForContents(TabContents* contents) {
@@ -1011,7 +1012,8 @@ void BrowserWindowGtk::OnStateChanged(GdkWindowState state) {
toolbar_->Hide();
tabstrip_->Hide();
bookmark_bar_->Hide(false);
- extension_shelf_->Hide();
+ if (extension_shelf_.get())
+ extension_shelf_->Hide();
} else {
UpdateCustomFrame();
ShowSupportedWindowFeatures();
@@ -1195,10 +1197,12 @@ void BrowserWindowGtk::InitWidgets() {
this));
bookmark_bar_->AddBookmarkbarToBox(content_vbox_);
- extension_shelf_.reset(new ExtensionShelfGtk(browser()->profile(),
- browser_.get()));
- extension_shelf_->AddShelfToBox(content_vbox_);
- MaybeShowExtensionShelf();
+ if (IsExtensionShelfSupported()) {
+ extension_shelf_.reset(new ExtensionShelfGtk(browser()->profile(),
+ browser_.get()));
+ extension_shelf_->AddShelfToBox(content_vbox_);
+ MaybeShowExtensionShelf();
+ }
// This vbox surrounds the render area: find bar, info bars and render view.
// The reason is that this area as a whole needs to be grouped in its own
@@ -1563,7 +1567,7 @@ void BrowserWindowGtk::HideUnsupportedWindowFeatures() {
if (!IsBookmarkBarSupported())
bookmark_bar_->Hide(false);
- if (!IsExtensionShelfSupported())
+ if (!IsExtensionShelfSupported() && extension_shelf_.get())
extension_shelf_->Hide();
}
diff --git a/chrome/browser/gtk/extension_shelf_gtk.cc b/chrome/browser/gtk/extension_shelf_gtk.cc
index 5f3a4b9..c83d182 100644
--- a/chrome/browser/gtk/extension_shelf_gtk.cc
+++ b/chrome/browser/gtk/extension_shelf_gtk.cc
@@ -67,7 +67,7 @@ void ExtensionShelfGtk::Toolstrip::Init() {
ExtensionShelfGtk::ExtensionShelfGtk(Profile* profile, Browser* browser)
: browser_(browser),
theme_provider_(GtkThemeProvider::GetFrom(profile)),
- model_(new ExtensionShelfModel(browser)) {
+ model_(browser->extension_shelf_model()) {
Init(profile);
registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
@@ -75,7 +75,8 @@ ExtensionShelfGtk::ExtensionShelfGtk(Profile* profile, Browser* browser)
}
ExtensionShelfGtk::~ExtensionShelfGtk() {
- model_->RemoveObserver(this);
+ if (model_)
+ model_->RemoveObserver(this);
event_box_.Destroy();
}
@@ -119,8 +120,8 @@ void ExtensionShelfGtk::ToolstripMoved(ExtensionHost* host,
AdjustHeight();
}
-void ExtensionShelfGtk::ToolstripChangedAt(ExtensionHost* host,
- int index) {
+void ExtensionShelfGtk::ToolstripChanged(
+ ExtensionShelfModel::iterator toolstrip) {
// TODO(phajdan.jr): Implement changing toolstrips.
AdjustHeight();
}
@@ -139,6 +140,18 @@ void ExtensionShelfGtk::ShelfModelReloaded() {
LoadFromModel();
}
+void ExtensionShelfGtk::ShelfModelDeleting() {
+ for (std::set<Toolstrip*>::iterator iter = toolstrips_.begin();
+ iter != toolstrips_.end(); ++iter) {
+ (*iter)->RemoveToolstripFromBox(shelf_hbox_);
+ delete *iter;
+ }
+ toolstrips_.clear();
+
+ model_->RemoveObserver(this);
+ model_ = NULL;
+}
+
void ExtensionShelfGtk::Init(Profile* profile) {
event_box_.Own(gtk_event_box_new());
@@ -187,12 +200,12 @@ void ExtensionShelfGtk::LoadFromModel() {
DCHECK(toolstrips_.empty());
int count = model_->count();
for (int i = 0; i < count; ++i)
- ToolstripInsertedAt(model_->ToolstripAt(i), i);
+ ToolstripInsertedAt(model_->ToolstripAt(i).host, i);
AdjustHeight();
}
ExtensionShelfGtk::Toolstrip* ExtensionShelfGtk::ToolstripAtIndex(int index) {
- return static_cast<Toolstrip*>(model_->ToolstripDataAt(index));
+ return static_cast<Toolstrip*>(model_->ToolstripAt(index).data);
}
// static
diff --git a/chrome/browser/gtk/extension_shelf_gtk.h b/chrome/browser/gtk/extension_shelf_gtk.h
index da2948d..81b055a89 100644
--- a/chrome/browser/gtk/extension_shelf_gtk.h
+++ b/chrome/browser/gtk/extension_shelf_gtk.h
@@ -42,9 +42,10 @@ class ExtensionShelfGtk : public ExtensionShelfModelObserver,
virtual void ToolstripMoved(ExtensionHost* host,
int from_index,
int to_index);
- virtual void ToolstripChangedAt(ExtensionHost* host, int index);
+ virtual void ToolstripChanged(ExtensionShelfModel::iterator toolstrip);
virtual void ExtensionShelfEmpty();
virtual void ShelfModelReloaded();
+ virtual void ShelfModelDeleting();
private:
class Toolstrip;
@@ -88,7 +89,7 @@ class ExtensionShelfGtk : public ExtensionShelfModelObserver,
NotificationRegistrar registrar_;
// The model representing the toolstrips on the shelf.
- scoped_ptr<ExtensionShelfModel> model_;
+ ExtensionShelfModel* model_;
// Set of toolstrip views which are really on the shelf.
std::set<Toolstrip*> toolstrips_;
diff --git a/chrome/browser/views/extensions/extension_shelf.cc b/chrome/browser/views/extensions/extension_shelf.cc
index 77df55a..3ba801c 100644
--- a/chrome/browser/views/extensions/extension_shelf.cc
+++ b/chrome/browser/views/extensions/extension_shelf.cc
@@ -153,6 +153,10 @@ class ExtensionShelf::Toolstrip : public views::View,
void DoHideShelfHandle();
void StopHandleTimer();
+ // Expand / Collapse
+ void Expand(int height, const GURL& url);
+ void Collapse(const GURL& url);
+
// BrowserBubble::Delegate
virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble);
virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble);
@@ -254,9 +258,8 @@ void ExtensionShelf::Toolstrip::Paint(gfx::Canvas* canvas) {
gfx::Size ExtensionShelf::Toolstrip::GetPreferredSize() {
gfx::Size sz = title_->GetPreferredSize();
sz.set_width(std::max(view()->width(), sz.width()));
- if (!expanded_) {
+ if (!expanded_)
sz.Enlarge(2 + kHandlePadding * 2, kHandlePadding * 2);
- }
if (dragging_ || expanded_) {
gfx::Size extension_size = view()->GetPreferredSize();
sz.Enlarge(0, extension_size.height() + 2);
@@ -333,33 +336,17 @@ void ExtensionShelf::Toolstrip::OnMouseReleased(const views::MouseEvent& event,
View::ConvertPointToView(NULL, shelf_, &loc);
shelf_->DropExtension(this, loc, canceled);
AttachToShelf(true);
- } else if (!canceled &&
- info_.mole.is_valid() && info_.toolstrip.is_valid()) {
+ } else if (!canceled) {
// Toggle mole to either expanded or collapsed.
- expanded_ = !expanded_;
- view()->set_is_toolstrip(!expanded_);
+ // TODO(erikkay) If there's no valid URL in the manifest, should we
+ // post an event to the toolstrip in this case?
if (expanded_) {
- host_->NavigateToURL(info_.mole);
- StopHandleTimer();
- DetachFromShelf(false);
-
- gfx::Size extension_size = view()->GetPreferredSize();
- extension_size.set_height(info_.mole_height);
- view()->SetPreferredSize(extension_size);
- LayoutHandle();
+ if (info_.toolstrip.is_valid())
+ shelf_->CollapseToolstrip(host_, info_.toolstrip);
} else {
- gfx::Size extension_size = view()->GetPreferredSize();
- extension_size.set_height(kToolstripHeight);
- view()->SetPreferredSize(extension_size);
-
- host_->NavigateToURL(info_.toolstrip);
- AttachToShelf(false);
+ if (info_.mole.is_valid())
+ shelf_->ExpandToolstrip(host_, info_.mole, info_.mole_height);
}
-
- // This is to prevent flickering as the page loads and lays out.
- // Once the navigation is finished, ExtensionView will wind up setting
- // visibility to true.
- view()->SetVisible(false);
}
}
@@ -414,6 +401,7 @@ void ExtensionShelf::Toolstrip::BubbleBrowserWindowClosing(
}
void ExtensionShelf::Toolstrip::DetachFromShelf(bool browserDetach) {
+ DCHECK(handle_.get());
DCHECK(!placeholder_view_);
if (browserDetach && handle_->attached())
handle_->DetachFromBrowser();
@@ -430,6 +418,8 @@ void ExtensionShelf::Toolstrip::DetachFromShelf(bool browserDetach) {
}
void ExtensionShelf::Toolstrip::AttachToShelf(bool browserAttach) {
+ DCHECK(handle_.get());
+ DCHECK(placeholder_view_);
if (browserAttach && !handle_->attached())
handle_->AttachToBrowser();
@@ -473,6 +463,56 @@ void ExtensionShelf::Toolstrip::StopHandleTimer() {
timer_factory_.RevokeAll();
}
+void ExtensionShelf::Toolstrip::Expand(int height, const GURL& url) {
+ DCHECK(!expanded_);
+
+ DoShowShelfHandle();
+
+ expanded_ = true;
+ view()->set_is_toolstrip(!expanded_);
+
+ bool navigate = (!url.is_empty() && url != host_->GetURL());
+ if (navigate)
+ host_->NavigateToURL(url);
+
+ StopHandleTimer();
+ DetachFromShelf(false);
+
+ gfx::Size extension_size = view()->GetPreferredSize();
+ extension_size.set_height(height);
+ view()->SetPreferredSize(extension_size);
+ LayoutHandle();
+
+ // This is to prevent flickering as the page loads and lays out.
+ // Once the navigation is finished, ExtensionView will wind up setting
+ // visibility to true.
+ if (navigate)
+ view()->SetVisible(false);
+}
+
+void ExtensionShelf::Toolstrip::Collapse(const GURL& url) {
+ DCHECK(expanded_);
+ expanded_ = false;
+ view()->set_is_toolstrip(!expanded_);
+
+ gfx::Size extension_size = view()->GetPreferredSize();
+ extension_size.set_height(kToolstripHeight);
+ view()->SetPreferredSize(extension_size);
+ AttachToShelf(false);
+
+ if (!url.is_empty() && url != host_->GetURL()) {
+ host_->NavigateToURL(url);
+
+ // This is to prevent flickering as the page loads and lays out.
+ // Once the navigation is finished, ExtensionView will wind up setting
+ // visibility to true.
+ view()->SetVisible(false);
+ }
+
+ // Must use the delay due to bug 18248.
+ HideShelfHandle(kHideDelayMs);
+}
+
void ExtensionShelf::Toolstrip::ShowShelfHandle() {
StopHandleTimer();
if (handle_visible())
@@ -500,19 +540,21 @@ void ExtensionShelf::Toolstrip::HideShelfHandle(int delay_ms) {
////////////////////////////////////////////////////////////////////////////////
ExtensionShelf::ExtensionShelf(Browser* browser)
- : model_(new ExtensionShelfModel(browser)) {
+ : model_(browser->extension_shelf_model()) {
model_->AddObserver(this);
LoadFromModel();
EnableCanvasFlippingForRTLUI(true);
}
ExtensionShelf::~ExtensionShelf() {
- int count = model_->count();
- for (int i = 0; i < count; ++i) {
- delete ToolstripAtIndex(i);
- model_->SetToolstripDataAt(i, NULL);
+ if (model_) {
+ int count = model_->count();
+ for (int i = 0; i < count; ++i) {
+ delete ToolstripAtIndex(i);
+ model_->SetToolstripDataAt(i, NULL);
+ }
+ model_->RemoveObserver(this);
}
- model_->RemoveObserver(this);
}
void ExtensionShelf::Paint(gfx::Canvas* canvas) {
@@ -563,6 +605,8 @@ void ExtensionShelf::ChildPreferredSizeChanged(View* child) {
void ExtensionShelf::Layout() {
if (!GetParent())
return;
+ if (!model_)
+ return;
int x = kLeftMargin;
int y = kTopMargin;
@@ -619,7 +663,7 @@ void ExtensionShelf::SetAccessibleName(const std::wstring& name) {
void ExtensionShelf::ToolstripInsertedAt(ExtensionHost* host,
int index) {
model_->SetToolstripDataAt(index,
- new Toolstrip(this, host, model_->ToolstripInfoAt(index)));
+ new Toolstrip(this, host, model_->ToolstripAt(index).info));
bool had_views = GetChildViewCount() > 0;
ExtensionView* view = host->view();
@@ -653,7 +697,15 @@ void ExtensionShelf::ToolstripMoved(ExtensionHost* host, int from_index,
Layout();
}
-void ExtensionShelf::ToolstripChangedAt(ExtensionHost* toolstrip, int index) {
+void ExtensionShelf::ToolstripChanged(ExtensionShelfModel::iterator toolstrip) {
+ Toolstrip* t = static_cast<Toolstrip*>(toolstrip->data);
+ if (toolstrip->height > 0) {
+ if (!t->expanded()) {
+ t->Expand(toolstrip->height, toolstrip->url);
+ }
+ } else if (t->expanded()) {
+ t->Collapse(toolstrip->url);
+ }
}
void ExtensionShelf::ExtensionShelfEmpty() {
@@ -666,6 +718,16 @@ void ExtensionShelf::ShelfModelReloaded() {
LoadFromModel();
}
+void ExtensionShelf::ShelfModelDeleting() {
+ int count = model_->count();
+ for (int i = 0; i < count; ++i) {
+ delete ToolstripAtIndex(i);
+ model_->SetToolstripDataAt(i, NULL);
+ }
+ model_->RemoveObserver(this);
+ model_ = NULL;
+}
+
void ExtensionShelf::OnExtensionMouseEvent(ExtensionView* view) {
Toolstrip *toolstrip = ToolstripForView(view);
if (toolstrip)
@@ -689,12 +751,23 @@ void ExtensionShelf::DropExtension(Toolstrip* toolstrip, const gfx::Point& pt,
}
if (toolstrip == dest_toolstrip)
return;
- int from = model_->IndexOfToolstrip(toolstrip->host());
- int to = model_->IndexOfToolstrip(dest_toolstrip->host());
+ int from = model_->IndexOfHost(toolstrip->host());
+ int to = model_->IndexOfHost(dest_toolstrip->host());
DCHECK(from != to);
model_->MoveToolstripAt(from, to);
}
+void ExtensionShelf::ExpandToolstrip(ExtensionHost* host, const GURL& url,
+ int height) {
+ ExtensionShelfModel::iterator toolstrip = model_->ToolstripForHost(host);
+ model_->ExpandToolstrip(toolstrip, url, height);
+}
+
+void ExtensionShelf::CollapseToolstrip(ExtensionHost* host, const GURL& url) {
+ ExtensionShelfModel::iterator toolstrip = model_->ToolstripForHost(host);
+ model_->CollapseToolstrip(toolstrip, url);
+}
+
void ExtensionShelf::InitBackground(gfx::Canvas* canvas, const SkRect& subset) {
if (!background_.empty())
return;
@@ -749,7 +822,7 @@ ExtensionShelf::Toolstrip* ExtensionShelf::ToolstripAtX(int x) {
}
ExtensionShelf::Toolstrip* ExtensionShelf::ToolstripAtIndex(int index) {
- return static_cast<Toolstrip*>(model_->ToolstripDataAt(index));
+ return static_cast<Toolstrip*>(model_->ToolstripAt(index).data);
}
ExtensionShelf::Toolstrip* ExtensionShelf::ToolstripForView(
@@ -760,12 +833,11 @@ ExtensionShelf::Toolstrip* ExtensionShelf::ToolstripForView(
if (view == toolstrip->view())
return toolstrip;
}
- NOTREACHED();
return NULL;
}
void ExtensionShelf::LoadFromModel() {
int count = model_->count();
for (int i = 0; i < count; ++i)
- ToolstripInsertedAt(model_->ToolstripAt(i), i);
+ ToolstripInsertedAt(model_->ToolstripAt(i).host, i);
}
diff --git a/chrome/browser/views/extensions/extension_shelf.h b/chrome/browser/views/extensions/extension_shelf.h
index 57a0954..e0dbe5d 100644
--- a/chrome/browser/views/extensions/extension_shelf.h
+++ b/chrome/browser/views/extensions/extension_shelf.h
@@ -27,7 +27,7 @@ class ExtensionShelf : public views::View,
virtual ~ExtensionShelf();
// Get the current model.
- ExtensionShelfModel* model() { return model_.get(); }
+ ExtensionShelfModel* model() { return model_; }
// View
virtual void Paint(gfx::Canvas* canvas);
@@ -50,9 +50,10 @@ class ExtensionShelf : public views::View,
virtual void ToolstripMoved(ExtensionHost* toolstrip,
int from_index,
int to_index);
- virtual void ToolstripChangedAt(ExtensionHost* toolstrip, int index);
+ virtual void ToolstripChanged(ExtensionShelfModel::iterator toolstrip);
virtual void ExtensionShelfEmpty();
virtual void ShelfModelReloaded();
+ virtual void ShelfModelDeleting();
protected:
// View
@@ -66,6 +67,13 @@ class ExtensionShelf : public views::View,
// Dragging toolstrips
void DropExtension(Toolstrip* handle, const gfx::Point& pt, bool cancel);
+ // Expand the specified toolstrip, navigating to |url| if non-empty,
+ // and setting the |height|.
+ void ExpandToolstrip(ExtensionHost* host, const GURL& url, int height);
+
+ // Collapse the specified toolstrip, navigating to |url| if non-empty.
+ void CollapseToolstrip(ExtensionHost* host, const GURL& url);
+
// Inits the background bitmap.
void InitBackground(gfx::Canvas* canvas, const SkRect& subset);
@@ -86,7 +94,7 @@ class ExtensionShelf : public views::View,
SkBitmap background_;
// The model representing the toolstrips on the shelf.
- scoped_ptr<ExtensionShelfModel> model_;
+ ExtensionShelfModel* model_;
// Storage of strings needed for accessibility.
std::wstring accessible_name_;
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index efce62a..120c2ef 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -599,9 +599,13 @@ void BrowserView::SetBounds(const gfx::Rect& bounds) {
}
void BrowserView::Close() {
- BubbleSet::iterator bubble = browser_bubbles_.begin();
- for (; bubble != browser_bubbles_.end(); ++bubble)
+ // BrowserWindowClosing will usually cause the bubble to remove itself from
+ // the set, so we need to iterate in a way that's safe against deletion.
+ for (BubbleSet::iterator i = browser_bubbles_.begin();
+ i != browser_bubbles_.end();) {
+ BubbleSet::iterator bubble = i++;
(*bubble)->BrowserWindowClosing();
+ }
frame_->GetWindow()->Close();
}
@@ -1490,10 +1494,12 @@ void BrowserView::Init() {
status_bubble_.reset(new StatusBubbleViews(GetWidget()));
- extension_shelf_ = new ExtensionShelf(browser_.get());
- extension_shelf_->
- SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_EXTENSIONS));
- AddChildView(extension_shelf_);
+ if (browser_->SupportsWindowFeature(Browser::FEATURE_EXTENSIONSHELF)) {
+ extension_shelf_ = new ExtensionShelf(browser_.get());
+ extension_shelf_->
+ SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_EXTENSIONS));
+ AddChildView(extension_shelf_);
+ }
#if defined(OS_WIN)
InitSystemMenu();
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 4940ed2..eee1158 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -984,6 +984,8 @@
'browser/extensions/extension_tabs_module.h',
'browser/extensions/extension_tabs_module_constants.cc',
'browser/extensions/extension_tabs_module_constants.h',
+ 'browser/extensions/extension_toolstrip_api.cc',
+ 'browser/extensions/extension_toolstrip_api.h',
'browser/extensions/extension_updater.cc',
'browser/extensions/extension_updater.h',
'browser/extensions/extensions_service.cc',
diff --git a/chrome/common/common_resources.grd b/chrome/common/common_resources.grd
index 5ea8f3c..e13c7c1 100644
--- a/chrome/common/common_resources.grd
+++ b/chrome/common/common_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. rw -->
+without changes to the corresponding grd file. -->
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/common_resources.h" type="rc_header">
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index 1561a41..2740069 100755
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -65,7 +65,6 @@
* TODO(rafaelw): Validate event arguments in debug and unittests
* TODO(rafaelw): Support $ref for json_schema validation.
*/
-
[
/**
* chrome.self
@@ -706,5 +705,36 @@
]
}
]
+ },
+
+ /**
+ * chrome.toolstrip
+ */
+ {
+ namespace: "toolstrip",
+ types: [],
+ functions: [
+ {
+ name: "expand",
+ type: "function",
+ description: "",
+ parameters: [
+ {type: "integer", name: "height", minimum: 0},
+ {type: "string", name: "url", optional: true},
+ {type: "function", name: "callback", optional: true}
+ ]
+ },
+ {
+ name: "collapse",
+ type: "function",
+ description: "",
+ parameters: [
+ {type: "string", name: "url", optional: true},
+ {type: "function", name: "callback", optional: true}
+ ]
+ }
+ ],
+ events: [
+ ]
}
]
diff --git a/chrome/common/extensions/docs/toolstrip.html b/chrome/common/extensions/docs/toolstrip.html
new file mode 100644
index 0000000..faf8a37
--- /dev/null
+++ b/chrome/common/extensions/docs/toolstrip.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!-- This page is a placeholder for generated extensions api doc. Note:
+ 1) The <head> information in this page is significant, should be uniform
+ across api docs and should be edited only with knowledge of the
+ templating mechanism.
+ 2) The <body> tag *must* retain id="body"
+ 3) All <body>.innerHTML is genereated as an rendering step. If viewed in a
+ browser, it will be re-generated from the template, json schema and
+ authored overview content.
+ 4) The <body>.innerHTML is also generated by an offline step so that this
+ page may easily be indexed by search engines.
+
+ TODO(rafaelw): Abstract this into a "pageshell" that becomes the single
+ version of page template shell and the "instance" pages (bookmarks.html,
+ etc...) can be generated with a build step.
+-->
+<!-- <html> must retain id="template -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <!-- <head> data is significant and loads the needed libraries and styles -->
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <title jscontent="pageTitle">pageTitle</title>
+ <link href="css/ApiRefStyles.css" rel="stylesheet" type="text/css">
+ <script type="text/javascript"
+ src="../../../third_party/jstemplate/jstemplate_compiled.js">
+ </script>
+ <script type="text/javascript" src="js/api_page_generator.js"></script>
+ <script>
+// Re-render the page if the user-agent is chrome and it is being served as
+// a file:/// scheme. This allows both the initial render to static as well
+// as dynamic re-rendering for developers/doc-writers working on local changes.
+window.onload = function() {
+ var chrome = navigator.userAgent.indexOf("Chrome") > -1;
+ var fileScheme = location.protocol == "file:";
+ var regenerate = chrome && fileScheme;
+ //if (regenerate) {
+ window.renderPage();
+ //}
+}
+ </script>
+ </head>
+ <!-- <body> content is completely generated. Do not edit, as it will be
+ and rewritten. -->
+ <body class="hidden">
+ </body>
+</html>
diff --git a/chrome/renderer/extensions/extension_api_client_unittest.cc b/chrome/renderer/extensions/extension_api_client_unittest.cc
index bd0ce09..5465a5c 100644
--- a/chrome/renderer/extensions/extension_api_client_unittest.cc
+++ b/chrome/renderer/extensions/extension_api_client_unittest.cc
@@ -518,3 +518,44 @@ TEST_F(ExtensionAPIClientTest, EnablePageAction) {
"pageActions.disableForTab",
"[\"dummy\",{\"tabId\":0,\"url\":\"http://foo/\"}]");
}
+
+TEST_F(ExtensionAPIClientTest, ExpandToolstrip) {
+ ExpectJsPass("chrome.toolstrip.expand(100, 'http://foo/')",
+ "toolstrip.expand",
+ "[100,\"http://foo/\"]");
+ ExpectJsPass("chrome.toolstrip.expand(100, null)",
+ "toolstrip.expand",
+ "[100,null]");
+ ExpectJsPass("chrome.toolstrip.expand(100, 'http://foo/', function(){})",
+ "toolstrip.expand",
+ "[100,\"http://foo/\"]");
+
+ ExpectJsFail("chrome.toolstrip.expand('100', 'http://foo/')",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'integer' but got 'string'.");
+ ExpectJsFail("chrome.toolstrip.expand(100, 100)",
+ "Uncaught Error: Invalid value for argument 1. "
+ "Expected 'string' but got 'integer'.");
+ ExpectJsFail("chrome.toolstrip.expand(100, 'http://foo/', 32)",
+ "Uncaught Error: Invalid value for argument 2. "
+ "Expected 'function' but got 'integer'.");
+}
+
+TEST_F(ExtensionAPIClientTest, CollapseToolstrip) {
+ ExpectJsPass("chrome.toolstrip.collapse('http://foo/')",
+ "toolstrip.collapse",
+ "\"http://foo/\"");
+ ExpectJsPass("chrome.toolstrip.collapse(null)",
+ "toolstrip.collapse",
+ "null");
+ ExpectJsPass("chrome.toolstrip.collapse('http://foo/', function(){})",
+ "toolstrip.collapse",
+ "\"http://foo/\"");
+
+ ExpectJsFail("chrome.toolstrip.collapse(100)",
+ "Uncaught Error: Invalid value for argument 0. "
+ "Expected 'string' but got 'integer'.");
+ ExpectJsFail("chrome.toolstrip.collapse('http://foo/', 32)",
+ "Uncaught Error: Invalid value for argument 1. "
+ "Expected 'function' but got 'integer'.");
+}
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index 5e05578..ec1d32c 100644
--- 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. rw3 -->
+without changes to the corresponding grd file. ek2 -->
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/renderer_resources.h" type="rc_header">
diff --git a/chrome/test/data/extensions/samples/mappy/manifest.json b/chrome/test/data/extensions/samples/mappy/manifest.json
new file mode 100755
index 0000000..b0fe6a8
--- /dev/null
+++ b/chrome/test/data/extensions/samples/mappy/manifest.json
@@ -0,0 +1,11 @@
+{
+ "name": "Mappy",
+ "version": "0.2",
+ "description": "Map stuff",
+ "toolstrips": [
+ "mappy_toolstrip.html"
+ ],
+ "content_scripts": [
+ { "matches": ["http://*/*"], "js": ["mappy_content_script.js"] }
+ ]
+}
diff --git a/chrome/test/data/extensions/samples/mappy/mappy_content_script.js b/chrome/test/data/extensions/samples/mappy/mappy_content_script.js
new file mode 100755
index 0000000..0e1c8e6
--- /dev/null
+++ b/chrome/test/data/extensions/samples/mappy/mappy_content_script.js
@@ -0,0 +1,55 @@
+// find map on demand
+
+console.log("mappy_content_script.js loaded");
+
+var maps_key = "ABQIAAAATfHumDbW3OmRByfquHd3SRTRERdeAiwZ9EeJWta3L_JZVS0bOBRQeZgr4K0xyVKzUdnnuFl8X9PX0w";
+
+chrome.extension.onConnect.addListener(function(port) {
+ //console.log("extension connected");
+ port.onMessage.addListener(function(data) {
+ //console.log("extension sent message");
+ findAddress(port);
+ });
+});
+
+var findAddress = function(port) {
+ var found;
+ var re = /(\d+ [':.,\s\w]*,\s*[A-Za-z]+\s*\d{5}(-\d{4})?)/m;
+ var node = document.body;
+ var done = false;
+ while (!done) {
+ done = true;
+ for (var i = 0; i < node.childNodes.length; ++i) {
+ var child = node.childNodes[i];
+ if (child.textContent.match(re)) {
+ node = child;
+ found = node;
+ done = false;
+ break;
+ }
+ }
+ }
+ if (found) {
+ var text = "";
+ if (found.childNodes.length) {
+ for (var i = 0; i < found.childNodes.length; ++i) {
+ text += found.childNodes[i].textContent + " ";
+ }
+ } else {
+ text = found.textContent;
+ }
+ var match = re.exec(text);
+ if (match && match.length) {
+ console.log("found: " + match[0]);
+ var trim = /\s{2,}/g;
+ var map = match[0].replace(trim, " ");
+ port.postMessage({message:"map", values:[map]});
+ } else {
+ console.log("found bad " + found.textContent);
+ console.log("no match in: " + text);
+ }
+ } else {
+ console.log("no match in " + node.textContent);
+ }
+}
+
diff --git a/chrome/test/data/extensions/samples/mappy/mappy_toolstrip.html b/chrome/test/data/extensions/samples/mappy/mappy_toolstrip.html
new file mode 100755
index 0000000..84be55b
--- /dev/null
+++ b/chrome/test/data/extensions/samples/mappy/mappy_toolstrip.html
@@ -0,0 +1,68 @@
+<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAATfHumDbW3OmRByfquHd3SRTRERdeAiwZ9EeJWta3L_JZVS0bOBRQeZgr4K0xyVKzUdnnuFl8X9PX0w&sensor=false"
+ type="text/javascript"></script>
+<script>
+var maps_key = "ABQIAAAATfHumDbW3OmRByfquHd3SRTRERdeAiwZ9EeJWta3L_JZVS0bOBRQeZgr4K0xyVKzUdnnuFl8X9PX0w";
+
+function xhr_geocode(address) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4 && xhr.status == 200) {
+ console.log(xhr.responseText);
+ var parsed = JSON.parse(xhr.responseText);
+ console.dir(parsed);
+ var point = parsed.Placemark[0].Point.coordinates;
+ var latlng = point[1] + "," + point[0];
+ window.open("http://maps.google.com/staticmap?center=" + latlng +
+ "&markers=" + latlng + "&zoom=14" +
+ "&size=512x512&sensor=false&key=" + maps_key,
+ "mappy_popup",
+ "width=512,height=512");
+ } else {
+ console.log("xhr: " + xhr.readyState);
+ }
+ };
+ var url =
+ "http://maps.google.com/maps/geo?output=json&oe=utf8&sensor=false&key=" +
+ maps_key + "&q=" + address;
+ console.log(url);
+ xhr.open("GET", url);
+ xhr.send(null);
+}
+
+function gclient_geocode(address) {
+ var geocoder = new GClientGeocoder();
+ geocoder.getLatLng(address, function(point) {
+ if (!point) {
+ console.log(address + " not found");
+ } else {
+ var latlng = point.toUrlValue();
+ var url = "http://maps.google.com/staticmap?center=" + latlng +
+ "&markers=" + latlng + "&zoom=14" +
+ "&size=512x512&sensor=false&key=" + maps_key;
+ //window.open(url, "mappy_popup", "width=512,height=512");
+ document.body.style.width = "512px";
+ setTimeout(chrome.toolstrip.expand, 100, 512, url);
+ }
+ });
+}
+
+function map() {
+ chrome.tabs.getSelected(null, function(tab) {
+ var port = chrome.tabs.connect(tab.id);
+ if (!port) {
+ console.log("no port");
+ } else {
+ port.onMessage.addListener(function(data) {
+ var address = data.values[0];
+ //xhr_geocode(address);
+ gclient_geocode(address);
+ });
+ port.postMessage({message: "hello tab: " + tab.id});
+ console.log("sent message");
+ }
+ });
+};
+</script>
+<div class="toolstrip-button" onclick="map()">
+<span>Mappy</span>
+</div>