Chromium Code Reviews| Index: chrome/browser/ui/views/location_bar/zoom_bubble_view.cc |
| diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc |
| index 80367b88c05a657ac660c074c700deaa4fc231ce..6f6d8d30799be071370930d40252d4d8a069df40 100644 |
| --- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc |
| +++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc |
| @@ -5,22 +5,29 @@ |
| #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h" |
| #include "base/i18n/rtl.h" |
| +#include "base/strings/stringprintf.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/chrome_page_zoom.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| +#include "chrome/browser/ui/browser_tabstrip.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
| #include "chrome/browser/ui/views/location_bar/zoom_view.h" |
| #include "chrome/browser/ui/zoom/zoom_controller.h" |
| +#include "chrome/common/extensions/api/extension_action/action_info.h" |
| #include "content/public/browser/notification_source.h" |
| +#include "extensions/common/manifest_handlers/icons_handler.h" |
| #include "grit/generated_resources.h" |
| +#include "grit/theme_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| +#include "ui/gfx/favicon_size.h" |
| +#include "ui/views/controls/button/image_button.h" |
| #include "ui/views/controls/button/label_button.h" |
| #include "ui/views/controls/separator.h" |
| -#include "ui/views/layout/box_layout.h" |
| +#include "ui/views/layout/grid_layout.h" |
| #include "ui/views/layout/layout_constants.h" |
| #include "ui/views/widget/widget.h" |
| @@ -51,41 +58,54 @@ void ZoomBubbleView::ShowBubble(content::WebContents* web_contents, |
| views::View* anchor_view = anchor_to_view ? |
| browser_view->GetLocationBarView()->zoom_view() : NULL; |
| - // If the bubble is already showing in this window and its |auto_close_| value |
| - // is equal to |auto_close|, the bubble can be reused and only the label text |
| - // needs to be updated. |
| + // Find the extension that initiated the zoom change, if any. |
| + ZoomController* zoom_controller = |
| + ZoomController::FromWebContents(web_contents); |
| + const extensions::Extension* extension = zoom_controller->last_extension(); |
| + |
| + // If the bubble is already showing in this window, its |auto_close_| value |
| + // is equal to |auto_close|, and the zoom change was not initiated by an |
| + // extension, then the bubble can be reused and only the label text needs to |
| + // be updated. |
| if (zoom_bubble_ && |
| zoom_bubble_->GetAnchorView() == anchor_view && |
| - zoom_bubble_->auto_close_ == auto_close) { |
| - zoom_bubble_->Refresh(); |
| - } else { |
| - // If the bubble is already showing but its |auto_close_| value is not equal |
| - // to |auto_close|, the bubble's focus properties must change, so the |
| - // current bubble must be closed and a new one created. |
| - CloseBubble(); |
| - |
| - zoom_bubble_ = new ZoomBubbleView(anchor_view, |
| - web_contents, |
| - auto_close, |
| - browser_view->immersive_mode_controller(), |
| - browser->fullscreen_controller()); |
| - |
| - // If we do not have an anchor view, parent the bubble to the content area. |
| - if (!anchor_to_view) { |
| - zoom_bubble_->set_parent_window(web_contents->GetTopLevelNativeWindow()); |
| - } |
| + zoom_bubble_->auto_close_ == auto_close && |
| + !extension) { |
| + zoom_bubble_->Refresh(); |
|
sky
2014/06/24 16:23:41
nit: spacing.
wjmaclean
2014/06/24 18:04:07
Done.
|
| + return; |
| + } |
| + |
| + // If the bubble is already showing but its |auto_close_| value is not equal |
| + // to |auto_close|, the bubble's focus properties must change, so the |
| + // current bubble must be closed and a new one created. |
| + CloseBubble(); |
| - views::BubbleDelegateView::CreateBubble(zoom_bubble_); |
| + zoom_bubble_ = new ZoomBubbleView(anchor_view, |
| + web_contents, |
| + auto_close, |
| + browser_view->immersive_mode_controller(), |
| + browser->fullscreen_controller()); |
| - // Adjust for fullscreen after creation as it relies on the content size. |
| - if (is_fullscreen) |
| - zoom_bubble_->AdjustForFullscreen(browser_view->GetBoundsInScreen()); |
| + // If the zoom change was initiated by an extension, capture the relevent |
| + // information from it. |
| + if (extension) |
| + zoom_bubble_->SetExtensionInfo(extension); |
| - if (zoom_bubble_->use_focusless()) |
| - zoom_bubble_->GetWidget()->ShowInactive(); |
| - else |
| - zoom_bubble_->GetWidget()->Show(); |
| + // If we do not have an anchor view, parent the bubble to the content area. |
| + if (!anchor_to_view) { |
|
sky
2014/06/24 16:23:41
nit: no {}
wjmaclean
2014/06/24 18:04:07
Done.
|
| + zoom_bubble_->set_parent_window(web_contents->GetTopLevelNativeWindow()); |
| } |
| + |
| + views::BubbleDelegateView::CreateBubble(zoom_bubble_); |
| + |
| + // Adjust for fullscreen after creation as it relies on the content size. |
| + if (is_fullscreen) |
| + zoom_bubble_->AdjustForFullscreen(browser_view->GetBoundsInScreen()); |
| + |
| + if (zoom_bubble_->use_focusless()) |
| + zoom_bubble_->GetWidget()->ShowInactive(); |
| + else |
| + zoom_bubble_->GetWidget()->Show(); |
| } |
| // static |
| @@ -113,6 +133,7 @@ ZoomBubbleView::ZoomBubbleView( |
| FullscreenController* fullscreen_controller) |
| : BubbleDelegateView(anchor_view, anchor_view ? |
| views::BubbleBorder::TOP_RIGHT : views::BubbleBorder::NONE), |
| + image_button_(NULL), |
| label_(NULL), |
| web_contents_(web_contents), |
| auto_close_(auto_close), |
| @@ -150,7 +171,7 @@ void ZoomBubbleView::AdjustForFullscreen(const gfx::Rect& screen_bounds) { |
| void ZoomBubbleView::Refresh() { |
| ZoomController* zoom_controller = |
| ZoomController::FromWebContents(web_contents_); |
| - int zoom_percent = zoom_controller->zoom_percent(); |
| + int zoom_percent = zoom_controller->GetZoomPercent(); |
| label_->SetText( |
| l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent)); |
| StartTimerIfNecessary(); |
| @@ -160,6 +181,50 @@ void ZoomBubbleView::Close() { |
| GetWidget()->Close(); |
| } |
| +void ZoomBubbleView::SetExtensionInfo(const extensions::Extension* extension) { |
| + DCHECK(extension); |
| + extension_info_.id = extension->id(); |
| + extension_info_.name = extension->name(); |
| + |
| + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| + const gfx::ImageSkia& default_extension_icon_image = |
| + *rb.GetImageSkiaNamed(IDR_EXTENSIONS_FAVICON); |
| + int icon_size = gfx::kFaviconSize; |
| + |
| + // We give first preference to an icon from the extension's icon set that |
| + // matches the size of the default. But not all extensions will declare an |
| + // icon set, or may not have an icon of the default size (we don't want the |
| + // bubble to display, for example, a very large icon). In that case, if there |
| + // is a browser-action icon (size-19) this is an acceptable alternative. |
| + const ExtensionIconSet& icons = extensions::IconsInfo::GetIcons(extension); |
| + bool has_default_sized_icon = |
| + !icons.Get(gfx::kFaviconSize, ExtensionIconSet::MATCH_EXACTLY).empty(); |
| + if (has_default_sized_icon) { |
| + extension_info_.icon_image.reset( |
| + new extensions::IconImage(web_contents_->GetBrowserContext(), |
| + extension, |
| + icons, |
| + icon_size, |
| + default_extension_icon_image, |
| + this)); |
| + return; |
| + } |
| + |
| + const extensions::ActionInfo* browser_action = |
| + extensions::ActionInfo::GetBrowserActionInfo(extension); |
| + if (!browser_action || browser_action->default_icon.empty()) |
| + return; |
| + |
| + icon_size = browser_action->default_icon.map().begin()->first; |
| + extension_info_.icon_image.reset( |
| + new extensions::IconImage(web_contents_->GetBrowserContext(), |
| + extension, |
| + browser_action->default_icon, |
| + icon_size, |
| + default_extension_icon_image, |
| + this)); |
| +} |
| + |
| void ZoomBubbleView::StartTimerIfNecessary() { |
| if (auto_close_) { |
| if (timer_.IsRunning()) { |
| @@ -178,6 +243,14 @@ void ZoomBubbleView::StopTimer() { |
| timer_.Stop(); |
| } |
| +void ZoomBubbleView::OnExtensionIconImageChanged(extensions::IconImage* image) { |
| + DCHECK_EQ(image, extension_info_.icon_image.get()) << |
| + "Unknown ImageIcon update."; |
| + image_button_->SetImage(views::Button::STATE_NORMAL, |
|
sky
2014/06/24 16:23:41
Because the image for this comes in async does the
wjmaclean
2014/06/24 18:04:07
I've checked in both debug and release builds on L
sky
2014/06/25 16:02:39
I was thinking you would force the image_button_ t
wjmaclean
2014/06/25 17:21:30
Ahh, Ok ... I didn't understand where your comment
|
| + &extension_info_.icon_image->image_skia()); |
| + image_button_->SchedulePaint(); |
| +} |
| + |
| void ZoomBubbleView::OnMouseEntered(const ui::MouseEvent& event) { |
| set_use_focusless(false); |
| StopTimer(); |
| @@ -202,28 +275,70 @@ void ZoomBubbleView::OnGestureEvent(ui::GestureEvent* event) { |
| void ZoomBubbleView::ButtonPressed(views::Button* sender, |
| const ui::Event& event) { |
| - chrome_page_zoom::Zoom(web_contents_, content::PAGE_ZOOM_RESET); |
| + if (sender == image_button_) { |
| + DCHECK(extension_info_.icon_image.get()) << "Invalid button press."; |
| + Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); |
| + std::string url = base::StringPrintf("chrome://extensions?id="); |
|
sky
2014/06/24 16:23:41
Why the base::StringPrintf here?
wjmaclean
2014/06/24 18:04:07
The call to base::StringPrintf() was a request in
Devlin
2014/06/24 18:11:15
Sorry, should have been more clear.
std::string u
|
| + url += extension_info_.id; |
| + chrome::AddSelectedTabWithURL(browser, |
| + GURL(url), |
| + content::PAGE_TRANSITION_FROM_API); |
| + } else { |
| + chrome_page_zoom::Zoom(web_contents_, content::PAGE_ZOOM_RESET); |
| + } |
| } |
| void ZoomBubbleView::Init() { |
| - SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, |
| - 0, 0, views::kRelatedControlVerticalSpacing)); |
| + // Set up the layout of the zoom bubble. A grid layout is used because |
| + // sometimes an extension icon is shown next to the zoom label. |
| + views::GridLayout* grid_layout = new views::GridLayout(this); |
| + SetLayoutManager(grid_layout); |
| + views::ColumnSet* columns = grid_layout->AddColumnSet(0); |
| + // First row. |
| + if (extension_info_.icon_image.get()) { |
| + columns->AddColumn(views::GridLayout::CENTER,views::GridLayout::CENTER, 2, |
| + views::GridLayout::USE_PREF, 0, 0); |
| + } |
| + columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| + views::GridLayout::USE_PREF, 0, 0); |
| + grid_layout->StartRow(0, 0); |
| + |
| + // If this zoom change was initiated by an extension, that extension will be |
| + // attributed by showing its icon in the zoom bubble. |
| + if (extension_info_.icon_image.get()) { |
| + image_button_ = new views::ImageButton(this); |
| + image_button_->SetTooltipText(l10n_util::GetStringFUTF16( |
| + IDS_TOOLTIP_ZOOM_EXTENSION_ICON, |
| + base::UTF8ToUTF16(extension_info_.name))); |
| + image_button_->SetImage(views::Button::STATE_NORMAL, |
| + &extension_info_.icon_image->image_skia()); |
| + grid_layout->AddView(image_button_); |
| + } |
| + // Add zoom label with the new zoom percent. |
| ZoomController* zoom_controller = |
| ZoomController::FromWebContents(web_contents_); |
| - int zoom_percent = zoom_controller->zoom_percent(); |
| + int zoom_percent = zoom_controller->GetZoomPercent(); |
| label_ = new views::Label( |
| l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent)); |
| label_->SetFontList( |
| ui::ResourceBundle::GetSharedInstance().GetFontList( |
| ui::ResourceBundle::MediumFont)); |
| - AddChildView(label_); |
| + grid_layout->AddView(label_); |
| + // Second row. |
| + grid_layout->AddPaddingRow(0, 8); |
| + columns = grid_layout->AddColumnSet(1); |
| + columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| + views::GridLayout::USE_PREF, 0, 0); |
| + grid_layout->StartRow(0, 1); |
| + |
| + // Add "Reset to Default" button. |
| views::LabelButton* set_default_button = new views::LabelButton( |
| this, l10n_util::GetStringUTF16(IDS_ZOOM_SET_DEFAULT)); |
| set_default_button->SetStyle(views::Button::STYLE_BUTTON); |
| set_default_button->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| - AddChildView(set_default_button); |
| + grid_layout->AddView(set_default_button); |
| StartTimerIfNecessary(); |
| } |
| @@ -249,3 +364,7 @@ void ZoomBubbleView::WindowClosing() { |
| if (zoom_bubble_ == this) |
| zoom_bubble_ = NULL; |
| } |
| + |
| +ZoomBubbleView::ZoomBubbleExtensionInfo::ZoomBubbleExtensionInfo() {} |
| + |
| +ZoomBubbleView::ZoomBubbleExtensionInfo::~ZoomBubbleExtensionInfo() {} |