summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
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