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