Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / extensions / extension_installed_bubble_view.cc
blob136716415f682a6bc71d3a73a357c8d46a305203
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/extensions/extension_installed_bubble_view.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/i18n/rtl.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/extensions/api/commands/command_service.h"
14 #include "chrome/browser/extensions/extension_action.h"
15 #include "chrome/browser/extensions/extension_action_manager.h"
16 #include "chrome/browser/extensions/extension_install_ui.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/signin/signin_promo.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/singleton_tabs.h"
22 #include "chrome/browser/ui/sync/sync_promo_ui.h"
23 #include "chrome/browser/ui/views/frame/browser_view.h"
24 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
25 #include "chrome/browser/ui/views/tabs/tab_strip.h"
26 #include "chrome/browser/ui/views/toolbar/browser_action_view.h"
27 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
28 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
29 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
30 #include "chrome/common/extensions/sync_helper.h"
31 #include "chrome/common/url_constants.h"
32 #include "extensions/common/extension.h"
33 #include "grit/chromium_strings.h"
34 #include "grit/generated_resources.h"
35 #include "grit/ui_resources.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/gfx/render_text.h"
39 #include "ui/gfx/text_elider.h"
40 #include "ui/views/controls/button/image_button.h"
41 #include "ui/views/controls/image_view.h"
42 #include "ui/views/controls/label.h"
43 #include "ui/views/controls/link.h"
44 #include "ui/views/controls/link_listener.h"
45 #include "ui/views/layout/fill_layout.h"
46 #include "ui/views/layout/layout_constants.h"
48 using extensions::Extension;
50 namespace {
52 const int kIconSize = 43;
54 const int kRightColumnWidth = 285;
56 // The Bubble uses a BubbleBorder which adds about 6 pixels of whitespace
57 // around the content view. We compensate by reducing our outer borders by this
58 // amount + 4px.
59 const int kOuterMarginInset = 10;
60 const int kHorizOuterMargin = views::kPanelHorizMargin - kOuterMarginInset;
61 const int kVertOuterMargin = views::kPanelVertMargin - kOuterMarginInset;
63 // Interior vertical margin is 8px smaller than standard
64 const int kVertInnerMargin = views::kPanelVertMargin - 8;
66 // We want to shift the right column (which contains the header and text) up
67 // 4px to align with icon.
68 const int kRightcolumnVerticalShift = -4;
70 } // namespace
72 namespace chrome {
74 void ShowExtensionInstalledBubble(const Extension* extension,
75 Browser* browser,
76 const SkBitmap& icon) {
77 ExtensionInstalledBubbleView::Show(extension, browser, icon);
80 } // namespace chrome
82 // InstalledBubbleContent is the content view which is placed in the
83 // ExtensionInstalledBubbleView. It displays the install icon and explanatory
84 // text about the installed extension.
85 class InstalledBubbleContent : public views::View,
86 public views::ButtonListener,
87 public views::LinkListener {
88 public:
89 InstalledBubbleContent(Browser* browser,
90 const Extension* extension,
91 ExtensionInstalledBubble::BubbleType type,
92 const SkBitmap* icon,
93 ExtensionInstalledBubbleView* bubble)
94 : browser_(browser),
95 extension_id_(extension->id()),
96 bubble_(bubble),
97 type_(type),
98 flavors_(NONE),
99 height_of_signin_promo_(0u),
100 how_to_use_(NULL),
101 sign_in_link_(NULL),
102 manage_(NULL),
103 manage_shortcut_(NULL) {
104 // The Extension Installed bubble takes on various forms, depending on the
105 // type of extension installed. In general, though, they are all similar:
107 // -------------------------
108 // | | Heading [X] |
109 // | Icon | Info |
110 // | | Extra info |
111 // -------------------------
113 // Icon and Heading are always shown (as well as the close button).
114 // Info is shown for browser actions, page actions and Omnibox keyword
115 // extensions and might list keyboard shorcut for the former two types.
116 // Extra info is...
117 // ... for other types, either a description of how to manage the extension
118 // or a link to configure the keybinding shortcut (if one exists).
119 // Extra info can include a promo for signing into sync.
121 // First figure out the keybinding situation.
122 extensions::Command command;
123 bool has_keybinding = GetKeybinding(&command);
124 base::string16 key; // Keyboard shortcut or keyword to display in bubble.
126 if (extensions::sync_helper::IsSyncableExtension(extension) &&
127 SyncPromoUI::ShouldShowSyncPromo(browser->profile()))
128 flavors_ |= SIGN_IN_PROMO;
130 // Determine the bubble flavor we want, based on the extension type.
131 switch (type_) {
132 case ExtensionInstalledBubble::BROWSER_ACTION:
133 case ExtensionInstalledBubble::PAGE_ACTION: {
134 flavors_ |= HOW_TO_USE;
135 if (has_keybinding) {
136 flavors_ |= SHOW_KEYBINDING;
137 key = command.accelerator().GetShortcutText();
138 } else {
139 // The How-To-Use text makes the bubble seem a little crowded when the
140 // extension has a keybinding, so the How-To-Manage text is not shown
141 // in those cases.
142 flavors_ |= HOW_TO_MANAGE;
144 break;
146 case ExtensionInstalledBubble::OMNIBOX_KEYWORD: {
147 flavors_ |= HOW_TO_USE | HOW_TO_MANAGE;
148 key = base::UTF8ToUTF16(extensions::OmniboxInfo::GetKeyword(extension));
149 break;
151 case ExtensionInstalledBubble::GENERIC: {
152 break;
154 default: {
155 // When adding a new bubble type, the flavor needs to be set.
156 COMPILE_ASSERT(ExtensionInstalledBubble::GENERIC == 3,
157 kBubbleTypeEnumHasChangedButNotThisSwitchStatement);
158 break;
162 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
163 const gfx::FontList& font_list =
164 rb.GetFontList(ui::ResourceBundle::BaseFont);
166 // Add the icon (for all flavors).
167 // Scale down to 43x43, but allow smaller icons (don't scale up).
168 gfx::Size size(icon->width(), icon->height());
169 if (size.width() > kIconSize || size.height() > kIconSize)
170 size = gfx::Size(kIconSize, kIconSize);
171 icon_ = new views::ImageView();
172 icon_->SetImageSize(size);
173 icon_->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(*icon));
174 AddChildView(icon_);
176 // Add the heading (for all flavors).
177 base::string16 extension_name = base::UTF8ToUTF16(extension->name());
178 base::i18n::AdjustStringForLocaleDirection(&extension_name);
179 heading_ = new views::Label(l10n_util::GetStringFUTF16(
180 IDS_EXTENSION_INSTALLED_HEADING, extension_name));
181 heading_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
182 heading_->SetMultiLine(true);
183 heading_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
184 AddChildView(heading_);
186 if (flavors_ & HOW_TO_USE) {
187 how_to_use_ = new views::Label(GetHowToUseDescription(key));
188 how_to_use_->SetFontList(font_list);
189 how_to_use_->SetMultiLine(true);
190 how_to_use_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
191 AddChildView(how_to_use_);
194 if (flavors_ & SHOW_KEYBINDING) {
195 manage_shortcut_ = new views::Link(
196 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_MANAGE_SHORTCUTS));
197 manage_shortcut_->set_listener(this);
198 AddChildView(manage_shortcut_);
201 if (flavors_ & HOW_TO_MANAGE) {
202 manage_ = new views::Label(l10n_util::GetStringUTF16(
203 #if defined(OS_CHROMEOS)
204 IDS_EXTENSION_INSTALLED_MANAGE_INFO_CHROMEOS));
205 #else
206 IDS_EXTENSION_INSTALLED_MANAGE_INFO));
207 #endif
208 manage_->SetFontList(font_list);
209 manage_->SetMultiLine(true);
210 manage_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
211 AddChildView(manage_);
214 if (flavors_ & SIGN_IN_PROMO) {
215 signin_promo_text_ =
216 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_SIGNIN_PROMO);
218 signin_promo_link_text_ =
219 l10n_util::GetStringFUTF16(
220 IDS_EXTENSION_INSTALLED_SIGNIN_PROMO_LINK,
221 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
222 sign_in_link_ = new views::Link(signin_promo_link_text_);
223 sign_in_link_->SetFontList(font_list);
224 sign_in_link_->set_listener(this);
225 AddChildView(sign_in_link_);
228 // Add the Close button (for all flavors).
229 close_button_ = new views::ImageButton(this);
230 close_button_->SetImage(views::CustomButton::STATE_NORMAL,
231 rb.GetImageSkiaNamed(IDR_CLOSE_2));
232 close_button_->SetImage(views::CustomButton::STATE_HOVERED,
233 rb.GetImageSkiaNamed(IDR_CLOSE_2_H));
234 close_button_->SetImage(views::CustomButton::STATE_PRESSED,
235 rb.GetImageSkiaNamed(IDR_CLOSE_2_P));
236 AddChildView(close_button_);
239 virtual void ButtonPressed(views::Button* sender,
240 const ui::Event& event) OVERRIDE {
241 if (sender == close_button_)
242 bubble_->StartFade(false);
243 else
244 NOTREACHED() << "Unknown view";
247 // Implements the views::LinkListener interface.
248 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE {
249 GetWidget()->Close();
250 std::string configure_url;
251 if (source == manage_shortcut_) {
252 configure_url = chrome::kChromeUIExtensionsURL;
253 configure_url += chrome::kExtensionConfigureCommandsSubPage;
254 } else if (source == sign_in_link_) {
255 configure_url = signin::GetPromoURL(
256 signin::SOURCE_EXTENSION_INSTALL_BUBBLE, false).spec();
257 } else {
258 NOTREACHED();
259 return;
261 chrome::NavigateParams params(
262 chrome::GetSingletonTabNavigateParams(
263 browser_, GURL(configure_url.c_str())));
264 chrome::Navigate(&params);
267 private:
268 enum Flavors {
269 NONE = 0,
270 HOW_TO_USE = 1 << 0,
271 HOW_TO_MANAGE = 1 << 1,
272 SHOW_KEYBINDING = 1 << 2,
273 SIGN_IN_PROMO = 1 << 3,
276 bool GetKeybinding(extensions::Command* command) {
277 extensions::CommandService* command_service =
278 extensions::CommandService::Get(browser_->profile());
279 if (type_ == ExtensionInstalledBubble::BROWSER_ACTION) {
280 return command_service->GetBrowserActionCommand(
281 extension_id_,
282 extensions::CommandService::ACTIVE_ONLY,
283 command,
284 NULL);
285 } else if (type_ == ExtensionInstalledBubble::PAGE_ACTION) {
286 return command_service->GetPageActionCommand(
287 extension_id_,
288 extensions::CommandService::ACTIVE_ONLY,
289 command,
290 NULL);
291 } else {
292 return false;
296 base::string16 GetHowToUseDescription(const base::string16& key) {
297 switch (type_) {
298 case ExtensionInstalledBubble::BROWSER_ACTION:
299 if (!key.empty()) {
300 return l10n_util::GetStringFUTF16(
301 IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO_WITH_SHORTCUT, key);
302 } else {
303 return l10n_util::GetStringUTF16(
304 IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO);
306 break;
307 case ExtensionInstalledBubble::PAGE_ACTION:
308 if (!key.empty()) {
309 return l10n_util::GetStringFUTF16(
310 IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO_WITH_SHORTCUT, key);
311 } else {
312 return l10n_util::GetStringUTF16(
313 IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO);
315 break;
316 case ExtensionInstalledBubble::OMNIBOX_KEYWORD:
317 return l10n_util::GetStringFUTF16(
318 IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO, key);
319 break;
320 default:
321 NOTREACHED();
322 break;
324 return base::string16();
327 // Layout the signin promo at coordinates |offset_x| and |offset_y|. Returns
328 // the height (in pixels) of the promo UI.
329 int LayoutSigninPromo(int offset_x, int offset_y) {
330 sign_in_promo_lines_.clear();
331 int height = 0;
332 gfx::Rect contents_area = GetContentsBounds();
333 if (contents_area.IsEmpty())
334 return height;
335 contents_area.set_width(kRightColumnWidth);
337 base::string16 full_text = signin_promo_link_text_ + signin_promo_text_;
339 // The link is the first item in the text.
340 const gfx::Size link_size = sign_in_link_->GetPreferredSize();
341 sign_in_link_->SetBounds(
342 offset_x, offset_y, link_size.width(), link_size.height());
344 // Word-wrap the full label text.
345 const gfx::FontList font_list;
346 std::vector<base::string16> lines;
347 gfx::ElideRectangleText(full_text, font_list, contents_area.width(),
348 contents_area.height(), gfx::ELIDE_LONG_WORDS,
349 &lines);
351 gfx::Point position = gfx::Point(
352 contents_area.origin().x() + offset_x,
353 contents_area.origin().y() + offset_y + 1);
354 if (base::i18n::IsRTL()) {
355 position -= gfx::Vector2d(
356 2 * views::kPanelHorizMargin + kHorizOuterMargin, 0);
359 // Loop through the lines, creating a renderer for each.
360 for (std::vector<base::string16>::const_iterator it = lines.begin();
361 it != lines.end(); ++it) {
362 gfx::RenderText* line = gfx::RenderText::CreateInstance();
363 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
364 line->SetText(*it);
365 const gfx::Size size(contents_area.width(),
366 line->GetStringSize().height());
367 line->SetDisplayRect(gfx::Rect(position, size));
368 position.set_y(position.y() + size.height());
369 sign_in_promo_lines_.push_back(line);
370 height += size.height();
373 // The link is drawn separately; make it transparent here to only draw once.
374 // The link always leads other text and is assumed to fit on the first line.
375 sign_in_promo_lines_.front()->ApplyColor(SK_ColorTRANSPARENT,
376 gfx::Range(0, signin_promo_link_text_.size()));
378 return height;
381 virtual gfx::Size GetPreferredSize() OVERRIDE {
382 int width = kHorizOuterMargin;
383 width += kIconSize;
384 width += views::kPanelHorizMargin;
385 width += kRightColumnWidth;
386 width += 2 * views::kPanelHorizMargin;
387 width += kHorizOuterMargin;
389 int height = kVertOuterMargin;
390 height += heading_->GetHeightForWidth(kRightColumnWidth);
391 height += kVertInnerMargin;
393 if (flavors_ & HOW_TO_USE) {
394 height += how_to_use_->GetHeightForWidth(kRightColumnWidth);
395 height += kVertInnerMargin;
398 if (flavors_ & HOW_TO_MANAGE) {
399 height += manage_->GetHeightForWidth(kRightColumnWidth);
400 height += kVertInnerMargin;
403 if (flavors_ & SIGN_IN_PROMO && height_of_signin_promo_ > 0u) {
404 height += height_of_signin_promo_;
405 height += kVertInnerMargin;
408 if (flavors_ & SHOW_KEYBINDING) {
409 height += manage_shortcut_->GetHeightForWidth(kRightColumnWidth);
410 height += kVertInnerMargin;
413 return gfx::Size(width, std::max(height, kIconSize + 2 * kVertOuterMargin));
416 virtual void Layout() OVERRIDE {
417 int x = kHorizOuterMargin;
418 int y = kVertOuterMargin;
420 icon_->SetBounds(x, y, kIconSize, kIconSize);
421 x += kIconSize;
422 x += views::kPanelHorizMargin;
424 y += kRightcolumnVerticalShift;
425 heading_->SizeToFit(kRightColumnWidth);
426 heading_->SetX(x);
427 heading_->SetY(y);
428 y += heading_->height();
429 y += kVertInnerMargin;
431 if (flavors_ & HOW_TO_USE) {
432 how_to_use_->SizeToFit(kRightColumnWidth);
433 how_to_use_->SetX(x);
434 how_to_use_->SetY(y);
435 y += how_to_use_->height();
436 y += kVertInnerMargin;
439 if (flavors_ & HOW_TO_MANAGE) {
440 manage_->SizeToFit(kRightColumnWidth);
441 manage_->SetX(x);
442 manage_->SetY(y);
443 y += manage_->height();
444 y += kVertInnerMargin;
447 if (flavors_ & SIGN_IN_PROMO) {
448 height_of_signin_promo_ = LayoutSigninPromo(x, y);
449 y += height_of_signin_promo_;
450 y += kVertInnerMargin;
453 if (flavors_ & SHOW_KEYBINDING) {
454 gfx::Size sz = manage_shortcut_->GetPreferredSize();
455 manage_shortcut_->SetBounds(width() - 2 * kHorizOuterMargin - sz.width(),
457 sz.width(),
458 sz.height());
459 y += manage_shortcut_->height();
460 y += kVertInnerMargin;
463 gfx::Size sz;
464 x += kRightColumnWidth + 2 * views::kPanelHorizMargin + kHorizOuterMargin -
465 close_button_->GetPreferredSize().width();
466 y = kVertOuterMargin;
467 sz = close_button_->GetPreferredSize();
468 // x-1 & y-1 is just slop to get the close button visually aligned with the
469 // title text and bubble arrow.
470 close_button_->SetBounds(x - 1, y - 1, sz.width(), sz.height());
473 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
474 for (ScopedVector<gfx::RenderText>::const_iterator it =
475 sign_in_promo_lines_.begin();
476 it != sign_in_promo_lines_.end(); ++it)
477 (*it)->Draw(canvas);
479 views::View::OnPaint(canvas);
482 // The browser we're associated with.
483 Browser* browser_;
485 // The id of the extension just installed.
486 const std::string extension_id_;
488 // The ExtensionInstalledBubbleView showing us.
489 ExtensionInstalledBubbleView* bubble_;
491 // The string that contains the link text at the beginning of the sign-in
492 // promo text.
493 base::string16 signin_promo_link_text_;
494 // The remaining text of the sign-in promo text.
495 base::string16 signin_promo_text_;
497 // A vector of RenderText objects representing the full sign-in promo
498 // paragraph as layed out within the bubble, but has the text of the link
499 // whited out so the link can be drawn in its place.
500 ScopedVector<gfx::RenderText> sign_in_promo_lines_;
502 // The type of the bubble to show (Browser Action, Omnibox keyword, etc).
503 ExtensionInstalledBubble::BubbleType type_;
505 // A bitmask containing the various flavors of bubble sections to show.
506 int flavors_;
508 // The height, in pixels, of the sign-in promo.
509 size_t height_of_signin_promo_;
511 views::ImageView* icon_;
512 views::Label* heading_;
513 views::Label* how_to_use_;
514 views::Link* sign_in_link_;
515 views::Label* manage_;
516 views::Link* manage_shortcut_;
517 views::ImageButton* close_button_;
519 DISALLOW_COPY_AND_ASSIGN(InstalledBubbleContent);
522 void ExtensionInstalledBubbleView::Show(const Extension* extension,
523 Browser *browser,
524 const SkBitmap& icon) {
525 new ExtensionInstalledBubbleView(extension, browser, icon);
528 ExtensionInstalledBubbleView::ExtensionInstalledBubbleView(
529 const Extension* extension, Browser *browser, const SkBitmap& icon)
530 : bubble_(this, extension, browser, icon) {
533 ExtensionInstalledBubbleView::~ExtensionInstalledBubbleView() {}
535 bool ExtensionInstalledBubbleView::MaybeShowNow() {
536 BrowserView* browser_view =
537 BrowserView::GetBrowserViewForBrowser(bubble_.browser());
538 extensions::ExtensionActionManager* extension_action_manager =
539 extensions::ExtensionActionManager::Get(bubble_.browser()->profile());
541 views::View* reference_view = NULL;
542 if (bubble_.type() == bubble_.BROWSER_ACTION) {
543 BrowserActionsContainer* container =
544 browser_view->GetToolbarView()->browser_actions();
545 if (container->animating())
546 return false;
548 reference_view = container->GetBrowserActionView(
549 extension_action_manager->GetBrowserAction(*bubble_.extension()));
550 // If the view is not visible then it is in the chevron, so point the
551 // install bubble to the chevron instead. If this is an incognito window,
552 // both could be invisible.
553 if (!reference_view || !reference_view->visible()) {
554 reference_view = container->chevron();
555 if (!reference_view || !reference_view->visible())
556 reference_view = NULL; // fall back to app menu below.
558 } else if (bubble_.type() == bubble_.PAGE_ACTION) {
559 LocationBarView* location_bar_view = browser_view->GetLocationBarView();
560 ExtensionAction* page_action =
561 extension_action_manager->GetPageAction(*bubble_.extension());
562 location_bar_view->SetPreviewEnabledPageAction(page_action,
563 true); // preview_enabled
564 reference_view = location_bar_view->GetPageActionView(page_action);
565 DCHECK(reference_view);
566 } else if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) {
567 LocationBarView* location_bar_view = browser_view->GetLocationBarView();
568 reference_view = location_bar_view;
569 DCHECK(reference_view);
572 // Default case.
573 if (reference_view == NULL)
574 reference_view = browser_view->GetToolbarView()->app_menu();
575 SetAnchorView(reference_view);
577 set_arrow(bubble_.type() == bubble_.OMNIBOX_KEYWORD ?
578 views::BubbleBorder::TOP_LEFT :
579 views::BubbleBorder::TOP_RIGHT);
580 SetLayoutManager(new views::FillLayout());
581 AddChildView(new InstalledBubbleContent(
582 bubble_.browser(), bubble_.extension(), bubble_.type(),
583 &bubble_.icon(), this));
585 views::BubbleDelegateView::CreateBubble(this);
587 // The bubble widget is now the parent and owner of |this| and takes care of
588 // deletion when the bubble or browser go away.
589 bubble_.IgnoreBrowserClosing();
591 StartFade(true);
592 return true;
595 gfx::Rect ExtensionInstalledBubbleView::GetAnchorRect() {
596 // For omnibox keyword bubbles, move the arrow to point to the left edge
597 // of the omnibox, just to the right of the icon.
598 if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) {
599 LocationBarView* location_bar_view =
600 BrowserView::GetBrowserViewForBrowser(bubble_.browser())->
601 GetLocationBarView();
602 return gfx::Rect(location_bar_view->GetOmniboxViewOrigin(),
603 gfx::Size(0, location_bar_view->omnibox_view()->height()));
605 return views::BubbleDelegateView::GetAnchorRect();
608 void ExtensionInstalledBubbleView::WindowClosing() {
609 if (bubble_.extension() && bubble_.type() == bubble_.PAGE_ACTION) {
610 BrowserView* browser_view =
611 BrowserView::GetBrowserViewForBrowser(bubble_.browser());
612 browser_view->GetLocationBarView()->SetPreviewEnabledPageAction(
613 extensions::ExtensionActionManager::Get(bubble_.browser()->profile())->
614 GetPageAction(*bubble_.extension()),
615 false); // preview_enabled