diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/app/generated_resources.grd | 13 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_ui.cc | 25 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_ui.h | 6 | ||||
-rw-r--r-- | chrome/browser/views/browser_actions_container.cc | 12 | ||||
-rw-r--r-- | chrome/browser/views/browser_actions_container.h | 3 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_installed_bubble.cc | 229 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_installed_bubble.h | 81 | ||||
-rw-r--r-- | chrome/browser/views/info_bubble.cc | 37 | ||||
-rw-r--r-- | chrome/browser/views/info_bubble.h | 16 | ||||
-rw-r--r-- | chrome/browser/views/location_bar_view.cc | 36 | ||||
-rw-r--r-- | chrome/browser/views/location_bar_view.h | 19 | ||||
-rwxr-xr-x | chrome/chrome.gyp | 4 | ||||
-rwxr-xr-x | chrome/common/extensions/docs/static/pageAction.html | 6 | ||||
-rw-r--r-- | chrome/test/data/extensions/samples/subscribe_page_action/background.html | 47 | ||||
-rw-r--r-- | chrome/test/data/extensions/samples/subscribe_page_action/manifest.json | 7 |
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 |