Upstream version 5.34.104.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/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"
47
48 using extensions::Extension;
49
50 namespace {
51
52 const int kIconSize = 43;
53
54 const int kRightColumnWidth = 285;
55
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;
62
63 // Interior vertical margin is 8px smaller than standard
64 const int kVertInnerMargin = views::kPanelVertMargin - 8;
65
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;
69
70 }  // namespace
71
72 namespace chrome {
73
74 void ShowExtensionInstalledBubble(const Extension* extension,
75                                   Browser* browser,
76                                   const SkBitmap& icon) {
77   ExtensionInstalledBubbleView::Show(extension, browser, icon);
78 }
79
80 }  // namespace chrome
81
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:
106     //
107     // -------------------------
108     // |      | Heading    [X] |
109     // | Icon | Info           |
110     // |      | Extra info     |
111     // -------------------------
112     //
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.
120
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.
125
126     if (extensions::sync_helper::IsSyncableExtension(extension) &&
127         SyncPromoUI::ShouldShowSyncPromo(browser->profile()))
128       flavors_ |= SIGN_IN_PROMO;
129
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;
143         }
144         break;
145       }
146       case ExtensionInstalledBubble::OMNIBOX_KEYWORD: {
147         flavors_ |= HOW_TO_USE | HOW_TO_MANAGE;
148         key = base::UTF8ToUTF16(extensions::OmniboxInfo::GetKeyword(extension));
149         break;
150       }
151       case ExtensionInstalledBubble::GENERIC: {
152         break;
153       }
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;
159       }
160     }
161
162     ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
163     const gfx::FontList& font_list =
164         rb.GetFontList(ui::ResourceBundle::BaseFont);
165
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_);
175
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_);
185
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_);
192     }
193
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_);
199     }
200
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_);
212     }
213
214     if (flavors_ & SIGN_IN_PROMO) {
215       signin_promo_text_ =
216           l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_SIGNIN_PROMO);
217
218       signin_promo_link_text_ =
219           l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_SIGNIN_PROMO_LINK);
220       sign_in_link_ = new views::Link(signin_promo_link_text_);
221       sign_in_link_->SetFontList(font_list);
222       sign_in_link_->set_listener(this);
223       AddChildView(sign_in_link_);
224     }
225
226     // Add the Close button (for all flavors).
227     close_button_ = new views::ImageButton(this);
228     close_button_->SetImage(views::CustomButton::STATE_NORMAL,
229         rb.GetImageSkiaNamed(IDR_CLOSE_2));
230     close_button_->SetImage(views::CustomButton::STATE_HOVERED,
231         rb.GetImageSkiaNamed(IDR_CLOSE_2_H));
232     close_button_->SetImage(views::CustomButton::STATE_PRESSED,
233         rb.GetImageSkiaNamed(IDR_CLOSE_2_P));
234     AddChildView(close_button_);
235   }
236
237   virtual void ButtonPressed(views::Button* sender,
238                              const ui::Event& event) OVERRIDE {
239     if (sender == close_button_)
240       bubble_->StartFade(false);
241     else
242       NOTREACHED() << "Unknown view";
243   }
244
245   // Implements the views::LinkListener interface.
246   virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE {
247     GetWidget()->Close();
248     std::string configure_url;
249     if (source == manage_shortcut_) {
250       configure_url = chrome::kChromeUIExtensionsURL;
251       configure_url += chrome::kExtensionConfigureCommandsSubPage;
252     } else if (source == sign_in_link_) {
253       configure_url = signin::GetPromoURL(
254           signin::SOURCE_EXTENSION_INSTALL_BUBBLE, false).spec();
255     } else {
256       NOTREACHED();
257       return;
258     }
259     chrome::NavigateParams params(
260         chrome::GetSingletonTabNavigateParams(
261             browser_, GURL(configure_url.c_str())));
262     chrome::Navigate(&params);
263   }
264
265  private:
266    enum Flavors {
267      NONE            = 0,
268      HOW_TO_USE      = 1 << 0,
269      HOW_TO_MANAGE   = 1 << 1,
270      SHOW_KEYBINDING = 1 << 2,
271      SIGN_IN_PROMO   = 1 << 3,
272    };
273
274   bool GetKeybinding(extensions::Command* command) {
275     extensions::CommandService* command_service =
276         extensions::CommandService::Get(browser_->profile());
277     if (type_ == ExtensionInstalledBubble::BROWSER_ACTION) {
278       return command_service->GetBrowserActionCommand(
279           extension_id_,
280           extensions::CommandService::ACTIVE_ONLY,
281           command,
282           NULL);
283     } else if (type_ == ExtensionInstalledBubble::PAGE_ACTION) {
284       return command_service->GetPageActionCommand(
285           extension_id_,
286           extensions::CommandService::ACTIVE_ONLY,
287           command,
288           NULL);
289     } else {
290       return false;
291     }
292   }
293
294   base::string16 GetHowToUseDescription(const base::string16& key) {
295     switch (type_) {
296       case ExtensionInstalledBubble::BROWSER_ACTION:
297         if (!key.empty()) {
298           return l10n_util::GetStringFUTF16(
299               IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO_WITH_SHORTCUT, key);
300         } else {
301           return l10n_util::GetStringUTF16(
302               IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO);
303         }
304         break;
305       case ExtensionInstalledBubble::PAGE_ACTION:
306         if (!key.empty()) {
307           return l10n_util::GetStringFUTF16(
308               IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO_WITH_SHORTCUT, key);
309         } else {
310           return l10n_util::GetStringUTF16(
311               IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO);
312         }
313         break;
314       case ExtensionInstalledBubble::OMNIBOX_KEYWORD:
315         return l10n_util::GetStringFUTF16(
316             IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO, key);
317         break;
318       default:
319         NOTREACHED();
320         break;
321     }
322     return base::string16();
323   }
324
325   // Layout the signin promo at coordinates |offset_x| and |offset_y|. Returns
326   // the height (in pixels) of the promo UI.
327   int LayoutSigninPromo(int offset_x, int offset_y) {
328     sign_in_promo_lines_.clear();
329     int height = 0;
330     gfx::Rect contents_area = GetContentsBounds();
331     if (contents_area.IsEmpty())
332       return height;
333     contents_area.set_width(kRightColumnWidth);
334
335     base::string16 full_text = signin_promo_link_text_ + signin_promo_text_;
336
337     // The link is the first item in the text.
338     const gfx::Size link_size = sign_in_link_->GetPreferredSize();
339     sign_in_link_->SetBounds(
340         offset_x, offset_y, link_size.width(), link_size.height());
341
342     // Word-wrap the full label text.
343     const gfx::FontList font_list;
344     std::vector<base::string16> lines;
345     gfx::ElideRectangleText(full_text, font_list, contents_area.width(),
346                             contents_area.height(), gfx::ELIDE_LONG_WORDS,
347                             &lines);
348
349     gfx::Point position = gfx::Point(
350         contents_area.origin().x() + offset_x,
351         contents_area.origin().y() + offset_y + 1);
352     if (base::i18n::IsRTL()) {
353       position -= gfx::Vector2d(
354           2 * views::kPanelHorizMargin + kHorizOuterMargin, 0);
355     }
356
357     // Loop through the lines, creating a renderer for each.
358     for (std::vector<base::string16>::const_iterator it = lines.begin();
359          it != lines.end(); ++it) {
360       gfx::RenderText* line = gfx::RenderText::CreateInstance();
361       line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
362       line->SetText(*it);
363       const gfx::Size size(contents_area.width(),
364                            line->GetStringSize().height());
365       line->SetDisplayRect(gfx::Rect(position, size));
366       position.set_y(position.y() + size.height());
367       sign_in_promo_lines_.push_back(line);
368       height += size.height();
369     }
370
371     // The link is drawn separately; make it transparent here to only draw once.
372     // The link always leads other text and is assumed to fit on the first line.
373     sign_in_promo_lines_.front()->ApplyColor(SK_ColorTRANSPARENT,
374         gfx::Range(0, signin_promo_link_text_.size()));
375
376     return height;
377   }
378
379   virtual gfx::Size GetPreferredSize() OVERRIDE {
380     int width = kHorizOuterMargin;
381     width += kIconSize;
382     width += views::kPanelHorizMargin;
383     width += kRightColumnWidth;
384     width += 2 * views::kPanelHorizMargin;
385     width += kHorizOuterMargin;
386
387     int height = kVertOuterMargin;
388     height += heading_->GetHeightForWidth(kRightColumnWidth);
389     height += kVertInnerMargin;
390
391     if (flavors_ & HOW_TO_USE) {
392       height += how_to_use_->GetHeightForWidth(kRightColumnWidth);
393       height += kVertInnerMargin;
394     }
395
396     if (flavors_ & HOW_TO_MANAGE) {
397       height += manage_->GetHeightForWidth(kRightColumnWidth);
398       height += kVertInnerMargin;
399     }
400
401     if (flavors_ & SIGN_IN_PROMO && height_of_signin_promo_ > 0u) {
402       height += height_of_signin_promo_;
403       height += kVertInnerMargin;
404     }
405
406     if (flavors_ & SHOW_KEYBINDING) {
407       height += manage_shortcut_->GetHeightForWidth(kRightColumnWidth);
408       height += kVertInnerMargin;
409     }
410
411     return gfx::Size(width, std::max(height, kIconSize + 2 * kVertOuterMargin));
412   }
413
414   virtual void Layout() OVERRIDE {
415     int x = kHorizOuterMargin;
416     int y = kVertOuterMargin;
417
418     icon_->SetBounds(x, y, kIconSize, kIconSize);
419     x += kIconSize;
420     x += views::kPanelHorizMargin;
421
422     y += kRightcolumnVerticalShift;
423     heading_->SizeToFit(kRightColumnWidth);
424     heading_->SetX(x);
425     heading_->SetY(y);
426     y += heading_->height();
427     y += kVertInnerMargin;
428
429     if (flavors_ & HOW_TO_USE) {
430       how_to_use_->SizeToFit(kRightColumnWidth);
431       how_to_use_->SetX(x);
432       how_to_use_->SetY(y);
433       y += how_to_use_->height();
434       y += kVertInnerMargin;
435     }
436
437     if (flavors_ & HOW_TO_MANAGE) {
438       manage_->SizeToFit(kRightColumnWidth);
439       manage_->SetX(x);
440       manage_->SetY(y);
441       y += manage_->height();
442       y += kVertInnerMargin;
443     }
444
445     if (flavors_ & SIGN_IN_PROMO) {
446       height_of_signin_promo_ = LayoutSigninPromo(x, y);
447       y += height_of_signin_promo_;
448       y += kVertInnerMargin;
449     }
450
451     if (flavors_ & SHOW_KEYBINDING) {
452       gfx::Size sz = manage_shortcut_->GetPreferredSize();
453       manage_shortcut_->SetBounds(width() - 2 * kHorizOuterMargin - sz.width(),
454                                   y,
455                                   sz.width(),
456                                   sz.height());
457       y += manage_shortcut_->height();
458       y += kVertInnerMargin;
459     }
460
461     gfx::Size sz;
462     x += kRightColumnWidth + 2 * views::kPanelHorizMargin + kHorizOuterMargin -
463         close_button_->GetPreferredSize().width();
464     y = kVertOuterMargin;
465     sz = close_button_->GetPreferredSize();
466     // x-1 & y-1 is just slop to get the close button visually aligned with the
467     // title text and bubble arrow.
468     close_button_->SetBounds(x - 1, y - 1, sz.width(), sz.height());
469   }
470
471   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
472     for (ScopedVector<gfx::RenderText>::const_iterator it =
473              sign_in_promo_lines_.begin();
474          it != sign_in_promo_lines_.end(); ++it)
475       (*it)->Draw(canvas);
476
477     views::View::OnPaint(canvas);
478   }
479
480   // The browser we're associated with.
481   Browser* browser_;
482
483   // The id of the extension just installed.
484   const std::string extension_id_;
485
486   // The ExtensionInstalledBubbleView showing us.
487   ExtensionInstalledBubbleView* bubble_;
488
489   // The string that contains the link text at the beginning of the sign-in
490   // promo text.
491   base::string16 signin_promo_link_text_;
492   // The remaining text of the sign-in promo text.
493   base::string16 signin_promo_text_;
494
495   // A vector of RenderText objects representing the full sign-in promo
496   // paragraph as layed out within the bubble, but has the text of the link
497   // whited out so the link can be drawn in its place.
498   ScopedVector<gfx::RenderText> sign_in_promo_lines_;
499
500   // The type of the bubble to show (Browser Action, Omnibox keyword, etc).
501   ExtensionInstalledBubble::BubbleType type_;
502
503   // A bitmask containing the various flavors of bubble sections to show.
504   int flavors_;
505
506   // The height, in pixels, of the sign-in promo.
507   size_t height_of_signin_promo_;
508
509   views::ImageView* icon_;
510   views::Label* heading_;
511   views::Label* how_to_use_;
512   views::Link* sign_in_link_;
513   views::Label* manage_;
514   views::Link* manage_shortcut_;
515   views::ImageButton* close_button_;
516
517   DISALLOW_COPY_AND_ASSIGN(InstalledBubbleContent);
518 };
519
520 void ExtensionInstalledBubbleView::Show(const Extension* extension,
521                                     Browser *browser,
522                                     const SkBitmap& icon) {
523   new ExtensionInstalledBubbleView(extension, browser, icon);
524 }
525
526 ExtensionInstalledBubbleView::ExtensionInstalledBubbleView(
527     const Extension* extension, Browser *browser, const SkBitmap& icon)
528     : bubble_(this, extension, browser, icon) {
529 }
530
531 ExtensionInstalledBubbleView::~ExtensionInstalledBubbleView() {}
532
533 bool ExtensionInstalledBubbleView::MaybeShowNow() {
534   BrowserView* browser_view =
535       BrowserView::GetBrowserViewForBrowser(bubble_.browser());
536   extensions::ExtensionActionManager* extension_action_manager =
537       extensions::ExtensionActionManager::Get(bubble_.browser()->profile());
538
539   views::View* reference_view = NULL;
540   if (bubble_.type() == bubble_.BROWSER_ACTION) {
541     BrowserActionsContainer* container =
542         browser_view->GetToolbarView()->browser_actions();
543     if (container->animating())
544       return false;
545
546     reference_view = container->GetBrowserActionView(
547         extension_action_manager->GetBrowserAction(*bubble_.extension()));
548     // If the view is not visible then it is in the chevron, so point the
549     // install bubble to the chevron instead. If this is an incognito window,
550     // both could be invisible.
551     if (!reference_view || !reference_view->visible()) {
552       reference_view = container->chevron();
553       if (!reference_view || !reference_view->visible())
554         reference_view = NULL;  // fall back to app menu below.
555     }
556   } else if (bubble_.type() == bubble_.PAGE_ACTION) {
557     LocationBarView* location_bar_view = browser_view->GetLocationBarView();
558     ExtensionAction* page_action =
559         extension_action_manager->GetPageAction(*bubble_.extension());
560     location_bar_view->SetPreviewEnabledPageAction(page_action,
561                                                    true);  // preview_enabled
562     reference_view = location_bar_view->GetPageActionView(page_action);
563     DCHECK(reference_view);
564   } else if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) {
565     LocationBarView* location_bar_view = browser_view->GetLocationBarView();
566     reference_view = location_bar_view;
567     DCHECK(reference_view);
568   }
569
570   // Default case.
571   if (reference_view == NULL)
572     reference_view = browser_view->GetToolbarView()->app_menu();
573   SetAnchorView(reference_view);
574
575   set_arrow(bubble_.type() == bubble_.OMNIBOX_KEYWORD ?
576             views::BubbleBorder::TOP_LEFT :
577             views::BubbleBorder::TOP_RIGHT);
578   SetLayoutManager(new views::FillLayout());
579   AddChildView(new InstalledBubbleContent(
580       bubble_.browser(), bubble_.extension(), bubble_.type(),
581       &bubble_.icon(), this));
582
583   views::BubbleDelegateView::CreateBubble(this);
584
585   // The bubble widget is now the parent and owner of |this| and takes care of
586   // deletion when the bubble or browser go away.
587   bubble_.IgnoreBrowserClosing();
588
589   StartFade(true);
590   return true;
591 }
592
593 gfx::Rect ExtensionInstalledBubbleView::GetAnchorRect() {
594   // For omnibox keyword bubbles, move the arrow to point to the left edge
595   // of the omnibox, just to the right of the icon.
596   if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) {
597     LocationBarView* location_bar_view =
598         BrowserView::GetBrowserViewForBrowser(bubble_.browser())->
599         GetLocationBarView();
600     return gfx::Rect(location_bar_view->GetOmniboxViewOrigin(),
601         gfx::Size(0, location_bar_view->omnibox_view()->height()));
602   }
603   return views::BubbleDelegateView::GetAnchorRect();
604 }
605
606 void ExtensionInstalledBubbleView::WindowClosing() {
607   if (bubble_.extension() && bubble_.type() == bubble_.PAGE_ACTION) {
608     BrowserView* browser_view =
609         BrowserView::GetBrowserViewForBrowser(bubble_.browser());
610     browser_view->GetLocationBarView()->SetPreviewEnabledPageAction(
611         extensions::ExtensionActionManager::Get(bubble_.browser()->profile())->
612         GetPageAction(*bubble_.extension()),
613         false);  // preview_enabled
614   }
615 }