diff options
author | erikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-04 16:44:43 +0000 |
---|---|---|
committer | erikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-04 16:44:43 +0000 |
commit | 9c45b718e0b24664945c98d7a6315e4666fe7c22 (patch) | |
tree | 8697f8b5890b4a0f6121a76adf17a00045943068 | |
parent | 79ae821680fd8d84cc567063ead475072b799bc9 (diff) | |
download | chromium_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
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&v=2&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> |