summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-02 20:19:20 +0000
committererikkay@chromium.org <erikkay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-02 20:19:20 +0000
commit2d8d923da42df61e9d65942945456d4b9304ef8e (patch)
tree21150546eca2423887f1cca5f9fe7a58ef08224a
parent9e0dfa8ae69f71441d62f725441f7075f791bd09 (diff)
downloadchromium_src-2d8d923da42df61e9d65942945456d4b9304ef8e.zip
chromium_src-2d8d923da42df61e9d65942945456d4b9304ef8e.tar.gz
chromium_src-2d8d923da42df61e9d65942945456d4b9304ef8e.tar.bz2
Add simple popup support to browser actions. This will create a popup HTML window that extends below a browser action button when the browser is clicked. When it loses focus, it is automatically dismissed.
BUG=23596 TEST=none Review URL: http://codereview.chromium.org/258011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27889 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_host.cc24
-rw-r--r--chrome/browser/extensions/extension_host.h1
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc33
-rw-r--r--chrome/browser/extensions/extension_process_manager.h16
-rw-r--r--chrome/browser/extensions/extension_shelf_model.cc2
-rw-r--r--chrome/browser/views/browser_actions_container.cc161
-rw-r--r--chrome/browser/views/browser_actions_container.h31
-rw-r--r--chrome/browser/views/browser_bubble.h10
-rw-r--r--chrome/browser/views/browser_bubble_gtk.cc3
-rw-r--r--chrome/browser/views/browser_bubble_win.cc49
-rw-r--r--chrome/browser/views/extensions/extension_popup.cc78
-rw-r--r--chrome/browser/views/extensions/extension_popup.h58
-rw-r--r--chrome/browser/views/extensions/extension_shelf.cc6
-rw-r--r--chrome/browser/views/extensions/extension_view.cc28
-rw-r--r--chrome/browser/views/extensions/extension_view.h10
-rwxr-xr-xchrome/chrome.gyp4
-rw-r--r--chrome/common/extensions/extension.cc31
-rw-r--r--chrome/common/extensions/extension_action.h11
-rw-r--r--chrome/common/extensions/extension_constants.cc9
-rw-r--r--chrome/common/extensions/extension_constants.h6
-rwxr-xr-xchrome/common/view_types.h3
-rwxr-xr-xchrome/test/data/extensions/samples/set_page_color/icon.pngbin0 -> 3622 bytes
-rwxr-xr-xchrome/test/data/extensions/samples/set_page_color/manifest.json12
-rwxr-xr-xchrome/test/data/extensions/samples/set_page_color/popup.html45
-rw-r--r--views/controls/button/menu_button.h2
25 files changed, 575 insertions, 58 deletions
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index 0f3f6d8..3baa908 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -295,21 +295,23 @@ void ExtensionHost::InsertThemeCSS() {
}
void ExtensionHost::DidStopLoading(RenderViewHost* render_view_host) {
- if (!did_stop_loading_) {
- did_stop_loading_ = true;
- LOG(INFO) << "Sending EXTENSION_HOST_DID_STOP_LOADING";
- NotificationService::current()->Notify(
- NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
- Source<Profile>(profile_),
- Details<ExtensionHost>(this));
- }
+ bool notify = !did_stop_loading_;
+ did_stop_loading_ = true;
if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP ||
- extension_host_type_ == ViewType::EXTENSION_MOLE) {
+ extension_host_type_ == ViewType::EXTENSION_MOLE ||
+ extension_host_type_ == ViewType::EXTENSION_POPUP) {
#if defined(TOOLKIT_VIEWS)
if (view_.get())
view_->DidStopLoading();
#endif
}
+ if (notify) {
+ LOG(INFO) << "Sending EXTENSION_HOST_DID_STOP_LOADING";
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
+ Source<Profile>(profile_),
+ Details<ExtensionHost>(this));
+ }
}
void ExtensionHost::DocumentAvailableInMainFrame(RenderViewHost* rvh) {
@@ -488,7 +490,9 @@ void ExtensionHost::RenderViewCreated(RenderViewHost* render_view_host) {
int ExtensionHost::GetBrowserWindowID() const {
int window_id = -1;
- if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP) {
+ if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP ||
+ extension_host_type_ == ViewType::EXTENSION_MOLE ||
+ extension_host_type_ == ViewType::EXTENSION_POPUP) {
window_id = ExtensionTabUtil::GetWindowId(
const_cast<ExtensionHost* >(this)->GetBrowser());
} else if (extension_host_type_ == ViewType::EXTENSION_BACKGROUND_PAGE) {
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index 70690ef..9a0f23e 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -70,6 +70,7 @@ class ExtensionHost : public RenderViewHostDelegate,
bool document_element_available() const {
return document_element_available_;
}
+ Profile* profile() const { return profile_; }
// Sets the the ViewType of this host (e.g. mole, toolstrip).
void SetRenderViewType(ViewType::Type type);
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index cec5a45..9ae47a7 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -57,30 +57,53 @@ ExtensionProcessManager::~ExtensionProcessManager() {
ExtensionHost* ExtensionProcessManager::CreateView(Extension* extension,
const GURL& url,
- Browser* browser) {
+ Browser* browser,
+ ViewType::Type view_type) {
DCHECK(extension);
DCHECK(browser);
ExtensionHost* host =
- new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
- ViewType::EXTENSION_TOOLSTRIP);
+ new ExtensionHost(extension, GetSiteInstanceForURL(url), url, view_type);
host->CreateView(browser);
OnExtensionHostCreated(host, false);
return host;
}
ExtensionHost* ExtensionProcessManager::CreateView(const GURL& url,
- Browser* browser) {
+ Browser* browser,
+ ViewType::Type view_type) {
DCHECK(browser);
ExtensionsService* service =
browsing_instance_->profile()->GetExtensionsService();
if (service) {
Extension* extension = service->GetExtensionByURL(url);
if (extension)
- return CreateView(extension, url, browser);
+ return CreateView(extension, url, browser, view_type);
}
return NULL;
}
+ExtensionHost* ExtensionProcessManager::CreateToolstrip(Extension* extension,
+ const GURL& url,
+ Browser* browser) {
+ return CreateView(extension, url, browser, ViewType::EXTENSION_TOOLSTRIP);
+}
+
+ExtensionHost* ExtensionProcessManager::CreateToolstrip(const GURL& url,
+ Browser* browser) {
+ return CreateView(url, browser, ViewType::EXTENSION_TOOLSTRIP);
+}
+
+ExtensionHost* ExtensionProcessManager::CreatePopup(Extension* extension,
+ const GURL& url,
+ Browser* browser) {
+ return CreateView(extension, url, browser, ViewType::EXTENSION_POPUP);
+}
+
+ExtensionHost* ExtensionProcessManager::CreatePopup(const GURL& url,
+ Browser* browser) {
+ return CreateView(url, browser, ViewType::EXTENSION_POPUP);
+}
+
ExtensionHost* ExtensionProcessManager::CreateBackgroundHost(
Extension* extension, const GURL& url) {
ExtensionHost* host =
diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h
index d2af74d..e154a33 100644
--- a/chrome/browser/extensions/extension_process_manager.h
+++ b/chrome/browser/extensions/extension_process_manager.h
@@ -11,6 +11,7 @@
#include "base/ref_counted.h"
#include "chrome/common/notification_registrar.h"
+#include "chrome/common/view_types.h"
class Browser;
class BrowsingInstance;
@@ -36,8 +37,19 @@ class ExtensionProcessManager : public NotificationObserver {
// profile.
ExtensionHost* CreateView(Extension* extension,
const GURL& url,
- Browser* browser);
- ExtensionHost* CreateView(const GURL& url, Browser* browser);
+ Browser* browser,
+ ViewType::Type view_type);
+ ExtensionHost* CreateView(const GURL& url,
+ Browser* browser,
+ ViewType::Type view_type);
+ ExtensionHost* CreateToolstrip(Extension* extension,
+ const GURL& url,
+ Browser* browser);
+ ExtensionHost* CreateToolstrip(const GURL& url, Browser* browser);
+ ExtensionHost* CreatePopup(Extension* extension,
+ const GURL& url,
+ Browser* browser);
+ ExtensionHost* CreatePopup(const GURL& url, Browser* browser);
// Creates a new UI-less extension instance. Like CreateView, but not
// displayed anywhere.
diff --git a/chrome/browser/extensions/extension_shelf_model.cc b/chrome/browser/extensions/extension_shelf_model.cc
index 10b56b2..dc584d8 100644
--- a/chrome/browser/extensions/extension_shelf_model.cc
+++ b/chrome/browser/extensions/extension_shelf_model.cc
@@ -198,7 +198,7 @@ void ExtensionShelfModel::AddExtension(Extension* extension) {
toolstrip != extension->toolstrips().end(); ++toolstrip) {
GURL url = toolstrip->toolstrip;
ToolstripItem item;
- item.host = manager->CreateView(extension, url, browser_);
+ item.host = manager->CreateToolstrip(extension, url, browser_);
item.info = *toolstrip;
item.data = NULL;
item.height = 0;
diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc
index bae129f..88c2b02 100644
--- a/chrome/browser/views/browser_actions_container.cc
+++ b/chrome/browser/views/browser_actions_container.cc
@@ -10,12 +10,13 @@
#include "chrome/browser/extensions/extension_tabs_module.h"
#include "chrome/browser/image_loading_tracker.h"
#include "chrome/browser/profile.h"
+#include "chrome/browser/views/extensions/extension_popup.h"
#include "chrome/browser/views/toolbar_view.h"
#include "chrome/common/extensions/extension_action.h"
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
#include "third_party/skia/include/core/SkBitmap.h"
-#include "views/controls/button/text_button.h"
+#include "views/controls/button/menu_button.h"
// The size of the icon for page actions.
static const int kIconSize = 30;
@@ -29,7 +30,7 @@ static const int kHorizontalPadding = 4;
// The BrowserActionImageView is a specialization of the TextButton class.
// It acts on a ExtensionAction, in this case a BrowserAction and handles
// loading the image for the button asynchronously on the file thread to
-class BrowserActionImageView : public views::TextButton,
+class BrowserActionImageView : public views::MenuButton,
public views::ButtonListener,
public ImageLoadingTracker::Observer,
public NotificationObserver {
@@ -50,6 +51,26 @@ class BrowserActionImageView : public views::TextButton,
const NotificationSource& source,
const NotificationDetails& details);
+ // MenuButton behavior overrides. These methods all default to TextButton
+ // behavior unless this button is a popup. In that case, it uses MenuButton
+ // behavior. MenuButton has the notion of a child popup being shown where the
+ // button will stay in the pushed state until the "menu" (a popup in this
+ // case) is dismissed.
+ virtual bool Activate();
+ virtual bool OnMousePressed(const views::MouseEvent& e);
+ virtual void OnMouseReleased(const views::MouseEvent& e, bool canceled);
+ virtual bool OnKeyReleased(const views::KeyEvent& e);
+ virtual void OnMouseExited(const views::MouseEvent& event);
+
+ // Does this button's action have a popup?
+ virtual bool IsPopup();
+
+ // Notifications when the popup is hidden or shown by the container.
+ virtual void PopupDidShow();
+ virtual void PopupDidHide();
+
+ const ExtensionAction& browser_action() const { return *browser_action_; }
+
private:
// Called to update the display to match the browser action's state.
void OnStateUpdated();
@@ -80,7 +101,7 @@ class BrowserActionImageView : public views::TextButton,
BrowserActionImageView::BrowserActionImageView(
ExtensionAction* browser_action, Extension* extension,
BrowserActionsContainer* panel)
- : TextButton(this, L""),
+ : MenuButton(this, L"", NULL, false),
browser_action_(browser_action),
browser_action_state_(extension->browser_action_state()),
tracker_(NULL),
@@ -113,7 +134,7 @@ BrowserActionImageView::~BrowserActionImageView() {
void BrowserActionImageView::ButtonPressed(
views::Button* sender, const views::Event& event) {
- panel_->OnBrowserActionExecuted(*browser_action_);
+ panel_->OnBrowserActionExecuted(this);
}
void BrowserActionImageView::OnImageLoaded(SkBitmap* image, size_t index) {
@@ -142,12 +163,76 @@ void BrowserActionImageView::Observe(NotificationType type,
}
}
+bool BrowserActionImageView::IsPopup() {
+ return (browser_action_ && !browser_action_->popup_url().is_empty());
+}
+
+bool BrowserActionImageView::Activate() {
+ if (IsPopup()) {
+ panel_->OnBrowserActionExecuted(this);
+
+ // TODO(erikkay): Run a nested modal loop while the mouse is down to
+ // enable menu-like drag-select behavior.
+
+ // The return value of this method is returned via OnMousePressed.
+ // We need to return false here since we're handing off focus to another
+ // widget/view, and true will grab it right back and try to send events
+ // to us.
+ return false;
+ }
+ return true;
+}
+
+bool BrowserActionImageView::OnMousePressed(const views::MouseEvent& e) {
+ if (IsPopup())
+ return MenuButton::OnMousePressed(e);
+ return TextButton::OnMousePressed(e);
+}
+
+void BrowserActionImageView::OnMouseReleased(const views::MouseEvent& e,
+ bool canceled) {
+ if (IsPopup()) {
+ // TODO(erikkay) this never actually gets called (probably because of the
+ // loss of focus).
+ MenuButton::OnMouseReleased(e, canceled);
+ } else {
+ TextButton::OnMouseReleased(e, canceled);
+ }
+}
+
+bool BrowserActionImageView::OnKeyReleased(const views::KeyEvent& e) {
+ if (IsPopup())
+ return MenuButton::OnKeyReleased(e);
+ return TextButton::OnKeyReleased(e);
+}
+
+void BrowserActionImageView::OnMouseExited(const views::MouseEvent& e) {
+ if (IsPopup())
+ MenuButton::OnMouseExited(e);
+ else
+ TextButton::OnMouseExited(e);
+}
+
+void BrowserActionImageView::PopupDidShow() {
+ SetState(views::CustomButton::BS_PUSHED);
+ menu_visible_ = true;
+}
+
+void BrowserActionImageView::PopupDidHide() {
+ SetState(views::CustomButton::BS_NORMAL);
+ menu_visible_ = false;
+}
+
////////////////////////////////////////////////////////////////////////////////
// BrowserActionsContainer
BrowserActionsContainer::BrowserActionsContainer(
Profile* profile, ToolbarView* toolbar)
- : profile_(profile), toolbar_(toolbar) {
+ : profile_(profile),
+ toolbar_(toolbar),
+ popup_(NULL),
+ popup_button_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) {
ExtensionsService* extension_service = profile->GetExtensionsService();
registrar_.Add(this, NotificationType::EXTENSION_LOADED,
Source<ExtensionsService>(extension_service));
@@ -160,6 +245,7 @@ BrowserActionsContainer::BrowserActionsContainer(
}
BrowserActionsContainer::~BrowserActionsContainer() {
+ HidePopup();
DeleteBrowserActionViews();
}
@@ -201,8 +287,51 @@ void BrowserActionsContainer::OnBrowserActionVisibilityChanged() {
toolbar_->Layout();
}
+void BrowserActionsContainer::HidePopup() {
+ if (popup_) {
+ popup_->Hide();
+ popup_->DetachFromBrowser();
+ delete popup_;
+ popup_ = NULL;
+ popup_button_->PopupDidHide();
+ popup_button_ = NULL;
+ return;
+ }
+}
+
void BrowserActionsContainer::OnBrowserActionExecuted(
- const ExtensionAction& browser_action) {
+ BrowserActionImageView* button) {
+ const ExtensionAction& browser_action = button->browser_action();
+
+ // Popups just display. No notification to the extension.
+ // TODO(erikkay): should there be?
+ if (button->IsPopup()) {
+ // If we're showing the same popup, just hide it and return.
+ bool same_showing = popup_ && button == popup_button_;
+
+ // Always hide the current popup, even if it's not the same.
+ // Only one popup should be visible at a time.
+ HidePopup();
+
+ if (same_showing)
+ return;
+
+ gfx::Point origin;
+ View::ConvertPointToWidget(button, &origin);
+ gfx::Rect rect = bounds();
+ rect.set_x(origin.x());
+ rect.set_y(origin.y());
+ popup_ = ExtensionPopup::Show(browser_action.popup_url(),
+ toolbar_->browser(),
+ rect,
+ browser_action.popup_height());
+ popup_->set_delegate(this);
+ popup_button_ = button;
+ popup_button_->PopupDidShow();
+ return;
+ }
+
+ // Otherwise, we send the action to the extension.
int window_id = ExtensionTabUtil::GetWindowId(toolbar_->browser());
ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
profile_, browser_action.extension_id(), window_id);
@@ -235,3 +364,23 @@ void BrowserActionsContainer::Observe(NotificationType type,
NOTREACHED() << L"Received unexpected notification";
}
}
+
+void BrowserActionsContainer::BubbleBrowserWindowMoved(BrowserBubble* bubble) {
+}
+
+void BrowserActionsContainer::BubbleBrowserWindowClosing(
+ BrowserBubble* bubble) {
+ HidePopup();
+}
+
+void BrowserActionsContainer::BubbleGotFocus(BrowserBubble* bubble) {
+}
+
+void BrowserActionsContainer::BubbleLostFocus(BrowserBubble* bubble) {
+ // This is a bit annoying. If you click on the button that generated the
+ // current popup, then we first get this lost focus message, and then
+ // we get the click action. This results in the popup being immediately
+ // shown again. To workaround this, we put in a delay.
+ MessageLoop::current()->PostTask(FROM_HERE,
+ task_factory_.NewRunnableMethod(&BrowserActionsContainer::HidePopup));
+}
diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h
index fd83437..178a3a2 100644
--- a/chrome/browser/views/browser_actions_container.h
+++ b/chrome/browser/views/browser_actions_container.h
@@ -7,15 +7,19 @@
#include <vector>
+#include "base/task.h"
+#include "chrome/browser/views/browser_bubble.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
#include "views/view.h"
+class BrowserActionImageView;
class ExtensionAction;
+class ExtensionPopup;
class Profile;
class ToolbarView;
namespace views {
-class TextButton;
+class MenuButton;
}
////////////////////////////////////////////////////////////////////////////////
@@ -26,7 +30,8 @@ class TextButton;
//
////////////////////////////////////////////////////////////////////////////////
class BrowserActionsContainer : public views::View,
- public NotificationObserver {
+ public NotificationObserver,
+ public BrowserBubble::Delegate {
public:
BrowserActionsContainer(Profile* profile, ToolbarView* toolbar);
virtual ~BrowserActionsContainer();
@@ -43,7 +48,7 @@ class BrowserActionsContainer : public views::View,
void OnBrowserActionVisibilityChanged();
// Called when the user clicks on the browser action icon.
- void OnBrowserActionExecuted(const ExtensionAction& browser_action);
+ void OnBrowserActionExecuted(BrowserActionImageView* button);
// Overridden from views::View:
virtual gfx::Size GetPreferredSize();
@@ -54,9 +59,18 @@ class BrowserActionsContainer : public views::View,
const NotificationSource& source,
const NotificationDetails& details);
+ // BrowserBubble::Delegate methods.
+ virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble);
+ virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble);
+ virtual void BubbleGotFocus(BrowserBubble* bubble);
+ virtual void BubbleLostFocus(BrowserBubble* bubble);
+
private:
+ // Hide the current popup.
+ void HidePopup();
+
// The vector of browser actions (icons/image buttons for each action).
- std::vector<views::TextButton*> browser_action_views_;
+ std::vector<views::MenuButton*> browser_action_views_;
NotificationRegistrar registrar_;
@@ -65,6 +79,15 @@ class BrowserActionsContainer : public views::View,
// The toolbar that owns us.
ToolbarView* toolbar_;
+ // The current popup and the button it came from. NULL if no popup.
+ ExtensionPopup* popup_;
+
+ // The button that triggered the current popup (just a reference to a button
+ // from browser_action_views_).
+ BrowserActionImageView* popup_button_;
+
+ ScopedRunnableMethodFactory<BrowserActionsContainer> task_factory_;
+
DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainer);
};
diff --git a/chrome/browser/views/browser_bubble.h b/chrome/browser/views/browser_bubble.h
index 1ffacb0..327d4fd 100644
--- a/chrome/browser/views/browser_bubble.h
+++ b/chrome/browser/views/browser_bubble.h
@@ -24,6 +24,12 @@ class BrowserBubble {
// Called with the Browser Window that this bubble is attached to is
// about to close.
virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble) = 0;
+
+ // Called when the bubble became active / got focus.
+ virtual void BubbleGotFocus(BrowserBubble* bubble) {}
+
+ // Called when the bubble became inactive / lost focus.
+ virtual void BubbleLostFocus(BrowserBubble* bubble) {}
};
// Note that the bubble will size itself to the preferred size of |view|.
@@ -54,8 +60,8 @@ class BrowserBubble {
virtual void BrowserWindowClosing();
// Show or hide the bubble.
- void Show();
- void Hide();
+ virtual void Show(bool activate);
+ virtual void Hide();
bool visible() const { return visible_; }
// The contained view.
diff --git a/chrome/browser/views/browser_bubble_gtk.cc b/chrome/browser/views/browser_bubble_gtk.cc
index d73f1df..43a6af3 100644
--- a/chrome/browser/views/browser_bubble_gtk.cc
+++ b/chrome/browser/views/browser_bubble_gtk.cc
@@ -30,9 +30,10 @@ void BrowserBubble::MovePopup(int x, int y, int w, int h) {
pop->SetBounds(gfx::Rect(x, y, w, h));
}
-void BrowserBubble::Show() {
+void BrowserBubble::Show(bool activate) {
if (visible_)
return;
+ // TODO(port) respect activate flag.
views::WidgetGtk* pop = static_cast<views::WidgetGtk*>(popup_);
pop->Show();
visible_ = true;
diff --git a/chrome/browser/views/browser_bubble_win.cc b/chrome/browser/views/browser_bubble_win.cc
index e1a1a06..8cae2da 100644
--- a/chrome/browser/views/browser_bubble_win.cc
+++ b/chrome/browser/views/browser_bubble_win.cc
@@ -6,12 +6,53 @@
#include "app/l10n_util_win.h"
#include "chrome/browser/views/frame/browser_view.h"
+#include "views/widget/root_view.h"
#include "views/widget/widget_win.h"
#include "views/window/window.h"
+class BubbleWidget : public views::WidgetWin
+{
+public:
+ BubbleWidget(BrowserBubble* bubble) : bubble_(bubble), closed_(false) {
+ }
+
+ void Show(bool activate) {
+ if (activate)
+ ShowWindow(SW_SHOW);
+ else
+ views::WidgetWin::Show();
+ }
+
+ void Close() {
+ if (closed_)
+ return;
+ closed_ = true;
+ views::WidgetWin::Close();
+ }
+
+ void OnActivate(UINT action, BOOL minimized, HWND window) {
+ BrowserBubble::Delegate* delegate = bubble_->delegate();
+ if (!delegate)
+ return;
+
+ if (action == WA_INACTIVE && !closed_) {
+ delegate->BubbleLostFocus(bubble_);
+ } else if (action == WA_ACTIVE) {
+ delegate->BubbleGotFocus(bubble_);
+ }
+ }
+
+private:
+ bool closed_;
+ BrowserBubble* bubble_;
+};
+
void BrowserBubble::InitPopup() {
gfx::NativeWindow native_window = frame_->GetWindow()->GetNativeWindow();
- views::WidgetWin* pop = new views::WidgetWin();
+
+ // popup_ is a Widget, but we need to do some WidgetWin stuff first, then
+ // we'll assign it into popup_.
+ views::WidgetWin* pop = new BubbleWidget(this);
pop->set_window_style(WS_POPUP);
#if 0
@@ -38,11 +79,11 @@ void BrowserBubble::MovePopup(int x, int y, int w, int h) {
pop->MoveWindow(x, y, w, h);
}
-void BrowserBubble::Show() {
+void BrowserBubble::Show(bool activate) {
if (visible_)
return;
- views::WidgetWin* pop = static_cast<views::WidgetWin*>(popup_);
- pop->Show();
+ BubbleWidget* pop = static_cast<BubbleWidget*>(popup_);
+ pop->Show(activate);
visible_ = true;
}
diff --git a/chrome/browser/views/extensions/extension_popup.cc b/chrome/browser/views/extensions/extension_popup.cc
new file mode 100644
index 0000000..b938b27
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_popup.cc
@@ -0,0 +1,78 @@
+// 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/views/extensions/extension_popup.h"
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+
+ExtensionPopup::ExtensionPopup(ExtensionHost* host,
+ views::Widget* frame,
+ const gfx::Rect& relative_to)
+ : BrowserBubble(host->view(),
+ frame,
+ gfx::Point()),
+ relative_to_(relative_to),
+ extension_host_(host) {
+ registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
+ Source<Profile>(host->profile()));
+}
+
+ExtensionPopup::~ExtensionPopup() {
+}
+
+void ExtensionPopup::Show() {
+ ResizeToView();
+
+ // Anchor on the lower right corner and extend to the left.
+ SetBounds(relative_to_.right() - width(), relative_to_.bottom(),
+ width(), height());
+ BrowserBubble::Show(true);
+}
+
+void ExtensionPopup::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::EXTENSION_HOST_DID_STOP_LOADING) {
+ // Once we receive did stop loading, the content will be complete and
+ // the width will have been computed. Now it's safe to show.
+ if (extension_host_.get() == Details<ExtensionHost>(details).ptr())
+ Show();
+ } else {
+ NOTREACHED() << L"Received unexpected notification";
+ }
+}
+
+// static
+ExtensionPopup* ExtensionPopup::Show(const GURL& url, Browser* browser,
+ const gfx::Rect& relative_to,
+ int height) {
+ ExtensionProcessManager* manager =
+ browser->profile()->GetExtensionProcessManager();
+ DCHECK(manager);
+ if (!manager)
+ return NULL;
+
+ ExtensionHost* host = manager->CreatePopup(url, browser);
+ views::Widget* frame = BrowserView::GetBrowserViewForNativeWindow(
+ browser->window()->GetNativeHandle())->GetWidget();
+ ExtensionPopup* popup = new ExtensionPopup(host, frame, relative_to);
+ gfx::Size sz = host->view()->GetPreferredSize();
+ sz.set_height(height);
+ host->view()->SetPreferredSize(sz);
+
+ // If the host had somehow finished loading, then we'd miss the notification
+ // and not show. This seems to happen in single-process mode.
+ if (host->did_stop_loading())
+ popup->Show();
+
+ return popup;
+}
diff --git a/chrome/browser/views/extensions/extension_popup.h b/chrome/browser/views/extensions/extension_popup.h
new file mode 100644
index 0000000..cdefc8e
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_popup.h
@@ -0,0 +1,58 @@
+// 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_VIEWS_EXTENSIONS_EXTENSION_POPUP_H_
+#define CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_POPUP_H_
+
+#include "googleurl/src/gurl.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/views/browser_bubble.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+class Browser;
+class ExtensionHost;
+
+class ExtensionPopup : public BrowserBubble,
+ public NotificationObserver {
+ public:
+ virtual ~ExtensionPopup();
+
+ // Create and show a popup with |url| positioned below |relative_to| in
+ // |browser| coordinates. This is anchored to the lower right corner of the
+ // rect, extending to the left, just like the wrench and page menus.
+ //
+ // The actual display of the popup is delayed until the page contents
+ // finish loading in order to minimize UI flashing and resizing.
+ static ExtensionPopup* Show(const GURL& url, Browser* browser,
+ const gfx::Rect& relative_to,
+ int height);
+
+ ExtensionHost* host() const { return extension_host_.get(); }
+
+ // BrowserBubble overrides.
+ virtual void Show();
+
+ // NotificationObserver overrides.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ ExtensionPopup(ExtensionHost* host,
+ views::Widget* frame,
+ const gfx::Rect& relative_to);
+
+ // The area on the screen that the popup should be positioned relative to.
+ const gfx::Rect relative_to_;
+
+ // The contained host for the view.
+ scoped_ptr<ExtensionHost> extension_host_;
+
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionPopup);
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_H_
diff --git a/chrome/browser/views/extensions/extension_shelf.cc b/chrome/browser/views/extensions/extension_shelf.cc
index eee60a1..a586da32 100644
--- a/chrome/browser/views/extensions/extension_shelf.cc
+++ b/chrome/browser/views/extensions/extension_shelf.cc
@@ -587,7 +587,7 @@ void ExtensionShelf::Toolstrip::ShowWindow() {
LayoutWindow();
if (!window_visible())
- window_->Show();
+ window_->Show(false); // |false| means show, but don't activate.
}
void ExtensionShelf::Toolstrip::DoHideShelfHandle() {
@@ -611,7 +611,6 @@ void ExtensionShelf::Toolstrip::Expand(int height, const GURL& url) {
DCHECK(!expanded_);
expanded_ = true;
- view()->set_is_toolstrip(!expanded_);
ShowWindow();
bool navigate = (!url.is_empty() && url != host_->GetURL());
@@ -638,7 +637,6 @@ void ExtensionShelf::Toolstrip::Expand(int height, const GURL& url) {
void ExtensionShelf::Toolstrip::Collapse(const GURL& url) {
DCHECK(expanded_);
expanded_ = false;
- view()->set_is_toolstrip(!expanded_);
if (window_visible())
mole_animation_->Hide();
@@ -1076,7 +1074,7 @@ gfx::Size ExtensionShelf::LayoutItems(bool compute_bounds_only) {
if (clipped)
pref.set_width(std::max(0, max_x - x));
if (view == toolstrip->view())
- toolstrip->view()->set_is_clipped(clipped);
+ toolstrip->view()->SetIsClipped(clipped);
view->SetBounds(x, y, pref.width(), content_height);
view->Layout();
if (toolstrip->window_visible())
diff --git a/chrome/browser/views/extensions/extension_view.cc b/chrome/browser/views/extensions/extension_view.cc
index 24ce5f1..20fc7b1 100644
--- a/chrome/browser/views/extensions/extension_view.cc
+++ b/chrome/browser/views/extensions/extension_view.cc
@@ -15,7 +15,7 @@
ExtensionView::ExtensionView(ExtensionHost* host, Browser* browser)
: host_(host), browser_(browser),
initialized_(false), pending_preferred_width_(0), container_(NULL),
- did_stop_loading_(false), is_clipped_(false), is_toolstrip_(true) {
+ is_clipped_(false) {
host_->set_view(this);
}
@@ -35,10 +35,17 @@ RenderViewHost* ExtensionView::render_view_host() const {
}
void ExtensionView::DidStopLoading() {
- did_stop_loading_ = true;
ShowIfCompletelyLoaded();
}
+void ExtensionView::SetIsClipped(bool is_clipped) {
+ if (is_clipped_ != is_clipped) {
+ is_clipped_ = is_clipped;
+ if (IsVisible())
+ ShowIfCompletelyLoaded();
+ }
+}
+
void ExtensionView::SetVisible(bool is_visible) {
if (is_visible != IsVisible()) {
NativeViewHost::SetVisible(is_visible);
@@ -96,12 +103,17 @@ void ExtensionView::CreateWidgetHostView() {
}
void ExtensionView::ShowIfCompletelyLoaded() {
- // We wait to show the ExtensionView until it has loaded, our parent has
- // given us a background and css has been inserted into page. These can happen
- // in different orders.
- if (!IsVisible() && host_->did_stop_loading() && render_view_host()->view() &&
- !is_clipped_ && did_stop_loading_ &&
- !render_view_host()->view()->background().empty()) {
+ if (IsVisible() || is_clipped_)
+ return;
+
+ // We wait to show the ExtensionView until it has loaded, and the view has
+ // actually been created. These can happen in different orders.
+ if (host_->did_stop_loading() && initialized_) {
+ // For toolstrips, also wait until our parent has given us a background.
+ if (host_->GetRenderViewType() == ViewType::EXTENSION_TOOLSTRIP &&
+ render_view_host()->view()->background().empty()) {
+ return;
+ }
SetVisible(true);
UpdatePreferredWidth(pending_preferred_width_);
}
diff --git a/chrome/browser/views/extensions/extension_view.h b/chrome/browser/views/extensions/extension_view.h
index fc1ef90..3eceee0 100644
--- a/chrome/browser/views/extensions/extension_view.h
+++ b/chrome/browser/views/extensions/extension_view.h
@@ -37,9 +37,7 @@ class ExtensionView : public views::NativeViewHost {
Extension* extension() const;
RenderViewHost* render_view_host() const;
void DidStopLoading();
- void set_is_clipped(bool is_clipped) { is_clipped_ = is_clipped; }
- bool is_toolstrip() const { return is_toolstrip_; }
- void set_is_toolstrip(bool is) { is_toolstrip_ = is; }
+ void SetIsClipped(bool is_clipped);
// Notification from ExtensionHost.
void UpdatePreferredWidth(int pref_width);
@@ -94,15 +92,9 @@ class ExtensionView : public views::NativeViewHost {
// Note: the view does not own its container.
ExtensionContainer* container_;
- // Whether the RenderView has finished loading.
- bool did_stop_loading_;
-
// Whether this extension view is clipped.
bool is_clipped_;
- // Whether this view is currently displaying in toolstrip mode.
- bool is_toolstrip_;
-
DISALLOW_COPY_AND_ASSIGN(ExtensionView);
};
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 7a20754..8214db3 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -2086,6 +2086,8 @@
'browser/views/event_utils.h',
'browser/views/extensions/extension_install_prompt.cc',
'browser/views/extensions/extension_pack_dialog.cc',
+ 'browser/views/extensions/extension_popup.cc',
+ 'browser/views/extensions/extension_popup.h',
'browser/views/extensions/extension_shelf.cc',
'browser/views/extensions/extension_shelf.h',
'browser/views/extensions/extension_view.cc',
@@ -2597,6 +2599,8 @@
['include', '^browser/views/dragged_tab_controller.h'],
['include', '^browser/views/event_utils.cc'],
['include', '^browser/views/event_utils.h'],
+ ['include', '^browser/views/extensions/extension_popup.cc'],
+ ['include', '^browser/views/extensions/extension_popup.h'],
['include', '^browser/views/extensions/extension_shelf.cc'],
['include', '^browser/views/extensions/extension_shelf.h'],
['include', '^browser/views/extensions/extension_view.cc'],
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 9760efe..2ac504e 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -372,6 +372,37 @@ ExtensionAction* Extension::LoadExtensionActionHelper(
}
result->set_name(name);
+ // Read the action's |popup| (optional).
+ DictionaryValue* popup = NULL;
+ if (page_action->HasKey(keys::kPageActionPopup) &&
+ !page_action->GetDictionary(keys::kPageActionPopup, &popup)) {
+ *error = errors::kInvalidPageActionPopup;
+ return NULL;
+ }
+ if (popup) {
+ std::string url_str;
+ if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPageActionPopupPath, "<missing>");
+ return NULL;
+ }
+ GURL url = GetResourceURL(url_str);
+ if (!url.is_valid()) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPageActionPopupPath, url_str);
+ return NULL;
+ }
+ result->set_popup_url(url);
+
+ int height;
+ if (!popup->GetInteger(keys::kPageActionPopupHeight, &height)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPageActionPopupHeight, "<missing>");
+ return NULL;
+ }
+ result->set_popup_height(height);
+ }
+
return result.release();
}
diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h
index f091487..630671c 100644
--- a/chrome/common/extensions/extension_action.h
+++ b/chrome/common/extensions/extension_action.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "googleurl/src/gurl.h"
class ExtensionAction {
public:
@@ -42,6 +43,12 @@ class ExtensionAction {
icon_paths_.push_back(icon_path);
}
+ const GURL& popup_url() const { return popup_url_; }
+ void set_popup_url(const GURL& url) { popup_url_ = url; }
+
+ const int popup_height() const { return popup_height_; }
+ void set_popup_height(int height) { popup_height_ = height; }
+
private:
static int next_command_id_;
@@ -65,6 +72,10 @@ class ExtensionAction {
// An integer for use with the browser's command system. These should always
// be in the range [IDC_BROWSER_ACTION_FIRST, IDC_BROWSER_ACTION_LAST].
int command_id_;
+
+ // If the action has a popup, it has a URL and a height.
+ GURL popup_url_;
+ int popup_height_;
};
typedef std::map<std::string, ExtensionAction*> ExtensionActionMap;
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 8281d42..228197b 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -20,6 +20,9 @@ const wchar_t* kName = L"name";
const wchar_t* kPageActionId = L"id";
const wchar_t* kPageActions = L"page_actions";
const wchar_t* kPageActionIcons = L"icons";
+const wchar_t* kPageActionPopup = L"popup";
+const wchar_t* kPageActionPopupHeight = L"height";
+const wchar_t* kPageActionPopupPath = L"path";
const wchar_t* kPermissions = L"permissions";
const wchar_t* kPlugins = L"plugins";
const wchar_t* kPluginsPath = L"path";
@@ -99,6 +102,12 @@ const char* kInvalidPageActionIconPaths =
"Required value 'page_actions[*].icons' is missing or invalid.";
const char* kInvalidPageActionId =
"Required value 'id' is missing or invalid.";
+const char* kInvalidPageActionPopup =
+ "Invalid type for page action popup.";
+const char* kInvalidPageActionPopupHeight =
+ "Invalid value for page action popup height [*].";
+const char* kInvalidPageActionPopupPath =
+ "Invalid value for page action popup path [*].";
const char* kInvalidPageActionTypeValue =
"Invalid value for 'page_actions[*].type', expected 'tab' or 'permanent'.";
const char* kInvalidPermissions =
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index 8b321b6..125a47f 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -21,6 +21,9 @@ namespace extension_manifest_keys {
extern const wchar_t* kPageActionId;
extern const wchar_t* kPageActions;
extern const wchar_t* kPageActionIcons;
+ extern const wchar_t* kPageActionPopup;
+ extern const wchar_t* kPageActionPopupHeight;
+ extern const wchar_t* kPageActionPopupPath;
extern const wchar_t* kPermissions;
extern const wchar_t* kPlugins;
extern const wchar_t* kPluginsPath;
@@ -87,6 +90,9 @@ namespace extension_manifest_errors {
extern const char* kInvalidPageActionIconPath;
extern const char* kInvalidPageActionIconPaths;
extern const char* kInvalidPageActionId;
+ extern const char* kInvalidPageActionPopup;
+ extern const char* kInvalidPageActionPopupHeight;
+ extern const char* kInvalidPageActionPopupPath;
extern const char* kInvalidPageActionTypeValue;
extern const char* kInvalidPermissions;
extern const char* kInvalidPermission;
diff --git a/chrome/common/view_types.h b/chrome/common/view_types.h
index 7272250..c010244 100755
--- a/chrome/common/view_types.h
+++ b/chrome/common/view_types.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// 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.
@@ -16,6 +16,7 @@ class ViewType {
EXTENSION_TOOLSTRIP,
EXTENSION_MOLE,
EXTENSION_BACKGROUND_PAGE,
+ EXTENSION_POPUP,
DEV_TOOLS_UI,
INTERSTITIAL_PAGE,
};
diff --git a/chrome/test/data/extensions/samples/set_page_color/icon.png b/chrome/test/data/extensions/samples/set_page_color/icon.png
new file mode 100755
index 0000000..1f1c906
--- /dev/null
+++ b/chrome/test/data/extensions/samples/set_page_color/icon.png
Binary files differ
diff --git a/chrome/test/data/extensions/samples/set_page_color/manifest.json b/chrome/test/data/extensions/samples/set_page_color/manifest.json
new file mode 100755
index 0000000..081681a
--- /dev/null
+++ b/chrome/test/data/extensions/samples/set_page_color/manifest.json
@@ -0,0 +1,12 @@
+{
+ "name": "A browser action with a popup that changes the page color.",
+ "version": "1.0",
+ "permissions": [
+ "tabs", "http://*/*"
+ ],
+ "browser_action": {
+ "name": "Set this page's color.",
+ "icons": ["icon.png"],
+ "popup": { "path": "popup.html", "height": 78 }
+ }
+} \ No newline at end of file
diff --git a/chrome/test/data/extensions/samples/set_page_color/popup.html b/chrome/test/data/extensions/samples/set_page_color/popup.html
new file mode 100755
index 0000000..e781b9b
--- /dev/null
+++ b/chrome/test/data/extensions/samples/set_page_color/popup.html
@@ -0,0 +1,45 @@
+<style>
+body {
+ overflow: hidden;
+ margin: 0px;
+ padding: 0px;
+ background: #cccccc;
+ border: 1px solid black;
+}
+div {
+ cursor: pointer;
+ text-align: center;
+ padding: 1px 3px;
+ font: menu;
+ width: 100px;
+}
+div:hover {
+ background: #aaaaaa;
+}
+#red {
+ border: 1px solid red;
+ color: red;
+}
+#blue {
+ border: 1px solid blue;
+ color: blue;
+}
+#green {
+ border: 1px solid green;
+ color: green;
+}
+#yellow {
+ border: 1px solid yellow;
+ color: yellow;
+}
+</style>
+<script>
+function click(color) {
+ chrome.tabs.executeScript(null,
+ {code:"document.body.bgColor='" + color.id + "'"});
+}
+</script>
+<div onclick="click(this)" id="red">red</div>
+<div onclick="click(this)" id="blue">blue</div>
+<div onclick="click(this)" id="green">green</div>
+<div onclick="click(this)" id="yellow">yellow</div>
diff --git a/views/controls/button/menu_button.h b/views/controls/button/menu_button.h
index e17f65d..1f5f8e8 100644
--- a/views/controls/button/menu_button.h
+++ b/views/controls/button/menu_button.h
@@ -47,7 +47,7 @@ class MenuButton : public TextButton {
// These methods are overriden to implement a simple push button
// behavior
virtual bool OnMousePressed(const MouseEvent& e);
- void OnMouseReleased(const MouseEvent& e, bool canceled);
+ virtual void OnMouseReleased(const MouseEvent& e, bool canceled);
virtual bool OnKeyReleased(const KeyEvent& e);
virtual void OnMouseExited(const MouseEvent& event);