summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorrafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-07 02:51:18 +0000
committerrafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-07 02:51:18 +0000
commitbb74cb05646499f540935db6b23b0d603e14aa22 (patch)
tree48e7417391bb0554508102bdb0464dd332222d1d /chrome
parent7978508d53990b762518d993a1f42dbd3dfc429a (diff)
downloadchromium_src-bb74cb05646499f540935db6b23b0d603e14aa22.zip
chromium_src-bb74cb05646499f540935db6b23b0d603e14aa22.tar.gz
chromium_src-bb74cb05646499f540935db6b23b0d603e14aa22.tar.bz2
Reland Extension Installed InfoBubble
(fix mac build breakage) This creates UI feedback upon successful installation of an extension. An InfoBubble is shown containing the install icon and some information about how to manage extensions. TEST=Install a packaged extension. Verify the InfoBubble is shown, with the install icon and some description. The InfoBubble should disappear when the bubble looses focus (click elsewhere). For a browserAction, the bubble should point to the browserAction icon. For a pageAction **that has a "default_icon" set in it's manifest (see the samples/subscribe_page_action in this CL)**, it should point to a temporarily shown pageAction icon that will be hidden when the bubble closes. Otherwise it should point to the wrench menu. BUG=21412 TBR=willchan Review URL: http://codereview.chromium.org/376014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31365 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd13
-rw-r--r--chrome/browser/extensions/extension_install_ui.cc25
-rw-r--r--chrome/browser/extensions/extension_install_ui.h6
-rw-r--r--chrome/browser/views/browser_actions_container.cc12
-rw-r--r--chrome/browser/views/browser_actions_container.h3
-rw-r--r--chrome/browser/views/extensions/extension_installed_bubble.cc229
-rw-r--r--chrome/browser/views/extensions/extension_installed_bubble.h81
-rw-r--r--chrome/browser/views/info_bubble.cc37
-rw-r--r--chrome/browser/views/info_bubble.h16
-rw-r--r--chrome/browser/views/location_bar_view.cc36
-rw-r--r--chrome/browser/views/location_bar_view.h19
-rwxr-xr-xchrome/chrome.gyp4
-rwxr-xr-xchrome/common/extensions/docs/static/pageAction.html6
-rw-r--r--chrome/test/data/extensions/samples/subscribe_page_action/background.html47
-rw-r--r--chrome/test/data/extensions/samples/subscribe_page_action/manifest.json7
15 files changed, 482 insertions, 59 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f510295..dc9760a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2603,6 +2603,19 @@ each locale. -->
This extension will have full access to your computer and private data.
</message>
+ <!-- Extension installed bubble -->
+ <message name="IDS_EXTENSION_INSTALLED_HEADING" desc="First line in the content area of the extension installed bubble. Instructs that the extension was installed.">
+ <ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph> is now installed
+ </message>
+
+ <message name="IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO" desc="Text displayed in the InfoBubble which explains that the UI of this extension is a Page Action icon which may appear for some pages.">
+ This icon will be visible when the extension can act on the current page.
+ </message>
+
+ <message name="IDS_EXTENSION_INSTALLED_MANAGE_INFO" desc="Text displayed in the InfoBubble with instructives on how to find the chrome://extensions/ management page">
+ You can manage your installed extensions by clicking on the wrench menu, and then "Extensions".
+ </message>
+
<!-- chrome://extensions page -->
<message name="IDS_EXTENSIONS_DEVELOPER_MODE_LINK" desc="Text of the link for developer mode.">
Developer mode
diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc
index 1712791..ae13fc5 100644
--- a/chrome/browser/extensions/extension_install_ui.cc
+++ b/chrome/browser/extensions/extension_install_ui.cc
@@ -16,6 +16,9 @@
#include "chrome/browser/extensions/theme_installed_infobar_delegate.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
+#if defined(TOOLKIT_VIEWS) // TODO(port)
+#include "chrome/browser/views/extensions/extension_installed_bubble.h"
+#endif // TOOLKIT_VIEWS
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_service.h"
#include "grit/browser_resources.h"
@@ -113,19 +116,35 @@ void ExtensionInstallUI::ConfirmInstall(Delegate* delegate,
install_icon = ResourceBundle::GetSharedInstance().GetBitmapNamed(
IDR_EXTENSIONS_SECTION);
}
+ icon_ = *install_icon;
NotificationService* service = NotificationService::current();
service->Notify(NotificationType::EXTENSION_WILL_SHOW_CONFIRM_DIALOG,
Source<ExtensionInstallUI>(this),
NotificationService::NoDetails());
- ShowExtensionInstallPrompt(profile_, delegate, extension, install_icon,
+ ShowExtensionInstallPrompt(profile_, delegate, extension, &icon_,
GetInstallWarning(extension));
-
}
void ExtensionInstallUI::OnInstallSuccess(Extension* extension) {
- ShowThemeInfoBar(extension);
+ if (extension->IsTheme()) {
+ ShowThemeInfoBar(extension);
+ return;
+ }
+
+#if defined(TOOLKIT_VIEWS)
+ // GetLastActiveWithProfile will fail on the build bots. This needs to
+ // implemented differently if any test is created which depends on
+ // ExtensionInstalledBubble showing.
+ Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
+ if (!browser)
+ return;
+
+ ExtensionInstalledBubble::Show(extension, browser, icon_);
+#else
+// TODO(port) crbug.com/26973 (linux) crbug.com/26974 (mac)
+#endif // TOOLKIT_VIEWS
}
void ExtensionInstallUI::OnInstallFailure(const std::string& error) {
diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h
index 18811a5..f2ddf8d 100644
--- a/chrome/browser/extensions/extension_install_ui.h
+++ b/chrome/browser/extensions/extension_install_ui.h
@@ -8,6 +8,8 @@
#include "app/gfx/native_widget_types.h"
#include "base/file_path.h"
#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
#include <string>
@@ -17,7 +19,6 @@ class MessageLoop;
class Profile;
class InfoBarDelegate;
class SandboxedExtensionUnpacker;
-class SkBitmap;
class TabContents;
// Displays all the UI around extension installation.
@@ -42,7 +43,7 @@ class ExtensionInstallUI {
const std::wstring& warning_text);
static void ShowExtensionInstallError(const std::string& error);
- ExtensionInstallUI(Profile* profile);
+ explicit ExtensionInstallUI(Profile* profile);
// This is called by the installer to verify whether the installation should
// proceed.
@@ -75,6 +76,7 @@ class ExtensionInstallUI {
Profile* profile_;
MessageLoop* ui_loop_;
std::string previous_theme_id_; // Used to undo theme installation.
+ SkBitmap icon_; // The extensions installation icon.
#if defined(TOOLKIT_GTK)
// Also needed to undo theme installation in the linux UI.
diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc
index 23c3402..dbae46b 100644
--- a/chrome/browser/views/browser_actions_container.cc
+++ b/chrome/browser/views/browser_actions_container.cc
@@ -256,6 +256,18 @@ int BrowserActionsContainer::GetCurrentTabId() {
return tab_contents->controller().session_id().id();
}
+BrowserActionView* BrowserActionsContainer::GetBrowserActionView(
+ Extension* extension) {
+ for (std::vector<BrowserActionView*>::iterator iter =
+ browser_action_views_.begin(); iter != browser_action_views_.end();
+ ++iter) {
+ if ((*iter)->button()->extension() == extension)
+ return *iter;
+ }
+
+ return NULL;
+}
+
void BrowserActionsContainer::RefreshBrowserActionViews() {
for (size_t i = 0; i < browser_action_views_.size(); ++i)
browser_action_views_[i]->button()->UpdateState();
diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h
index a3ba6fe..cd308c2 100644
--- a/chrome/browser/views/browser_actions_container.h
+++ b/chrome/browser/views/browser_actions_container.h
@@ -152,6 +152,9 @@ class BrowserActionsContainer : public views::View,
return browser_action_views_[index];
}
+ // Retrieve the BrowserActionView for |extension|.
+ BrowserActionView* GetBrowserActionView(Extension* extension);
+
// Update the views to reflect the state of the browser action icons.
void RefreshBrowserActionViews();
diff --git a/chrome/browser/views/extensions/extension_installed_bubble.cc b/chrome/browser/views/extensions/extension_installed_bubble.cc
new file mode 100644
index 0000000..396ca6c
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_installed_bubble.cc
@@ -0,0 +1,229 @@
+// 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_installed_bubble.h"
+
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/message_loop.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/views/browser_actions_container.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/browser/views/location_bar_view.h"
+#include "chrome/browser/views/toolbar_view.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
+#include "grit/generated_resources.h"
+#include "views/controls/label.h"
+#include "views/standard_layout.h"
+#include "views/view.h"
+
+namespace {
+
+const int kIconSize = 43;
+
+const int kRightColumnWidth = 270;
+
+// The InfoBubble uses a BubbleBorder which adds about 6 pixels of whitespace
+// around the content view. We compensate by reducing our outer borders by this
+// amount.
+const int kBubbleBorderInsert = 6;
+const int kHorizOuterMargin = kPanelHorizMargin - kBubbleBorderInsert;
+const int kVertOuterMargin = kPanelVertMargin - kBubbleBorderInsert;
+
+// InstalledBubbleContent is the content view which is placed in the
+// ExtensionInstalledBubble. It displays the install icon and explanatory
+// text about the installed extension.
+class InstalledBubbleContent : public views::View {
+ public:
+ InstalledBubbleContent(Extension* extension,
+ ExtensionInstalledBubble::BubbleType type,
+ SkBitmap* icon)
+ : type_(type),
+ info_(NULL) {
+ const gfx::Font& font =
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
+
+ // Scale down to 43x43, but allow smaller icons (don't scale up).
+ gfx::Size size(icon->width(), icon->height());
+ if (size.width() > kIconSize || size.height() > kIconSize)
+ size = gfx::Size(kIconSize, kIconSize);
+ icon_ = new views::ImageView();
+ icon_->SetImageSize(size);
+ icon_->SetImage(*icon);
+ AddChildView(icon_);
+
+ heading_ = new views::Label(
+ l10n_util::GetStringF(IDS_EXTENSION_INSTALLED_HEADING,
+ UTF8ToWide(extension->name())));
+ heading_->SetFont(font.DeriveFont(3, gfx::Font::NORMAL));
+ heading_->SetMultiLine(true);
+ heading_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(heading_);
+
+ if (type_ == ExtensionInstalledBubble::PAGE_ACTION) {
+ info_ = new views::Label(l10n_util::GetString(
+ IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO));
+ info_->SetFont(font);
+ info_->SetMultiLine(true);
+ info_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(info_);
+ }
+
+ manage_ = new views::Label(l10n_util::GetString(
+ IDS_EXTENSION_INSTALLED_MANAGE_INFO));
+ manage_->SetFont(font);
+ manage_->SetMultiLine(true);
+ manage_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(manage_);
+ }
+
+ private:
+ virtual gfx::Size GetPreferredSize() {
+ int width = kRightColumnWidth + kHorizOuterMargin + kHorizOuterMargin;
+ width += kIconSize;
+ width += kPanelHorizMargin;
+
+ int height = kVertOuterMargin * 2;
+ height += heading_->GetHeightForWidth(kRightColumnWidth);
+ height += kPanelVertMargin;
+ if (type_ == ExtensionInstalledBubble::PAGE_ACTION) {
+ height += info_->GetHeightForWidth(kRightColumnWidth);
+ height += kPanelVertMargin;
+ }
+ height += manage_->GetHeightForWidth(kRightColumnWidth);
+
+ return gfx::Size(width, std::max(height, kIconSize + kVertOuterMargin * 2));
+ }
+
+ virtual void Layout() {
+ int x = kHorizOuterMargin;
+ int y = kVertOuterMargin;
+
+ icon_->SetBounds(x, y, kIconSize, kIconSize);
+ x += kIconSize;
+ x += kPanelHorizMargin;
+
+ heading_->SizeToFit(kRightColumnWidth);
+ heading_->SetX(x);
+ heading_->SetY(y);
+ y += heading_->height();
+ y += kPanelVertMargin;
+
+ if (type_ == ExtensionInstalledBubble::PAGE_ACTION) {
+ info_->SizeToFit(kRightColumnWidth);
+ info_->SetX(x);
+ info_->SetY(y);
+ y += info_->height();
+ y += kPanelVertMargin;
+ }
+
+ manage_->SizeToFit(kRightColumnWidth);
+ manage_->SetX(x);
+ manage_->SetY(y);
+ }
+
+ ExtensionInstalledBubble::BubbleType type_;
+ views::ImageView* icon_;
+ views::Label* heading_;
+ views::Label* info_;
+ views::Label* manage_;
+
+ DISALLOW_COPY_AND_ASSIGN(InstalledBubbleContent);
+};
+
+} // namespace
+
+void ExtensionInstalledBubble::Show(Extension *extension, Browser *browser,
+ SkBitmap icon) {
+ new ExtensionInstalledBubble(extension, browser, icon);
+}
+
+ExtensionInstalledBubble::ExtensionInstalledBubble(Extension *extension,
+ Browser *browser,
+ SkBitmap icon)
+ : extension_(extension),
+ browser_(browser),
+ icon_(icon) {
+ AddRef(); // Balanced in InfoBubbleClosing.
+
+ if (extension_->browser_action()) {
+ type_ = BROWSER_ACTION;
+ } else if (extension->page_action() &&
+ !extension->page_action()->default_icon_path().empty()) {
+ type_ = PAGE_ACTION;
+ } else {
+ type_ = GENERIC;
+ }
+
+ // |extension| has been initialized but not loaded at this point. We need
+ // to wait on showing the Bubble until not only the EXTENSION_LOADED gets
+ // fired, but all of the EXTENSION_LOADED Observers have run. Only then can we
+ // be sure that a BrowserAction or PageAction has had views created which we
+ // can inspect for the purpose of previewing of pointing to them.
+ registrar_.Add(this, NotificationType::EXTENSION_LOADED,
+ NotificationService::AllSources());
+}
+
+void ExtensionInstalledBubble::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::EXTENSION_LOADED) {
+ Extension* extension = Details<Extension>(details).ptr();
+ if (extension == extension_) {
+ // PostTask to ourself to allow all EXTENSION_LOADED Observers to run.
+ MessageLoopForUI::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &ExtensionInstalledBubble::ShowInternal));
+ }
+ } else {
+ NOTREACHED() << L"Received unexpected notification";
+ }
+}
+
+void ExtensionInstalledBubble::ShowInternal() {
+ BrowserView* browser_view = BrowserView::GetBrowserViewForNativeWindow(
+ browser_->window()->GetNativeHandle());
+
+ views::View* reference_view = NULL;
+ if (type_ == BROWSER_ACTION) {
+ reference_view = browser_view->GetToolbarView()->browser_actions()
+ ->GetBrowserActionView(extension_);
+ DCHECK(reference_view);
+ } else if (type_ == PAGE_ACTION) {
+ LocationBarView* location_bar_view = browser_view->GetLocationBarView();
+ location_bar_view->SetPreviewEnabledPageAction(extension_->page_action(),
+ true); // preview_enabled
+ reference_view = location_bar_view->GetPageActionView(
+ extension_->page_action());
+ DCHECK(reference_view);
+ }
+
+ // Default case.
+ if (reference_view == NULL)
+ reference_view = browser_view->GetToolbarView()->app_menu();
+
+ gfx::Point origin;
+ views::View::ConvertPointToScreen(reference_view, &origin);
+ gfx::Rect bounds = reference_view->bounds();
+ bounds.set_x(origin.x());
+ bounds.set_y(origin.y());
+
+ views::View* bubble_content = new InstalledBubbleContent(extension_, type_,
+ &icon_);
+ InfoBubble::Show(browser_view->GetWindow(), bounds, bubble_content, this);
+}
+
+// InfoBubbleDelegate
+void ExtensionInstalledBubble::InfoBubbleClosing(InfoBubble* info_bubble,
+ bool closed_by_escape) {
+ if (extension_->page_action()) {
+ BrowserView* browser_view = BrowserView::GetBrowserViewForNativeWindow(
+ browser_->window()->GetNativeHandle());
+ browser_view->GetLocationBarView()->SetPreviewEnabledPageAction(
+ extension_->page_action(),
+ false); // preview_enabled
+ }
+ Release(); // Balanced in ctor.
+}
diff --git a/chrome/browser/views/extensions/extension_installed_bubble.h b/chrome/browser/views/extensions/extension_installed_bubble.h
new file mode 100644
index 0000000..def9fcb
--- /dev/null
+++ b/chrome/browser/views/extensions/extension_installed_bubble.h
@@ -0,0 +1,81 @@
+// 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_INSTALLED_BUBBLE_H_
+#define CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_INSTALLED_BUBBLE_H_
+
+#include "base/ref_counted.h"
+#include "chrome/browser/views/info_bubble.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+class Browser;
+class Extension;
+class SkBitmap;
+
+// Provides feedback to the user upon successful installation of an
+// extension. Depending on the type of extension, the InfoBubble will
+// point to:
+// BROWSER_ACTION -> The browserAction icon in the toolbar.
+// PAGE_ACTION -> A preview of the pageAction icon in the location
+// bar which is shown while the InfoBubble is shown.
+// GENERIC -> The wrench menu. This case includes pageActions that
+// don't specify a default icon.
+//
+// ExtensionInstallBubble manages its own lifetime.
+class ExtensionInstalledBubble :
+ public InfoBubbleDelegate,
+ public NotificationObserver,
+ public base::RefCountedThreadSafe<ExtensionInstalledBubble> {
+ public:
+ // The behavior and content of this InfoBubble comes in three varieties.
+ enum BubbleType {
+ BROWSER_ACTION,
+ PAGE_ACTION,
+ GENERIC
+ };
+
+ // Creates the ExtensionInstalledBubble and schedules it to be shown once
+ // the extension has loaded. |extension| is the installed extension. |browser|
+ // is the browser window which will host the bubble. |icon| is the install
+ // icon of the extension.
+ static void Show(Extension *extension, Browser *browser, SkBitmap icon);
+
+private:
+ friend class base::RefCountedThreadSafe<ExtensionInstalledBubble>;
+
+ // Private ctor. Registers a listener for EXTENSION_LOADED.
+ ExtensionInstalledBubble(Extension *extension, Browser *browser,
+ SkBitmap icon);
+
+ ~ExtensionInstalledBubble() {}
+
+ // Shows the bubble. Called internally via PostTask.
+ void ShowInternal();
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // InfoBubbleDelegate
+ virtual void InfoBubbleClosing(InfoBubble* info_bubble,
+ bool closed_by_escape);
+ virtual bool CloseOnEscape() { return true; }
+
+ // Arrow subjects appear on the right side (for RTL), so do not prefer
+ // origin side anchor.
+ virtual bool PreferOriginSideAnchor() { return false; }
+
+ Extension *extension_;
+ Browser *browser_;
+ SkBitmap icon_;
+ NotificationRegistrar registrar_;
+ BubbleType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionInstalledBubble);
+};
+
+#endif // CHROME_BROWSER_VIEWS_EXTENSIONS_EXTENSION_INSTALLED_BUBBLE_H_
diff --git a/chrome/browser/views/info_bubble.cc b/chrome/browser/views/info_bubble.cc
index 1218f46..4f39ba9 100644
--- a/chrome/browser/views/info_bubble.cc
+++ b/chrome/browser/views/info_bubble.cc
@@ -35,17 +35,16 @@ class BorderContents : public views::View {
// Given the size of the contents and the rect to point at, initializes the
// bubble and returns the bounds of both the border
// and the contents inside the bubble.
- // |is_rtl| is true if the UI is RTL and thus the arrow should default to the
- // right side of the bubble; otherwise it defaults to the left top corner, and
- // then is moved as necessary to try and fit the whole bubble on the same
- // monitor as the rect being pointed to.
+ // |prefer_arrow_on_right| specifies the preferred location for the arrow
+ // anchor. If the bubble does not fit on the monitor, the arrow location may
+ // changed so it can.
//
// TODO(pkasting): Maybe this should use mirroring transformations instead,
// which would hopefully simplify this code.
void InitAndGetBounds(
const gfx::Rect& position_relative_to, // In screen coordinates
const gfx::Size& contents_size,
- bool is_rtl,
+ bool prefer_arrow_on_right,
gfx::Rect* contents_bounds, // Returned in window coordinates
gfx::Rect* window_bounds); // Returned in screen coordinates
@@ -61,7 +60,7 @@ class BorderContents : public views::View {
void BorderContents::InitAndGetBounds(
const gfx::Rect& position_relative_to,
const gfx::Size& contents_size,
- bool is_rtl,
+ bool prefer_arrow_on_right,
gfx::Rect* contents_bounds,
gfx::Rect* window_bounds) {
// Margins between the contents and the inside of the border, in pixels.
@@ -80,8 +79,9 @@ void BorderContents::InitAndGetBounds(
local_contents_size.Enlarge(kLeftMargin + kRightMargin,
kTopMargin + kBottomMargin);
- // Try putting the arrow in its default location, and calculating the bounds.
- BubbleBorder::ArrowLocation arrow_location(is_rtl ?
+ // Try putting the arrow in its initial location, and calculating the
+ // bounds.
+ BubbleBorder::ArrowLocation arrow_location(prefer_arrow_on_right ?
BubbleBorder::TOP_RIGHT : BubbleBorder::TOP_LEFT);
bubble_border->set_arrow_location(arrow_location);
*window_bounds =
@@ -94,9 +94,9 @@ void BorderContents::InitAndGetBounds(
monitor_provider->GetMonitorWorkAreaMatching(position_relative_to));
if (!monitor_bounds.IsEmpty() && !monitor_bounds.Contains(*window_bounds)) {
// The bounds don't fit. Move the arrow to try and improve things.
- bool arrow_on_left =
- (is_rtl ? (window_bounds->x() < monitor_bounds.x()) :
- (window_bounds->right() <= monitor_bounds.right()));
+ bool arrow_on_left = prefer_arrow_on_right ?
+ (window_bounds->x() < monitor_bounds.x()) :
+ (window_bounds->right() <= monitor_bounds.right());
if (window_bounds->bottom() > monitor_bounds.bottom()) {
arrow_location = arrow_on_left ?
BubbleBorder::BOTTOM_LEFT : BubbleBorder::BOTTOM_RIGHT;
@@ -155,13 +155,14 @@ gfx::Rect BorderWidget::InitAndGetBounds(
HWND owner,
const gfx::Rect& position_relative_to,
const gfx::Size& contents_size,
- bool is_rtl) {
+ bool prefer_arrow_on_right) {
// Set up the border view and ask it to calculate our bounds (and our
// contents').
BorderContents* border_contents = new BorderContents;
gfx::Rect contents_bounds, window_bounds;
- border_contents->InitAndGetBounds(position_relative_to, contents_size, is_rtl,
- &contents_bounds, &window_bounds);
+ border_contents->InitAndGetBounds(position_relative_to, contents_size,
+ prefer_arrow_on_right, &contents_bounds,
+ &window_bounds);
// Initialize ourselves.
WidgetWin::Init(GetAncestor(owner, GA_ROOT), window_bounds);
@@ -247,12 +248,16 @@ void InfoBubble::Init(views::Window* parent,
// Calculate and set the bounds for all windows and views.
gfx::Rect window_bounds;
+
+ bool prefer_arrow_on_right =
+ (contents->UILayoutIsRightToLeft() == delegate->PreferOriginSideAnchor());
+
#if defined(OS_WIN)
border_.reset(new BorderWidget);
// Initialize and position the border window.
window_bounds = border_->InitAndGetBounds(GetNativeView(),
position_relative_to, contents->GetPreferredSize(),
- contents->UILayoutIsRightToLeft());
+ prefer_arrow_on_right);
// Make |contents| take up the entire contents view.
contents_view->SetLayoutManager(new views::FillLayout);
@@ -265,7 +270,7 @@ void InfoBubble::Init(views::Window* parent,
BorderContents* border_contents = new BorderContents;
gfx::Rect contents_bounds;
border_contents->InitAndGetBounds(position_relative_to,
- contents->GetPreferredSize(), contents->UILayoutIsRightToLeft(),
+ contents->GetPreferredSize(), prefer_arrow_on_right,
&contents_bounds, &window_bounds);
// This new view must be added before |contents| so it will paint under it.
contents_view->AddChildView(0, border_contents);
diff --git a/chrome/browser/views/info_bubble.h b/chrome/browser/views/info_bubble.h
index 996413a..0e7e622 100644
--- a/chrome/browser/views/info_bubble.h
+++ b/chrome/browser/views/info_bubble.h
@@ -74,6 +74,12 @@ class InfoBubbleDelegate {
// Whether the InfoBubble should be closed when the Esc key is pressed.
virtual bool CloseOnEscape() = 0;
+
+ // Whether the default placement of the anchor is on the origin side of the
+ // text direction. For example: if true (the default) in LTR text direction,
+ // the ArrowLocation will be TOP_LEFT, if false it will be TOP_RIGHT.
+ // RTL is the reverse.
+ virtual bool PreferOriginSideAnchor() { return true; }
};
// TODO: this code is ifdef-tastic. It might be cleaner to refactor the
@@ -92,9 +98,13 @@ class InfoBubble
// screen coordinates at which the InfoBubble will point. Show() takes
// ownership of |contents| and deletes the created InfoBubble when another
// window is activated. You can explicitly close the bubble by invoking
- // Close(). You may provide an optional |delegate| to be notified when the
- // InfoBubble is closed and/or to prevent the InfoBubble from being closed
- // when the Escape key is pressed (the default behavior).
+ // Close(). You may provide an optional |delegate| to:
+ // - Be notified when the InfoBubble is closed.
+ // - Prevent the InfoBubble from being closed when the Escape key is
+ // pressed (the default behavior).
+ // - Have the InfoBubble prefer to anchor its arrow to the non-origin
+ // side of text direction. (see comment above
+ // InfoBubbleDelegate::PreferOriginSideAnchor); .
static InfoBubble* Show(views::Window* parent,
const gfx::Rect& position_relative_to,
views::View* contents,
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index fe71b89..01c6b2b 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -338,6 +338,36 @@ void LocationBarView::SetProfile(Profile* profile) {
}
}
+void LocationBarView::SetPreviewEnabledPageAction(ExtensionAction *page_action,
+ bool preview_enabled) {
+ DCHECK(page_action);
+ TabContents* contents = delegate_->GetTabContents();
+
+ RefreshPageActionViews();
+ PageActionWithBadgeView* page_action_view =
+ static_cast<PageActionWithBadgeView*>(GetPageActionView(page_action));
+ DCHECK(page_action_view);
+ if (!page_action_view)
+ return;
+
+ page_action_view->image_view()->set_preview_enabled(preview_enabled);
+ page_action_view->UpdateVisibility(contents,
+ GURL(WideToUTF8(model_->GetText())));
+ Layout();
+ SchedulePaint();
+}
+
+views::View* LocationBarView::GetPageActionView(
+ ExtensionAction *page_action) {
+ DCHECK(page_action);
+ for (std::vector<PageActionWithBadgeView*>::iterator iter =
+ page_action_views_.begin(); iter != page_action_views_.end();
+ ++iter) {
+ if ((*iter)->image_view()->page_action() == page_action)
+ return *iter;
+ }
+ return NULL;
+}
gfx::Size LocationBarView::GetPreferredSize() {
return gfx::Size(0,
(popup_window_mode_ ? kPopupBackground : kBackground)->height());
@@ -1263,7 +1293,8 @@ LocationBarView::PageActionImageView::PageActionImageView(
owner_(owner),
profile_(profile),
page_action_(page_action),
- current_tab_id_(-1) {
+ current_tab_id_(-1),
+ preview_enabled_(false) {
Extension* extension = profile->GetExtensionsService()->GetExtensionById(
page_action->extension_id());
DCHECK(extension);
@@ -1370,7 +1401,8 @@ void LocationBarView::PageActionImageView::UpdateVisibility(
current_tab_id_ = ExtensionTabUtil::GetTabId(contents);
current_url_ = url;
- bool visible = page_action_->GetIsVisible(current_tab_id_);
+ bool visible = preview_enabled_ ||
+ page_action_->GetIsVisible(current_tab_id_);
if (visible) {
// Set the tooltip.
tooltip_ = page_action_->GetTitle(current_tab_id_);
diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h
index 8bac242..26d562b 100644
--- a/chrome/browser/views/location_bar_view.h
+++ b/chrome/browser/views/location_bar_view.h
@@ -95,6 +95,17 @@ class LocationBarView : public LocationBar,
void SetProfile(Profile* profile);
Profile* profile() { return profile_; }
+ // Sets |preview_enabled| for the PageAction View associated with this
+ // |page_action|. If |preview_enabled| is true, the view will display the
+ // PageActions icon even though it has not been activated by the extension.
+ // This is used by the ExtensionInstalledBubble to preview what the icon
+ // will look like for the user upon installation of the extension.
+ void SetPreviewEnabledPageAction(ExtensionAction *page_action,
+ bool preview_enabled);
+
+ // Retrieves the PageAction View which is associated with |page_action|
+ views::View* GetPageActionView(ExtensionAction* page_action);
+
// Sizing functions
virtual gfx::Size GetPreferredSize();
@@ -360,6 +371,10 @@ class LocationBarView : public LocationBar,
int current_tab_id() { return current_tab_id_; }
+ void set_preview_enabled(bool preview_enabled) {
+ preview_enabled_ = preview_enabled;
+ }
+
// Overridden from view.
virtual void OnMouseMoved(const views::MouseEvent& event);
virtual bool OnMousePressed(const views::MouseEvent& event);
@@ -406,6 +421,10 @@ class LocationBarView : public LocationBar,
// The string to show for a tooltip;
std::string tooltip_;
+ // This is used for post-install visual feedback. The page_action icon
+ // is briefly shown even if it hasn't been enabled by it's extension.
+ bool preview_enabled_;
+
DISALLOW_COPY_AND_ASSIGN(PageActionImageView);
};
friend class PageActionImageView;
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 33d9422..9371aba 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -2282,6 +2282,8 @@
'browser/views/event_utils.cc',
'browser/views/event_utils.h',
'browser/views/extensions/extension_install_prompt.cc',
+ 'browser/views/extensions/extension_installed_bubble.cc',
+ 'browser/views/extensions/extension_installed_bubble.h',
'browser/views/extensions/extension_popup.cc',
'browser/views/extensions/extension_popup.h',
'browser/views/extensions/extension_shelf.cc',
@@ -2805,6 +2807,8 @@
['include', '^browser/views/event_utils.cc'],
['include', '^browser/views/event_utils.h'],
['include', '^browser/views/extensions/extension_install_prompt.cc'],
+ ['include', '^browser/views/extensions/extension_installed_bubble.cc'],
+ ['include', '^browser/views/extensions/extension_installed_bubble.h'],
['include', '^browser/views/extensions/extension_popup.cc'],
['include', '^browser/views/extensions/extension_popup.h'],
['include', '^browser/views/extensions/extension_shelf.cc'],
diff --git a/chrome/common/extensions/docs/static/pageAction.html b/chrome/common/extensions/docs/static/pageAction.html
index 204048d..2683661 100755
--- a/chrome/common/extensions/docs/static/pageAction.html
+++ b/chrome/common/extensions/docs/static/pageAction.html
@@ -49,6 +49,12 @@ like this:
reserves a slot for your page action
in the address bar.</p>
+<p>
+Although the "default_icon" is optional, it's a good idea to provide one.
+When users install your extension, if you have set a default icon, it can
+be previewed to the user so they know what to look for when your page action
+is active for a given page.</p>
+
<h2 id="ui">Parts of the UI</h2>
<p>
diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/background.html b/chrome/test/data/extensions/samples/subscribe_page_action/background.html
index 9ab4f3f..a5d6772 100644
--- a/chrome/test/data/extensions/samples/subscribe_page_action/background.html
+++ b/chrome/test/data/extensions/samples/subscribe_page_action/background.html
@@ -1,17 +1,13 @@
<html>
<head>
<script>
- // The Page Action ID.
- var pageActionId = "RssPageAction";
-
- // The icon to use. This corresponds to the icon listed in the manifest.
- var subscribeId = 0;
-
// A dictionary keyed off of tabId that keeps track of data per tab (for
// example what feedUrl was detected in the tab).
var feedData = {};
chrome.extension.onConnect.addListener(function(port) {
+ var tab = port.sender.tab;
+
// This will get called from the content script using PostMessage.
// |feedUrls| is a list of URL feeds found on the page. We only need 1 to
// enable the PageAction icon in the Omnibox.
@@ -20,34 +16,29 @@
// Let Chrome know that the PageAction needs to be enabled for this tabId
// and for the url of this page.
if (feedUrl) {
- feedData[port.tab.id] = {pageUrl: port.tab.url,
- feedUrl: feedUrl};
+ feedData[tab.id] = { pageUrl: tab.url,
+ feedUrl: feedUrl };
- chrome.pageActions.enableForTab(
- pageActionId, {tabId: port.tab.id,
- url: port.tab.url,
- title: "Click to subscribe...",
- iconId: subscribeId});
+ chrome.pageAction.setTitle({ tabId: tab.id,
+ title: "Click to subscribe..." });
+ chrome.pageAction.show(tab.id);
}
});
});
// Chrome will call into us when the user clicks on the icon in the OmniBox.
- chrome.pageActions["RssPageAction"].addListener(function(pageActionId,
- pageActionInfo) {
- chrome.windows.getCurrent(function(window) {
- chrome.tabs.get(pageActionInfo.tabId, function(tab) {
- // We need to know if we are the active window, because the tab may
- // have moved to another window and we don't want to execute this
- // action multiple times.
- if (window.focused) {
- // Create a new tab showing the subscription page with the right
- // feed URL.
- var url = "subscribe.html?" +
- encodeURIComponent(feedData[pageActionInfo.tabId].feedUrl);
- chrome.tabs.create({url: url, windowId: window.windowId});
- }
- });
+ chrome.pageAction.onClicked.addListener(function(tab) {
+ chrome.windows.get(tab.windowId, function(window) {
+ // We need to know if we are the active window, because the tab may
+ // have moved to another window and we don't want to execute this
+ // action multiple times.
+ if (window.focused) {
+ // Create a new tab showing the subscription page with the right
+ // feed URL.
+ var url = "subscribe.html?" +
+ encodeURIComponent(feedData[tab.id].feedUrl);
+ chrome.tabs.create({url: url, windowId: window.id});
+ }
});
});
diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json b/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json
index 84e84e9..ceb3bfe 100644
--- a/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json
+++ b/chrome/test/data/extensions/samples/subscribe_page_action/manifest.json
@@ -16,11 +16,8 @@
"icons": { "128": "feed-icon-128x128.png" },
"page_actions": [
{
- "id": "RssPageAction",
- "name": "Subscribe to this feed",
- "icons": [
- "feed-icon-16x16.png"
- ]
+ "default_title": "Subscribe to this feed",
+ "default_icon": "feed-icon-16x16.png"
}
]
} \ No newline at end of file