From 4ad2cf722f04ad38b8c4900eb6ffb8379454cc14 Mon Sep 17 00:00:00 2001 From: "finnur@chromium.org" Date: Thu, 3 Jul 2014 17:36:53 +0000 Subject: [PATCH] Extension Toolbar redesign, part 1 (overflow) Add a flag that allows the browser action icons to overflow into the wrench menu and make sure all extensions without browser action/page action icon show at least the default icon. The overflow is implemented by introducing a master/slave mode for the BrowserActionsContainer (BAC) class. The BAC next to the Omnibox is considered the master and works as before. The BAC in inside the wrench menu is considered the slave and shows only the icons that the master does not. Known issues: - Dragging between BAC objects works, but in one direction only (from slave to master). The other way doesn't work because the wrench menu isn't a drop target for browser action icons (and doesn't open). - Context menu handling for browser action icons needs to be changed to prevent a nested messageloop when you right-click two icons in a row. - Badges on icons in the overflow container need adjusting (appear a bit too low). BUG=391280 TBR=cpu Review URL: https://codereview.chromium.org/324393002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281291 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/app/chrome_command_ids.h | 1 + chrome/browser/ui/toolbar/wrench_menu_model.cc | 12 ++ chrome/browser/ui/toolbar/wrench_menu_model.h | 3 + .../ui/views/toolbar/browser_action_view.cc | 35 ++-- .../browser/ui/views/toolbar/browser_action_view.h | 3 + .../ui/views/toolbar/browser_actions_container.cc | 197 +++++++++++++++------ .../ui/views/toolbar/browser_actions_container.h | 54 ++++-- .../views/toolbar/extension_toolbar_menu_view.cc | 51 ++++++ .../ui/views/toolbar/extension_toolbar_menu_view.h | 32 ++++ chrome/browser/ui/views/toolbar/toolbar_view.cc | 5 +- chrome/browser/ui/views/toolbar/wrench_menu.cc | 36 +++- chrome/chrome_browser_ui.gypi | 2 + chrome/chrome_common.gypi | 2 + .../common/extensions/chrome_manifest_handlers.cc | 2 + .../synthesize_browser_action_handler.cc | 49 +++++ .../synthesize_browser_action_handler.h | 33 ++++ extensions/common/feature_switch.cc | 8 +- extensions/common/feature_switch.h | 1 + extensions/common/manifest_constants.cc | 1 + extensions/common/manifest_constants.h | 1 + extensions/common/switches.cc | 3 + extensions/common/switches.h | 1 + 22 files changed, 448 insertions(+), 84 deletions(-) create mode 100644 chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc create mode 100644 chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.h create mode 100644 chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.cc create mode 100644 chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.h diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index e52c84b7a74f..46a08c2d018b 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h @@ -213,6 +213,7 @@ #define IDC_SHOW_SYNC_ERROR 40243 #define IDC_DISTILL_PAGE 40244 #define IDC_HELP_MENU 40245 +#define IDC_EXTENSIONS_OVERFLOW_MENU 40246 // Spell-check // Insert any additional suggestions before _LAST; these have to be consecutive. diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.cc b/chrome/browser/ui/toolbar/wrench_menu_model.cc index 2b2d50f58350..7ef517ef7388 100644 --- a/chrome/browser/ui/toolbar/wrench_menu_model.cc +++ b/chrome/browser/ui/toolbar/wrench_menu_model.cc @@ -47,6 +47,7 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" +#include "extensions/common/feature_switch.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -529,6 +530,9 @@ void WrenchMenuModel::Build(bool is_new_menu) { AddSeparator(ui::NORMAL_SEPARATOR); #endif + if (extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) + CreateExtensionToolbarOverflowMenu(); + AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB); AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW); @@ -727,6 +731,14 @@ void WrenchMenuModel::AddGlobalErrorMenuItems() { } } +void WrenchMenuModel::CreateExtensionToolbarOverflowMenu() { +#if defined(TOOLKIT_VIEWS) + AddItem(IDC_EXTENSIONS_OVERFLOW_MENU, base::string16()); + // TODO(devlin): Add the separator only if there are > 0 icons to show. + AddSeparator(ui::UPPER_SEPARATOR); +#endif // defined(TOOLKIT_VIEWS) +} + void WrenchMenuModel::CreateCutCopyPasteMenu(bool new_menu) { AddSeparator(new_menu ? ui::LOWER_SEPARATOR : ui::NORMAL_SEPARATOR); diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.h b/chrome/browser/ui/toolbar/wrench_menu_model.h index fbade9a0f51d..381937a61733 100644 --- a/chrome/browser/ui/toolbar/wrench_menu_model.h +++ b/chrome/browser/ui/toolbar/wrench_menu_model.h @@ -154,6 +154,9 @@ class WrenchMenuModel : public ui::SimpleMenuModel, // to false. void CreateCutCopyPasteMenu(bool new_menu); + // Add a menu item for the extension icons. + void CreateExtensionToolbarOverflowMenu(); + // Appends everything needed for the zoom menu: a menu break, then the zoom // menu content and then another menu break. If the new menu type is used, // |new_menu| should be set to true. diff --git a/chrome/browser/ui/views/toolbar/browser_action_view.cc b/chrome/browser/ui/views/toolbar/browser_action_view.cc index ef1d57168e31..5e2ae578e1e1 100644 --- a/chrome/browser/ui/views/toolbar/browser_action_view.cc +++ b/chrome/browser/ui/views/toolbar/browser_action_view.cc @@ -16,6 +16,7 @@ #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/view_ids.h" +#include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/toolbar/browser_actions_container.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" #include "extensions/common/extension.h" @@ -71,7 +72,7 @@ gfx::ImageSkia BrowserActionView::GetIconWithBadge() { } void BrowserActionView::Layout() { - button_->SetBounds(0, y(), width(), height()); + button_->SetBounds(0, 0, width(), height()); } void BrowserActionView::GetAccessibleState(ui::AXViewState* state) { @@ -184,20 +185,34 @@ void BrowserActionButton::ShowContextMenuForView( SetButtonPushed(); // Reconstructs the menu every time because the menu's contents are dynamic. - scoped_refptr context_menu_contents_( + scoped_refptr context_menu_contents( new ExtensionContextMenuModel(extension(), browser_, delegate_)); - menu_runner_.reset(new views::MenuRunner(context_menu_contents_.get())); + menu_runner_.reset(new views::MenuRunner(context_menu_contents.get())); context_menu_ = menu_runner_->GetMenu(); gfx::Point screen_loc; views::View::ConvertPointToScreen(this, &screen_loc); - if (menu_runner_->RunMenuAt( - GetWidget(), - NULL, - gfx::Rect(screen_loc, size()), - views::MENU_ANCHOR_TOPLEFT, - source_type, - views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) == + + views::Widget* parent = NULL; + int run_types = views::MenuRunner::HAS_MNEMONICS | + views::MenuRunner::CONTEXT_MENU; + if (delegate_->ShownInsideMenu()) { + run_types |= views::MenuRunner::IS_NESTED; + // RunMenuAt expects a nested menu to be parented by the same widget as the + // already visible menu, in this case the Chrome menu. + parent = BrowserView::GetBrowserViewForBrowser(browser_)->toolbar() + ->app_menu() + ->GetWidget(); + } else { + parent = GetWidget(); + } + + if (menu_runner_->RunMenuAt(parent, + NULL, + gfx::Rect(screen_loc, size()), + views::MENU_ANCHOR_TOPLEFT, + source_type, + run_types) == views::MenuRunner::MENU_DELETED) { return; } diff --git a/chrome/browser/ui/views/toolbar/browser_action_view.h b/chrome/browser/ui/views/toolbar/browser_action_view.h index 01248f446251..7dd24c371833 100644 --- a/chrome/browser/ui/views/toolbar/browser_action_view.h +++ b/chrome/browser/ui/views/toolbar/browser_action_view.h @@ -54,6 +54,9 @@ class BrowserActionView : public views::View { // Called when a browser action becomes visible/hidden. virtual void OnBrowserActionVisibilityChanged() = 0; + // Whether the container for this button is shown inside a menu. + virtual bool ShownInsideMenu() const = 0; + protected: virtual ~Delegate() {} }; diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc index 64a2be4c3d98..d935524540f9 100644 --- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc +++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc @@ -27,6 +27,7 @@ #include "extensions/browser/extension_system.h" #include "extensions/browser/pref_names.h" #include "extensions/browser/runtime_data.h" +#include "extensions/common/feature_switch.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "grit/ui_resources.h" @@ -57,6 +58,22 @@ const int kItemSpacing = ToolbarView::kStandardSpacing; // Horizontal spacing before the chevron (if visible). const int kChevronSpacing = kItemSpacing - 2; +// Padding to make sure the badge appears in the right location vertically when +// in overflow mode (inside the Chrome menu). +// TODO(devlin): Remove this hard coding and make sure the badge appears +// correctly. +const int kPaddingForBadge = 9; + +// The maximum number of icons to show per row when in overflow mode (showing +// icons in the application menu). +// TODO(devlin): Compute the right number of icons to show, depending on the +// menu width. +#if defined(OS_LINUX) +const int kIconsPerMenuRow = 8; // The menu on Linux is wider. +#else +const int kIconsPerMenuRow = 7; +#endif + // A version of MenuButton with almost empty insets to fit properly on the // toolbar. class ChevronMenuButton : public views::MenuButton { @@ -94,15 +111,19 @@ bool BrowserActionsContainer::disable_animations_during_testing_ = false; //////////////////////////////////////////////////////////////////////////////// // BrowserActionsContainer -BrowserActionsContainer::BrowserActionsContainer(Browser* browser, - View* owner_view) +BrowserActionsContainer::BrowserActionsContainer( + Browser* browser, + View* owner_view, + BrowserActionsContainer* main_container) : profile_(browser->profile()), browser_(browser), owner_view_(owner_view), + main_container_(main_container), popup_(NULL), popup_button_(NULL), model_(NULL), container_width_(0), + resize_area_(NULL), chevron_(NULL), overflow_menu_(NULL), suppress_chevron_(false), @@ -117,22 +138,33 @@ BrowserActionsContainer::BrowserActionsContainer(Browser* browser, if (model_) model_->AddObserver(this); - extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews( - browser->profile(), - owner_view->GetFocusManager(), - extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS, - this)); - - resize_animation_.reset(new gfx::SlideAnimation(this)); - resize_area_ = new views::ResizeArea(this); - AddChildView(resize_area_); - - chevron_ = new ChevronMenuButton(NULL, base::string16(), this, false); - chevron_->EnableCanvasFlippingForRTLUI(true); - chevron_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON)); - chevron_->SetVisible(false); - AddChildView(chevron_); + bool overflow_experiment = + extensions::FeatureSwitch::extension_action_redesign()->IsEnabled(); + DCHECK(!in_overflow_mode() || overflow_experiment); + + if (!in_overflow_mode()) { + extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews( + browser->profile(), + owner_view->GetFocusManager(), + extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS, + this)); + + resize_animation_.reset(new gfx::SlideAnimation(this)); + resize_area_ = new views::ResizeArea(this); + AddChildView(resize_area_); + + // 'Main' mode doesn't need a chevron overflow when overflow is shown inside + // the Chrome menu. + if (!overflow_experiment) { + chevron_ = new ChevronMenuButton(NULL, base::string16(), this, false); + chevron_->SetBorder(views::Border::NullBorder()); + chevron_->EnableCanvasFlippingForRTLUI(true); + chevron_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON)); + chevron_->SetVisible(false); + AddChildView(chevron_); + } + } } BrowserActionsContainer::~BrowserActionsContainer() { @@ -233,6 +265,10 @@ void BrowserActionsContainer::ExecuteExtensionCommand( command.accelerator()); } +bool BrowserActionsContainer::ShownInsideMenu() const { + return in_overflow_mode(); +} + void BrowserActionsContainer::AddObserver( BrowserActionsContainerObserver* observer) { observers_.AddObserver(observer); @@ -244,23 +280,37 @@ void BrowserActionsContainer::RemoveObserver( } gfx::Size BrowserActionsContainer::GetPreferredSize() const { + size_t icon_count = browser_action_views_.size() - + (in_overflow_mode() ? main_container_->VisibleBrowserActions() : 0); + + // If there are no actions to show, or we are in overflow mode and the main + // container is already showing them all, then no further work is required. + if (icon_count == 0) + return gfx::Size(); + + if (in_overflow_mode()) { + // When in overflow, y is multiline, so the pixel count is IconHeight() + // times the number of rows needed. + return gfx::Size( + IconCountToWidth(kIconsPerMenuRow, false), + (((icon_count - 1) / kIconsPerMenuRow) + 1) * IconHeight()); + } + // We calculate the size of the view by taking the current width and // subtracting resize_amount_ (the latter represents how far the user is // resizing the view or, if animating the snapping, how far to animate it). // But we also clamp it to a minimum size and the maximum size, so that the - // container can never shrink too far or take up more space than it needs. In - // other words: MinimumNonemptyWidth() < width() - resize < ClampTo(MAX). + // container can never shrink too far or take up more space than it needs. + // In other words: MinimumNonemptyWidth() < width() - resize < ClampTo(MAX). int preferred_width = std::min( std::max(MinimumNonemptyWidth(), container_width_ - resize_amount_), IconCountToWidth(-1, false)); - // Height will be ignored by the ToolbarView. - return gfx::Size(preferred_width, 0); + return gfx::Size(preferred_width, IconHeight()); } gfx::Size BrowserActionsContainer::GetMinimumSize() const { int min_width = std::min(MinimumNonemptyWidth(), IconCountToWidth(-1, false)); - // Height will be ignored by the ToolbarView. - return gfx::Size(min_width, 0); + return gfx::Size(min_width, IconHeight()); } void BrowserActionsContainer::Layout() { @@ -270,11 +320,12 @@ void BrowserActionsContainer::Layout() { } SetVisible(true); - resize_area_->SetBounds(0, 0, kItemSpacing, height()); + if (resize_area_) + resize_area_->SetBounds(0, 0, kItemSpacing, height()); // If the icons don't all fit, show the chevron (unless suppressed). int max_x = GetPreferredSize().width(); - if ((IconCountToWidth(-1, false) > max_x) && !suppress_chevron_) { + if ((IconCountToWidth(-1, false) > max_x) && !suppress_chevron_ && chevron_) { chevron_->SetVisible(true); gfx::Size chevron_size(chevron_->GetPreferredSize()); max_x -= @@ -284,20 +335,44 @@ void BrowserActionsContainer::Layout() { 0, chevron_size.width(), chevron_size.height()); - } else { + } else if (chevron_) { chevron_->SetVisible(false); } // Now draw the icons for the browser actions in the available space. int icon_width = IconWidth(false); - for (size_t i = 0; i < browser_action_views_.size(); ++i) { - BrowserActionView* view = browser_action_views_[i]; - int x = ToolbarView::kStandardSpacing + (i * IconWidth(true)); - if (x + icon_width <= max_x) { - view->SetBounds(x, 0, icon_width, height()); + if (in_overflow_mode()) { + for (size_t i = 0; + i < main_container_->VisibleBrowserActionsAfterAnimation(); ++i) { + // Ensure that any browser actions shown in the main view are hidden in + // the overflow view. + browser_action_views_[i]->SetVisible(false); + } + + for (size_t i = main_container_->VisibleBrowserActionsAfterAnimation(); + i < browser_action_views_.size(); ++i) { + BrowserActionView* view = browser_action_views_[i]; + size_t index = i - main_container_->VisibleBrowserActionsAfterAnimation(); + int row_index = static_cast(index) / kIconsPerMenuRow; + int x = (index * IconWidth(true)) - + (row_index * IconWidth(true) * kIconsPerMenuRow); + gfx::Rect rect_bounds( + x, + IconHeight() * row_index, + icon_width, + IconHeight() + kPaddingForBadge); + view->SetBoundsRect(rect_bounds); view->SetVisible(true); - } else { - view->SetVisible(false); + } + } else { + for (BrowserActionViews::const_iterator it = browser_action_views_.begin(); + it < browser_action_views_.end(); ++it) { + BrowserActionView* view = *it; + int x = ToolbarView::kStandardSpacing + + ((it - browser_action_views_.begin()) * IconWidth(true)); + view->SetVisible(x + icon_width <= max_x); + if (view->visible()) + view->SetBounds(x, 0, icon_width, IconHeight()); } } } @@ -333,13 +408,18 @@ int BrowserActionsContainer::OnDragUpdated( } StopShowFolderDropMenuTimer(); + // TODO(devlin): This calculation needs to take 'overflow' mode into account + // once the wrench menu becomes a drag target for browser action icons. + // Figure out where to display the indicator. This is a complex calculation: // First, we figure out how much space is to the left of the icon area, so we // can calculate the true offset into the icon area. - int width_before_icons = ToolbarView::kStandardSpacing + - (base::i18n::IsRTL() ? - (chevron_->GetPreferredSize().width() + kChevronSpacing) : 0); + int width_before_icons = ToolbarView::kStandardSpacing; + if (chevron_ && base::i18n::IsRTL()) { + width_before_icons += + chevron_->GetPreferredSize().width() + kChevronSpacing; + } int offset_into_icon_area = event.x() - width_before_icons; // Next, we determine which icon to place the indicator in front of. We want @@ -556,8 +636,10 @@ void BrowserActionsContainer::OnBrowserActionExecuted( void BrowserActionsContainer::OnBrowserActionVisibilityChanged() { SetVisible(!browser_action_views_.empty()); - owner_view_->Layout(); - owner_view_->SchedulePaint(); + if (owner_view_) { + owner_view_->Layout(); + owner_view_->SchedulePaint(); + } } extensions::ActiveTabPermissionGranter* @@ -765,11 +847,12 @@ void BrowserActionsContainer::BrowserActionRemoved(const Extension* extension) { } else { // Either we went from overflow to no-overflow, or we shrunk the no- // overflow container by 1. Either way the size changed, so animate. - chevron_->SetVisible(false); + if (chevron_) + chevron_->SetVisible(false); SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT, browser_action_views_.size()); } - return; + return; // We have found the action to remove, bail out. } } } @@ -811,6 +894,9 @@ void BrowserActionsContainer::HighlightModeChanged(bool is_highlighting) { void BrowserActionsContainer::LoadImages() { ui::ThemeProvider* tp = GetThemeProvider(); + if (!tp || !chevron_) + return; + chevron_->SetImage(views::Button::STATE_NORMAL, *tp->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW)); @@ -819,12 +905,19 @@ void BrowserActionsContainer::LoadImages() { } void BrowserActionsContainer::SetContainerWidth() { - int visible_actions = model_->GetVisibleIconCount(); + // The slave only draws the overflow (what isn't visible in the other + // container). + int visible_actions = in_overflow_mode() ? + model_->toolbar_items().size() - model_->GetVisibleIconCount() : + model_->GetVisibleIconCount(); if (visible_actions < 0) // All icons should be visible. visible_actions = model_->toolbar_items().size(); - chevron_->SetVisible( - static_cast(visible_actions) < model_->toolbar_items().size()); - container_width_ = IconCountToWidth(visible_actions, chevron_->visible()); + if (chevron_) { + chevron_->SetVisible( + static_cast(visible_actions) < model_->toolbar_items().size()); + } + container_width_ = + IconCountToWidth(visible_actions, chevron_ && chevron_->visible()); } void BrowserActionsContainer::CloseOverflowMenu() { @@ -868,7 +961,7 @@ int BrowserActionsContainer::IconCountToWidth(int icons, return ToolbarView::kStandardSpacing; int icons_size = (icons == 0) ? 0 : ((icons * IconWidth(true)) - kItemSpacing); - int chevron_size = display_chevron ? + int chevron_size = chevron_ && display_chevron ? (kChevronSpacing + chevron_->GetPreferredSize().width()) : 0; return ToolbarView::kStandardSpacing + icons_size + chevron_size + ToolbarView::kStandardSpacing; @@ -882,8 +975,8 @@ size_t BrowserActionsContainer::WidthToIconCount(int pixels) const { // We need to reserve space for the resize area, chevron, and the spacing on // either side of the chevron. int available_space = pixels - ToolbarView::kStandardSpacing - - chevron_->GetPreferredSize().width() - kChevronSpacing - - ToolbarView::kStandardSpacing; + (chevron_ ? chevron_->GetPreferredSize().width() : 0) - + kChevronSpacing - ToolbarView::kStandardSpacing; // Now we add an extra between-item padding value so the space can be divided // evenly by (size of icon with padding). return static_cast( @@ -891,8 +984,10 @@ size_t BrowserActionsContainer::WidthToIconCount(int pixels) const { } int BrowserActionsContainer::MinimumNonemptyWidth() const { - return ToolbarView::kStandardSpacing + kChevronSpacing + - chevron_->GetPreferredSize().width() + ToolbarView::kStandardSpacing; + if (!chevron_) + return ToolbarView::kStandardSpacing; + return (ToolbarView::kStandardSpacing * 2) + kChevronSpacing + + chevron_->GetPreferredSize().width(); } void BrowserActionsContainer::SaveDesiredSizeAndAnimate( @@ -908,7 +1003,7 @@ void BrowserActionsContainer::SaveDesiredSizeAndAnimate( model_->SetVisibleIconCount(num_visible_icons); int target_size = IconCountToWidth(num_visible_icons, num_visible_icons < browser_action_views_.size()); - if (!disable_animations_during_testing_) { + if (resize_animation_ && !disable_animations_during_testing_) { // Animate! We have to set the animation_target_size_ after calling Reset(), // because that could end up calling AnimationEnded which clears the value. resize_animation_->Reset(); diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.h b/chrome/browser/ui/views/toolbar/browser_actions_container.h index 9874e79cbcfb..e25ab66f8d2a 100644 --- a/chrome/browser/ui/views/toolbar/browser_actions_container.h +++ b/chrome/browser/ui/views/toolbar/browser_actions_container.h @@ -42,22 +42,35 @@ namespace views { class ResizeArea; } -//////////////////////////////////////////////////////////////////////////////// -// // The BrowserActionsContainer is a container view, responsible for drawing the -// browser action icons (extensions that add icons to the toolbar). +// browser action icons (extensions that add icons to the toolbar). It comes in +// two flavors, a main container (when residing on the toolbar) and an overflow +// container (that resides in the main application menu, aka the Chrome menu). +// +// When in 'main' mode, the container supports the full functionality of a +// BrowserActionContainer, but in 'overflow' mode the container is effectively +// just an overflow for the 'main' toolbar (shows only the icons that the main +// toolbar does not) and as such does not have an overflow itself. The overflow +// container also does not support resizing. Since the main container only shows +// icons in the Chrome toolbar, it is limited to a single row of icons. The +// overflow container, however, is allowed to display icons in multiple rows. // -// The container is placed flush against the omnibox and wrench menu, and its -// layout looks like: +// The main container is placed flush against the omnibox and hot dog menu, +// whereas the overflow container is placed within the hot dog menu. The +// layout is similar to this: // rI_I_IcCs // Where the letters are as follows: // r: An invisible resize area. This is ToolbarView::kStandardSpacing pixels -// wide and directly adjacent to the omnibox. +// wide and directly adjacent to the omnibox. Only shown for the main +// container. // I: An icon. This is as wide as the IDR_BROWSER_ACTION image. // _: kItemSpacing pixels of empty space. // c: kChevronSpacing pixels of empty space. Only present if C is present. -// C: An optional chevron, visible for overflow. As wide as the -// IDR_BROWSER_ACTIONS_OVERFLOW image. +// C: An optional chevron, as wide as the IDR_BROWSER_ACTIONS_OVERFLOW image, +// and visible only when both of the following statements are true: +// - The container is set to a width smaller than needed to show all icons. +// - There is no other container in 'overflow' mode to handle the +// non-visible icons for this container. // s: ToolbarView::kStandardSpacing pixels of empty space (before the wrench // menu). // The reason the container contains the trailing space "s", rather than having @@ -66,7 +79,8 @@ class ResizeArea; // ultimate drop indicator. (Otherwise, we'd be trying to draw it into the // padding beyond our right edge, and it wouldn't appear.) // -// The BrowserActionsContainer follows a few rules, in terms of user experience: +// The BrowserActionsContainer in 'main' mode follows a few rules, in terms of +// user experience: // // 1) The container can never grow beyond the space needed to show all icons // (hereby referred to as the max width). @@ -81,7 +95,7 @@ class ResizeArea; // 5) If the container is NOT at max width (has an overflow menu), we respect // that size when adding and removing icons and DON'T grow/shrink the container. // This means that new icons (which always appear at the far right) will show up -// in the overflow menu. The install bubble for extensions points to the chevron +// in the overflow. The install bubble for extensions points to the chevron // menu in this case. // // Resizing the BrowserActionsContainer: @@ -125,7 +139,12 @@ class BrowserActionsContainer public BrowserActionView::Delegate, public extensions::ExtensionKeybindingRegistry::Delegate { public: - BrowserActionsContainer(Browser* browser, views::View* owner_view); + // Constructs a BrowserActionContainer for a particular |browser| object, and + // specifies which view is the |owner_view|. For documentation of + // |main_container|, see class comments. + BrowserActionsContainer(Browser* browser, + views::View* owner_view, + BrowserActionsContainer* main_container); virtual ~BrowserActionsContainer(); void Init(); @@ -225,6 +244,7 @@ class BrowserActionsContainer virtual int GetCurrentTabId() const OVERRIDE; virtual void OnBrowserActionExecuted(BrowserActionButton* button) OVERRIDE; virtual void OnBrowserActionVisibilityChanged() OVERRIDE; + virtual bool ShownInsideMenu() const OVERRIDE; // Overridden from extension::ExtensionKeybindingRegistry::Delegate: virtual extensions::ActiveTabPermissionGranter* @@ -346,6 +366,10 @@ class BrowserActionsContainer ExtensionPopup::ShowAction show_action, bool grant_tab_permissions); + // Whether this container is in overflow mode (as opposed to in 'main' + // mode). See class comments for details on the difference. + bool in_overflow_mode() const { return main_container_ != NULL; } + // The vector of browser actions (icons/image buttons for each action). Note // that not every BrowserAction in the ToolbarModel will necessarily be in // this collection. Some extensions may be disabled in incognito windows. @@ -359,6 +383,11 @@ class BrowserActionsContainer // The view that owns us. views::View* owner_view_; + // The main container we are serving as overflow for, or NULL if this + // class is the the main container. See class comments for details on + // the difference between main and overflow. + BrowserActionsContainer* main_container_; + // The current popup and the button it came from. NULL if no popup. ExtensionPopup* popup_; @@ -375,7 +404,8 @@ class BrowserActionsContainer // The resize area for the container. views::ResizeArea* resize_area_; - // The chevron for accessing the overflow items. + // The chevron for accessing the overflow items. Can be NULL when in overflow + // mode or if the toolbar is permanently suppressing the chevron menu. views::MenuButton* chevron_; // The painter used when we are highlighting a subset of extensions. diff --git a/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc new file mode 100644 index 000000000000..ec259a9c6fbe --- /dev/null +++ b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.cc @@ -0,0 +1,51 @@ +// Copyright 2014 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/ui/views/toolbar/extension_toolbar_menu_view.h" + +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/toolbar/browser_actions_container.h" +#include "chrome/browser/ui/views/toolbar/toolbar_view.h" +#include "ui/views/controls/menu/menu_item_view.h" + +namespace { + +// Bottom padding to make sure we have enough room for the icons. +// TODO(devlin): Figure out why the bottom few pixels of the last row in the +// overflow menu are cut off (so we can remove this). +const int kVerticalPadding = 8; + +} // namespace + +ExtensionToolbarMenuView::ExtensionToolbarMenuView(Browser* browser) + : browser_(browser) { + BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); + container_ = new BrowserActionsContainer( + browser_, + NULL, // No owner view, means no extra keybindings are registered. + browser_view->GetToolbarView()->browser_actions()); + container_->Init(); + AddChildView(container_); +} + +ExtensionToolbarMenuView::~ExtensionToolbarMenuView() { +} + +gfx::Size ExtensionToolbarMenuView::GetPreferredSize() const { + gfx::Size sz = container_->GetPreferredSize(); + if (sz.height() == 0) + return sz; + + sz.Enlarge(0, kVerticalPadding); + return sz; +} + +void ExtensionToolbarMenuView::Layout() { + // All buttons are given the same width. + gfx::Size sz = container_->GetPreferredSize(); + container_->SetBounds(views::MenuItemView::label_start(), + 0, + sz.width(), + sz.height() + (kVerticalPadding / 2)); +} diff --git a/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.h b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.h new file mode 100644 index 000000000000..af1e9934ea32 --- /dev/null +++ b/chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.h @@ -0,0 +1,32 @@ +// Copyright 2014 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_UI_VIEWS_TOOLBAR_EXTENSION_TOOLBAR_MENU_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_EXTENSION_TOOLBAR_MENU_VIEW_H_ + +#include "ui/views/view.h" + +class Browser; +class BrowserActionsContainer; + +// ExtensionToolbarMenuView is the view containing the extension actions that +// overflowed from the BrowserActionsContainer, and is contained in and owned by +// the wrench menu. +class ExtensionToolbarMenuView : public views::View { + public: + explicit ExtensionToolbarMenuView(Browser* browser); + virtual ~ExtensionToolbarMenuView(); + + // views::View: + virtual gfx::Size GetPreferredSize() const OVERRIDE; + virtual void Layout() OVERRIDE; + + private: + Browser* browser_; + BrowserActionsContainer* container_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionToolbarMenuView); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_TOOLBAR_EXTENSION_TOOLBAR_MENU_VIEW_H_ diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc index 9c0ee34091d3..107005e2e3be 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc @@ -225,7 +225,10 @@ void ToolbarView::Init() { home_->set_id(VIEW_ID_HOME_BUTTON); home_->Init(); - browser_actions_ = new BrowserActionsContainer(browser_, this); + browser_actions_ = new BrowserActionsContainer( + browser_, + this, // Owner. + NULL); // No master container for this one (it is master). app_menu_ = new WrenchToolbarButton(this); app_menu_->EnableCanvasFlippingForRTLUI(true); diff --git a/chrome/browser/ui/views/toolbar/wrench_menu.cc b/chrome/browser/ui/views/toolbar/wrench_menu.cc index 6cbced8b55c8..496d8c3b8164 100644 --- a/chrome/browser/ui/views/toolbar/wrench_menu.cc +++ b/chrome/browser/ui/views/toolbar/wrench_menu.cc @@ -21,6 +21,7 @@ #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/toolbar/wrench_menu_model.h" #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h" +#include "chrome/browser/ui/views/toolbar/extension_toolbar_menu_view.h" #include "chrome/browser/ui/views/toolbar/wrench_menu_observer.h" #include "components/bookmarks/browser/bookmark_model.h" #include "content/public/browser/host_zoom_map.h" @@ -30,6 +31,7 @@ #include "content/public/browser/notification_types.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" +#include "extensions/common/feature_switch.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -678,9 +680,8 @@ class WrenchMenu::ZoomView : public WrenchMenuView { menu->use_new_menu() ? kHorizontalTouchPadding : kHorizontalPadding; fullscreen_button_->SetBorder(views::Border::CreateEmptyBorder( 0, horizontal_padding, 0, horizontal_padding)); - fullscreen_button_->set_background( - new InMenuButtonBackground(InMenuButtonBackground::SINGLE_BUTTON, - menu->use_new_menu())); + fullscreen_button_->set_background(new InMenuButtonBackground( + InMenuButtonBackground::SINGLE_BUTTON, menu->use_new_menu())); fullscreen_button_->SetAccessibleName( GetAccessibleNameForWrenchMenuItem( menu_model, fullscreen_index, IDS_ACCNAME_FULLSCREEN)); @@ -1144,10 +1145,11 @@ bool WrenchMenu::IsCommandEnabled(int command_id) const { if (command_id == 0) return false; // The root item. - // The items representing the cut menu (cut/copy/paste) and zoom menu - // (increment/decrement/reset) are always enabled. The child views of these - // items enabled state updates appropriately. - if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) + // The items representing the cut menu (cut/copy/paste), zoom menu + // (increment/decrement/reset) and extension toolbar view are always enabled. + // The child views of these items enabled state updates appropriately. + if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS || + command_id == IDC_EXTENSIONS_OVERFLOW_MENU) return true; const Entry& entry = command_id_to_entry_.find(command_id)->second; @@ -1160,7 +1162,8 @@ void WrenchMenu::ExecuteCommand(int command_id, int mouse_event_flags) { return; } - if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) { + if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS || + command_id == IDC_EXTENSIONS_OVERFLOW_MENU) { // These items are represented by child views. If ExecuteCommand is invoked // it means the user clicked on the area around the buttons and we should // not do anyting. @@ -1176,7 +1179,8 @@ bool WrenchMenu::GetAccelerator(int command_id, if (IsBookmarkCommand(command_id)) return false; - if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) { + if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS || + command_id == IDC_EXTENSIONS_OVERFLOW_MENU) { // These have special child views; don't show the accelerator for them. return false; } @@ -1243,6 +1247,12 @@ void WrenchMenu::PopulateMenu(MenuItemView* parent, model->GetCommandIdAt(i) == IDC_ZOOM_MINUS)) height = kMenuItemContainingButtonsHeight; + scoped_ptr extension_toolbar_menu_view; + if (model->GetCommandIdAt(i) == IDC_EXTENSIONS_OVERFLOW_MENU) { + extension_toolbar_menu_view.reset(new ExtensionToolbarMenuView(browser_)); + height = extension_toolbar_menu_view->GetPreferredSize().height(); + } + // Add the menu item at the end. int menu_index = parent->HasSubmenu() ? parent->GetSubmenu()->child_count() : 0; @@ -1253,6 +1263,12 @@ void WrenchMenu::PopulateMenu(MenuItemView* parent, PopulateMenu(item, model->GetSubmenuModelAt(i)); switch (model->GetCommandIdAt(i)) { + case IDC_EXTENSIONS_OVERFLOW_MENU: + if (height > 0) + item->AddChildView(extension_toolbar_menu_view.release()); + else + item->SetVisible(false); + break; case IDC_CUT: DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(i)); DCHECK_LT(i + 2, max); @@ -1326,6 +1342,8 @@ MenuItemView* WrenchMenu::AddMenuItem(MenuItemView* parent, // For menu items with a special menu height we use our special class to be // able to modify the item height. menu_item = new ButtonContainerMenuItemView(parent, command_id, height); + if (!parent->GetSubmenu()) + parent->CreateSubmenu(); parent->GetSubmenu()->AddChildViewAt(menu_item, menu_index); } else { // For all other cases we use the more generic way to add menu items. diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index 692eb7e29780..10fb472284b3 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi @@ -2280,6 +2280,8 @@ 'browser/ui/views/toolbar/browser_action_test_util_views.cc', 'browser/ui/views/toolbar/browser_action_view.cc', 'browser/ui/views/toolbar/browser_action_view.h', + 'browser/ui/views/toolbar/extension_toolbar_menu_view.cc', + 'browser/ui/views/toolbar/extension_toolbar_menu_view.h', 'browser/ui/views/toolbar/home_button.cc', 'browser/ui/views/toolbar/home_button.h', 'browser/ui/views/toolbar/reload_button.cc', diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index ad91673f05e7..ac5cc1bb15f6 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -107,6 +107,8 @@ 'common/extensions/manifest_handlers/nacl_modules_handler.h', 'common/extensions/manifest_handlers/settings_overrides_handler.cc', 'common/extensions/manifest_handlers/settings_overrides_handler.h', + 'common/extensions/manifest_handlers/synthesize_browser_action_handler.cc', + 'common/extensions/manifest_handlers/synthesize_browser_action_handler.h', 'common/extensions/manifest_handlers/theme_handler.cc', 'common/extensions/manifest_handlers/theme_handler.h', 'common/extensions/manifest_handlers/ui_overrides_handler.cc', diff --git a/chrome/common/extensions/chrome_manifest_handlers.cc b/chrome/common/extensions/chrome_manifest_handlers.cc index 18c68a544c3f..b08d004790fd 100644 --- a/chrome/common/extensions/chrome_manifest_handlers.cc +++ b/chrome/common/extensions/chrome_manifest_handlers.cc @@ -30,6 +30,7 @@ #include "chrome/common/extensions/manifest_handlers/minimum_chrome_version_checker.h" #include "chrome/common/extensions/manifest_handlers/nacl_modules_handler.h" #include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h" +#include "chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.h" #include "chrome/common/extensions/manifest_handlers/theme_handler.h" #include "chrome/common/extensions/manifest_handlers/ui_overrides_handler.h" #include "chrome/common/extensions/manifest_url_handler.h" @@ -72,6 +73,7 @@ void RegisterChromeManifestHandlers() { (new SpellcheckHandler)->Register(); (new StorageSchemaManifestHandler)->Register(); (new SupervisedUserHandler)->Register(); + (new SynthesizeBrowserActionHandler)->Register(); (new SystemIndicatorHandler)->Register(); (new ThemeHandler)->Register(); (new TtsEngineManifestHandler)->Register(); diff --git a/chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.cc b/chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.cc new file mode 100644 index 000000000000..c824c9fa8166 --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.cc @@ -0,0 +1,49 @@ +// Copyright 2014 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/common/extensions/manifest_handlers/synthesize_browser_action_handler.h" + +#include "chrome/common/extensions/api/extension_action/action_info.h" +#include "extensions/common/feature_switch.h" +#include "extensions/common/manifest_constants.h" + +namespace extensions { + +SynthesizeBrowserActionHandler::SynthesizeBrowserActionHandler() { +} + +SynthesizeBrowserActionHandler::~SynthesizeBrowserActionHandler() { +} + +bool SynthesizeBrowserActionHandler::Parse(Extension* extension, + base::string16* error) { + if (!extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) + return true; // Do nothing. + + if (extension->location() == Manifest::COMPONENT || + extension->location() == Manifest::EXTERNAL_COMPONENT) + return true; // Return no error (we're done). + + if (extension->manifest()->HasKey(manifest_keys::kSynthesizeBrowserAction)) + return false; // This key is reserved, no extension should be using it. + + // TODO(devlin): Make sure we don't have two icons showing for page action + // extensions if we show page action icons in the browser action + // toolbar. + if (!extension->manifest()->HasKey(manifest_keys::kBrowserAction)) + ActionInfo::SetBrowserActionInfo(extension, new ActionInfo()); + + return true; +} + +bool SynthesizeBrowserActionHandler::AlwaysParseForType( + Manifest::Type type) const { + return type == Manifest::TYPE_EXTENSION; +} + +const std::vector SynthesizeBrowserActionHandler::Keys() const { + return SingleKey(manifest_keys::kSynthesizeBrowserAction); +} + +} // namespace extensions diff --git a/chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.h b/chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.h new file mode 100644 index 000000000000..dcbc18f68054 --- /dev/null +++ b/chrome/common/extensions/manifest_handlers/synthesize_browser_action_handler.h @@ -0,0 +1,33 @@ +// Copyright 2014 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_COMMON_EXTENSIONS_MANIFEST_HANDLERS_SYNTHESIZE_BROWSER_ACTION_HANDLER_H_ +#define CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_SYNTHESIZE_BROWSER_ACTION_HANDLER_H_ + +#include + +#include "extensions/common/manifest_handler.h" + +namespace extensions { + +// Ensures that an extension that doesn't specify any icon in its manifest, gets +// a browser action icon, that we can hang UI off of. +// TODO(finnur): Combine the logic, as suggested in review 324393002. +class SynthesizeBrowserActionHandler : public ManifestHandler { + public: + SynthesizeBrowserActionHandler(); + virtual ~SynthesizeBrowserActionHandler(); + + virtual bool Parse(Extension* extension, base::string16* error) OVERRIDE; + virtual bool AlwaysParseForType(Manifest::Type type) const OVERRIDE; + + private: + virtual const std::vector Keys() const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(SynthesizeBrowserActionHandler); +}; + +} // namespace extensions + +#endif // CHROME_COMMON_EXTENSIONS_MANIFEST_HANDLERS_SYNTHESIZE_BROWSER_ACTION_HANDLER_H_ diff --git a/extensions/common/feature_switch.cc b/extensions/common/feature_switch.cc index 121dceb25ce8..eaee4a7fb1d1 100644 --- a/extensions/common/feature_switch.cc +++ b/extensions/common/feature_switch.cc @@ -43,6 +43,9 @@ class CommonSwitches { enable_override_bookmarks_ui( switches::kEnableOverrideBookmarksUI, FeatureSwitch::DEFAULT_DISABLED), + extension_action_redesign( + switches::kExtensionActionRedesign, + FeatureSwitch::DEFAULT_DISABLED), scripts_require_action(switches::kScriptsRequireAction, FeatureSwitch::DEFAULT_DISABLED) {} @@ -59,6 +62,7 @@ class CommonSwitches { FeatureSwitch error_console; FeatureSwitch enable_override_bookmarks_ui; + FeatureSwitch extension_action_redesign; FeatureSwitch scripts_require_action; }; @@ -85,7 +89,9 @@ FeatureSwitch* FeatureSwitch::error_console() { FeatureSwitch* FeatureSwitch::enable_override_bookmarks_ui() { return &g_common_switches.Get().enable_override_bookmarks_ui; } - +FeatureSwitch* FeatureSwitch::extension_action_redesign() { + return &g_common_switches.Get().extension_action_redesign; +} FeatureSwitch* FeatureSwitch::scripts_require_action() { return &g_common_switches.Get().scripts_require_action; } diff --git a/extensions/common/feature_switch.h b/extensions/common/feature_switch.h index 4b9cdd93e149..9f41990e8170 100644 --- a/extensions/common/feature_switch.h +++ b/extensions/common/feature_switch.h @@ -25,6 +25,7 @@ class FeatureSwitch { static FeatureSwitch* prompt_for_external_extensions(); static FeatureSwitch* error_console(); static FeatureSwitch* enable_override_bookmarks_ui(); + static FeatureSwitch* extension_action_redesign(); static FeatureSwitch* scripts_require_action(); enum DefaultValue { diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc index b7af0ec12b4f..95d6d8b43fcb 100644 --- a/extensions/common/manifest_constants.cc +++ b/extensions/common/manifest_constants.cc @@ -139,6 +139,7 @@ const char kSpellcheckDictionaryLocale[] = "dictionary_locale"; const char kSpellcheckDictionaryPath[] = "dictionary_path"; const char kStorageManagedSchema[] = "storage.managed_schema"; const char kSuggestedKey[] = "suggested_key"; +const char kSynthesizeBrowserAction[] = "_synthesize_browser_action"; const char kSystemIndicator[] = "system_indicator"; const char kTheme[] = "theme"; const char kThemeColors[] = "colors"; diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h index a2f93f0f2e11..9852e88ce1b4 100644 --- a/extensions/common/manifest_constants.h +++ b/extensions/common/manifest_constants.h @@ -146,6 +146,7 @@ extern const char kSpellcheckDictionaryLocale[]; extern const char kSpellcheckDictionaryPath[]; extern const char kStorageManagedSchema[]; extern const char kSuggestedKey[]; +extern const char kSynthesizeBrowserAction[]; extern const char kSystemIndicator[]; extern const char kTheme[]; extern const char kThemeColors[]; diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc index d6ab3a1697f1..a81510946842 100644 --- a/extensions/common/switches.cc +++ b/extensions/common/switches.cc @@ -35,6 +35,9 @@ const char kEventPageIdleTime[] = "event-page-idle-time"; // notified of its impending unload and that unload happening. const char kEventPageSuspendingTime[] = "event-page-unloading-time"; +// Whether to switch to extension action redesign mode (experimental). +const char kExtensionActionRedesign[] = "extension-action-redesign"; + // Marks a renderer as extension process. const char kExtensionProcess[] = "extension-process"; diff --git a/extensions/common/switches.h b/extensions/common/switches.h index e81d5dc0673d..29fe572ee973 100644 --- a/extensions/common/switches.h +++ b/extensions/common/switches.h @@ -18,6 +18,7 @@ extern const char kEnableOverrideBookmarksUI[]; extern const char kErrorConsole[]; extern const char kEventPageIdleTime[]; extern const char kEventPageSuspendingTime[]; +extern const char kExtensionActionRedesign[]; extern const char kExtensionProcess[]; extern const char kExtensionsOnChromeURLs[]; extern const char kForceDevModeHighlighting[]; -- 2.11.4.GIT