Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(85)

Side by Side Diff: chrome/browser/ui/views/extensions/extension_installed_bubble.cc

Issue 23461013: Refactor out duplicate code from GTK/Views extension installed bubble. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix gtk Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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.h"
6
7 #include <algorithm>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/i18n/rtl.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/api/commands/command_service.h"
16 #include "chrome/browser/extensions/extension_action.h"
17 #include "chrome/browser/extensions/extension_action_manager.h"
18 #include "chrome/browser/extensions/extension_install_ui.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/signin/signin_promo.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/browser/ui/singleton_tabs.h"
24 #include "chrome/browser/ui/sync/sync_promo_ui.h"
25 #include "chrome/browser/ui/views/browser_action_view.h"
26 #include "chrome/browser/ui/views/browser_actions_container.h"
27 #include "chrome/browser/ui/views/frame/browser_view.h"
28 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
29 #include "chrome/browser/ui/views/tabs/tab_strip.h"
30 #include "chrome/browser/ui/views/toolbar_view.h"
31 #include "chrome/common/extensions/api/extension_action/action_info.h"
32 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
33 #include "chrome/common/extensions/extension.h"
34 #include "chrome/common/extensions/sync_helper.h"
35 #include "chrome/common/url_constants.h"
36 #include "content/public/browser/notification_details.h"
37 #include "content/public/browser/notification_source.h"
38 #include "grit/chromium_strings.h"
39 #include "grit/generated_resources.h"
40 #include "grit/ui_resources.h"
41 #include "ui/base/l10n/l10n_util.h"
42 #include "ui/base/resource/resource_bundle.h"
43 #include "ui/base/text/text_elider.h"
44 #include "ui/gfx/render_text.h"
45 #include "ui/views/controls/button/image_button.h"
46 #include "ui/views/controls/image_view.h"
47 #include "ui/views/controls/label.h"
48 #include "ui/views/controls/link.h"
49 #include "ui/views/controls/link_listener.h"
50 #include "ui/views/layout/fill_layout.h"
51 #include "ui/views/layout/layout_constants.h"
52
53 using extensions::Extension;
54
55 namespace {
56
57 const int kIconSize = 43;
58
59 const int kRightColumnWidth = 285;
60
61 // The Bubble uses a BubbleBorder which adds about 6 pixels of whitespace
62 // around the content view. We compensate by reducing our outer borders by this
63 // amount + 4px.
64 const int kOuterMarginInset = 10;
65 const int kHorizOuterMargin = views::kPanelHorizMargin - kOuterMarginInset;
66 const int kVertOuterMargin = views::kPanelVertMargin - kOuterMarginInset;
67
68 // Interior vertical margin is 8px smaller than standard
69 const int kVertInnerMargin = views::kPanelVertMargin - 8;
70
71 // We want to shift the right column (which contains the header and text) up
72 // 4px to align with icon.
73 const int kRightcolumnVerticalShift = -4;
74
75 // How long to wait for browser action animations to complete before retrying.
76 const int kAnimationWaitTime = 50;
77
78 // How often we retry when waiting for browser action animation to end.
79 const int kAnimationWaitMaxRetry = 10;
80
81 } // namespace
82
83 namespace chrome {
84
85 void ShowExtensionInstalledBubble(const Extension* extension,
86 Browser* browser,
87 const SkBitmap& icon) {
88 ExtensionInstalledBubble::Show(extension, browser, icon);
89 }
90
91 } // namespace chrome
92
93 // InstalledBubbleContent is the content view which is placed in the
94 // ExtensionInstalledBubble. It displays the install icon and explanatory
95 // text about the installed extension.
96 class InstalledBubbleContent : public views::View,
97 public views::ButtonListener,
98 public views::LinkListener {
99 public:
100 InstalledBubbleContent(Browser* browser,
101 const Extension* extension,
102 ExtensionInstalledBubble::BubbleType type,
103 SkBitmap* icon,
104 ExtensionInstalledBubble* bubble)
105 : browser_(browser),
106 extension_id_(extension->id()),
107 bubble_(bubble),
108 type_(type),
109 flavors_(NONE),
110 height_of_signin_promo_(0u),
111 how_to_use_(NULL),
112 sign_in_link_(NULL),
113 manage_(NULL),
114 manage_shortcut_(NULL) {
115 // The Extension Installed bubble takes on various forms, depending on the
116 // type of extension installed. In general, though, they are all similar:
117 //
118 // -------------------------
119 // | | Heading [X] |
120 // | Icon | Info |
121 // | | Extra info |
122 // -------------------------
123 //
124 // Icon and Heading are always shown (as well as the close button).
125 // Info is shown for browser actions, page actions and Omnibox keyword
126 // extensions and might list keyboard shorcut for the former two types.
127 // Extra info is...
128 // ... for other types, either a description of how to manage the extension
129 // or a link to configure the keybinding shortcut (if one exists).
130 // Extra info can include a promo for signing into sync.
131
132 // First figure out the keybinding situation.
133 extensions::Command command;
134 bool has_keybinding = GetKeybinding(&command);
135 string16 key; // Keyboard shortcut or keyword to display in the bubble.
136
137 if (extensions::sync_helper::IsSyncableExtension(extension) &&
138 SyncPromoUI::ShouldShowSyncPromo(browser->profile()))
139 flavors_ |= SIGN_IN_PROMO;
140
141 // Determine the bubble flavor we want, based on the extension type.
142 switch (type_) {
143 case ExtensionInstalledBubble::BROWSER_ACTION:
144 case ExtensionInstalledBubble::PAGE_ACTION: {
145 flavors_ |= HOW_TO_USE;
146 if (has_keybinding) {
147 flavors_ |= SHOW_KEYBINDING;
148 key = command.accelerator().GetShortcutText();
149 } else {
150 // The How-To-Use text makes the bubble seem a little crowded when the
151 // extension has a keybinding, so the How-To-Manage text is not shown
152 // in those cases.
153 flavors_ |= HOW_TO_MANAGE;
154 }
155 break;
156 }
157 case ExtensionInstalledBubble::OMNIBOX_KEYWORD: {
158 flavors_ |= HOW_TO_USE | HOW_TO_MANAGE;
159 key = UTF8ToUTF16(extensions::OmniboxInfo::GetKeyword(extension));
160 break;
161 }
162 case ExtensionInstalledBubble::GENERIC: {
163 break;
164 }
165 default: {
166 // When adding a new bubble type, the flavor needs to be set.
167 COMPILE_ASSERT(ExtensionInstalledBubble::GENERIC == 3,
168 kBubbleTypeEnumHasChangedButNotThisSwitchStatement);
169 break;
170 }
171 }
172
173 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
174 const gfx::Font& font = rb.GetFont(ui::ResourceBundle::BaseFont);
175
176 // Add the icon (for all flavors).
177 // Scale down to 43x43, but allow smaller icons (don't scale up).
178 gfx::Size size(icon->width(), icon->height());
179 if (size.width() > kIconSize || size.height() > kIconSize)
180 size = gfx::Size(kIconSize, kIconSize);
181 icon_ = new views::ImageView();
182 icon_->SetImageSize(size);
183 icon_->SetImage(gfx::ImageSkia::CreateFrom1xBitmap(*icon));
184 AddChildView(icon_);
185
186 // Add the heading (for all flavors).
187 string16 extension_name = UTF8ToUTF16(extension->name());
188 base::i18n::AdjustStringForLocaleDirection(&extension_name);
189 heading_ = new views::Label(l10n_util::GetStringFUTF16(
190 IDS_EXTENSION_INSTALLED_HEADING, extension_name));
191 heading_->SetFont(rb.GetFont(ui::ResourceBundle::MediumFont));
192 heading_->SetMultiLine(true);
193 heading_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
194 AddChildView(heading_);
195
196 if (flavors_ & HOW_TO_USE) {
197 how_to_use_ = new views::Label(GetHowToUseDescription(key));
198 how_to_use_->SetFont(font);
199 how_to_use_->SetMultiLine(true);
200 how_to_use_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
201 AddChildView(how_to_use_);
202 }
203
204 if (flavors_ & SHOW_KEYBINDING) {
205 manage_shortcut_ = new views::Link(
206 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_MANAGE_SHORTCUTS));
207 manage_shortcut_->set_listener(this);
208 AddChildView(manage_shortcut_);
209 }
210
211 if (flavors_ & HOW_TO_MANAGE) {
212 manage_ = new views::Label(l10n_util::GetStringUTF16(
213 #if defined(OS_CHROMEOS)
214 IDS_EXTENSION_INSTALLED_MANAGE_INFO_CHROMEOS));
215 #else
216 IDS_EXTENSION_INSTALLED_MANAGE_INFO));
217 #endif
218 manage_->SetFont(font);
219 manage_->SetMultiLine(true);
220 manage_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
221 AddChildView(manage_);
222 }
223
224 if (flavors_ & SIGN_IN_PROMO) {
225 signin_promo_text_ =
226 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALLED_SIGNIN_PROMO);
227
228 signin_promo_link_text_ =
229 l10n_util::GetStringFUTF16(
230 IDS_EXTENSION_INSTALLED_SIGNIN_PROMO_LINK,
231 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
232 sign_in_link_ = new views::Link(signin_promo_link_text_);
233 sign_in_link_->SetFont(font);
234 sign_in_link_->set_listener(this);
235 AddChildView(sign_in_link_);
236 }
237
238 // Add the Close button (for all flavors).
239 close_button_ = new views::ImageButton(this);
240 close_button_->SetImage(views::CustomButton::STATE_NORMAL,
241 rb.GetImageSkiaNamed(IDR_CLOSE_2));
242 close_button_->SetImage(views::CustomButton::STATE_HOVERED,
243 rb.GetImageSkiaNamed(IDR_CLOSE_2_H));
244 close_button_->SetImage(views::CustomButton::STATE_PRESSED,
245 rb.GetImageSkiaNamed(IDR_CLOSE_2_P));
246 AddChildView(close_button_);
247 }
248
249 virtual void ButtonPressed(views::Button* sender,
250 const ui::Event& event) OVERRIDE {
251 if (sender == close_button_)
252 bubble_->StartFade(false);
253 else
254 NOTREACHED() << "Unknown view";
255 }
256
257 // Implements the views::LinkListener interface.
258 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE {
259 GetWidget()->Close();
260 std::string configure_url;
261 if (source == manage_shortcut_) {
262 configure_url = chrome::kChromeUIExtensionsURL;
263 configure_url += chrome::kExtensionConfigureCommandsSubPage;
264 } else if (source == sign_in_link_) {
265 configure_url = signin::GetPromoURL(
266 signin::SOURCE_EXTENSION_INSTALL_BUBBLE, false).spec();
267 } else {
268 NOTREACHED();
269 return;
270 }
271 chrome::NavigateParams params(
272 chrome::GetSingletonTabNavigateParams(
273 browser_, GURL(configure_url.c_str())));
274 chrome::Navigate(&params);
275 }
276
277 private:
278 enum Flavors {
279 NONE = 0,
280 HOW_TO_USE = 1 << 0,
281 HOW_TO_MANAGE = 1 << 1,
282 SHOW_KEYBINDING = 1 << 2,
283 SIGN_IN_PROMO = 1 << 3,
284 };
285
286 bool GetKeybinding(extensions::Command* command) {
287 extensions::CommandService* command_service =
288 extensions::CommandService::Get(browser_->profile());
289 if (type_ == ExtensionInstalledBubble::BROWSER_ACTION) {
290 return command_service->GetBrowserActionCommand(
291 extension_id_,
292 extensions::CommandService::ACTIVE_ONLY,
293 command,
294 NULL);
295 } else if (type_ == ExtensionInstalledBubble::PAGE_ACTION) {
296 return command_service->GetPageActionCommand(
297 extension_id_,
298 extensions::CommandService::ACTIVE_ONLY,
299 command,
300 NULL);
301 } else {
302 return false;
303 }
304 }
305
306 string16 GetHowToUseDescription(const string16& key) {
307 switch (type_) {
308 case ExtensionInstalledBubble::BROWSER_ACTION:
309 if (!key.empty()) {
310 return l10n_util::GetStringFUTF16(
311 IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO_WITH_SHORTCUT, key);
312 } else {
313 return l10n_util::GetStringUTF16(
314 IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO);
315 }
316 break;
317 case ExtensionInstalledBubble::PAGE_ACTION:
318 if (!key.empty()) {
319 return l10n_util::GetStringFUTF16(
320 IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO_WITH_SHORTCUT, key);
321 } else {
322 return l10n_util::GetStringUTF16(
323 IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO);
324 }
325 break;
326 case ExtensionInstalledBubble::OMNIBOX_KEYWORD:
327 return l10n_util::GetStringFUTF16(
328 IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO, key);
329 break;
330 default:
331 NOTREACHED();
332 break;
333 }
334 return string16();
335 }
336
337 // Layout the signin promo at coordinates |offset_x| and |offset_y|. Returns
338 // the height (in pixels) of the promo UI.
339 int LayoutSigninPromo(int offset_x, int offset_y) {
340 sign_in_promo_lines_.clear();
341 int height = 0;
342 gfx::Rect contents_area = GetContentsBounds();
343 if (contents_area.IsEmpty())
344 return height;
345 contents_area.set_width(kRightColumnWidth);
346
347 string16 full_text = signin_promo_link_text_ + signin_promo_text_;
348
349 // The link is the first item in the text.
350 const gfx::Size link_size = sign_in_link_->GetPreferredSize();
351 sign_in_link_->SetBounds(
352 offset_x, offset_y, link_size.width(), link_size.height());
353
354 // Word-wrap the full label text.
355 const gfx::Font font;
356 std::vector<string16> lines;
357 ui::ElideRectangleText(full_text, font, contents_area.width(),
358 contents_area.height(), ui::ELIDE_LONG_WORDS,
359 &lines);
360
361 gfx::Point position = gfx::Point(
362 contents_area.origin().x() + offset_x,
363 contents_area.origin().y() + offset_y + 1);
364 if (base::i18n::IsRTL()) {
365 position -= gfx::Vector2d(
366 2 * views::kPanelHorizMargin + kHorizOuterMargin, 0);
367 }
368
369 // Loop through the lines, creating a renderer for each.
370 for (std::vector<string16>::const_iterator it = lines.begin();
371 it != lines.end(); ++it) {
372 gfx::RenderText* line = gfx::RenderText::CreateInstance();
373 line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
374 line->SetText(*it);
375 const gfx::Size size(contents_area.width(),
376 line->GetStringSize().height());
377 line->SetDisplayRect(gfx::Rect(position, size));
378 position.set_y(position.y() + size.height());
379 sign_in_promo_lines_.push_back(line);
380 height += size.height();
381 }
382
383 // The link is drawn separately; make it transparent here to only draw once.
384 // The link always leads other text and is assumed to fit on the first line.
385 sign_in_promo_lines_.front()->ApplyColor(SK_ColorTRANSPARENT,
386 ui::Range(0, signin_promo_link_text_.size()));
387
388 return height;
389 }
390
391 virtual gfx::Size GetPreferredSize() OVERRIDE {
392 int width = kHorizOuterMargin;
393 width += kIconSize;
394 width += views::kPanelHorizMargin;
395 width += kRightColumnWidth;
396 width += 2 * views::kPanelHorizMargin;
397 width += kHorizOuterMargin;
398
399 int height = kVertOuterMargin;
400 height += heading_->GetHeightForWidth(kRightColumnWidth);
401 height += kVertInnerMargin;
402
403 if (flavors_ & HOW_TO_USE) {
404 height += how_to_use_->GetHeightForWidth(kRightColumnWidth);
405 height += kVertInnerMargin;
406 }
407
408 if (flavors_ & HOW_TO_MANAGE) {
409 height += manage_->GetHeightForWidth(kRightColumnWidth);
410 height += kVertInnerMargin;
411 }
412
413 if (flavors_ & SIGN_IN_PROMO && height_of_signin_promo_ > 0u) {
414 height += height_of_signin_promo_;
415 height += kVertInnerMargin;
416 }
417
418 if (flavors_ & SHOW_KEYBINDING) {
419 height += manage_shortcut_->GetHeightForWidth(kRightColumnWidth);
420 height += kVertInnerMargin;
421 }
422
423 return gfx::Size(width, std::max(height, kIconSize + 2 * kVertOuterMargin));
424 }
425
426 virtual void Layout() OVERRIDE {
427 int x = kHorizOuterMargin;
428 int y = kVertOuterMargin;
429
430 icon_->SetBounds(x, y, kIconSize, kIconSize);
431 x += kIconSize;
432 x += views::kPanelHorizMargin;
433
434 y += kRightcolumnVerticalShift;
435 heading_->SizeToFit(kRightColumnWidth);
436 heading_->SetX(x);
437 heading_->SetY(y);
438 y += heading_->height();
439 y += kVertInnerMargin;
440
441 if (flavors_ & HOW_TO_USE) {
442 how_to_use_->SizeToFit(kRightColumnWidth);
443 how_to_use_->SetX(x);
444 how_to_use_->SetY(y);
445 y += how_to_use_->height();
446 y += kVertInnerMargin;
447 }
448
449 if (flavors_ & HOW_TO_MANAGE) {
450 manage_->SizeToFit(kRightColumnWidth);
451 manage_->SetX(x);
452 manage_->SetY(y);
453 y += manage_->height();
454 y += kVertInnerMargin;
455 }
456
457 if (flavors_ & SIGN_IN_PROMO) {
458 height_of_signin_promo_ = LayoutSigninPromo(x, y);
459 y += height_of_signin_promo_;
460 y += kVertInnerMargin;
461 }
462
463 if (flavors_ & SHOW_KEYBINDING) {
464 gfx::Size sz = manage_shortcut_->GetPreferredSize();
465 manage_shortcut_->SetBounds(width() - 2 * kHorizOuterMargin - sz.width(),
466 y,
467 sz.width(),
468 sz.height());
469 y += manage_shortcut_->height();
470 y += kVertInnerMargin;
471 }
472
473 gfx::Size sz;
474 x += kRightColumnWidth + 2 * views::kPanelHorizMargin + kHorizOuterMargin -
475 close_button_->GetPreferredSize().width();
476 y = kVertOuterMargin;
477 sz = close_button_->GetPreferredSize();
478 // x-1 & y-1 is just slop to get the close button visually aligned with the
479 // title text and bubble arrow.
480 close_button_->SetBounds(x - 1, y - 1, sz.width(), sz.height());
481 }
482
483 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
484 for (ScopedVector<gfx::RenderText>::const_iterator it =
485 sign_in_promo_lines_.begin();
486 it != sign_in_promo_lines_.end(); ++it)
487 (*it)->Draw(canvas);
488
489 views::View::OnPaint(canvas);
490 }
491
492 // The browser we're associated with.
493 Browser* browser_;
494
495 // The id of the extension just installed.
496 const std::string extension_id_;
497
498 // The ExtensionInstalledBubble showing us.
499 ExtensionInstalledBubble* bubble_;
500
501 // The string that contains the link text at the beginning of the sign-in
502 // promo text.
503 string16 signin_promo_link_text_;
504 // The remaining text of the sign-in promo text.
505 string16 signin_promo_text_;
506
507 // A vector of RenderText objects representing the full sign-in promo
508 // paragraph as layed out within the bubble, but has the text of the link
509 // whited out so the link can be drawn in its place.
510 ScopedVector<gfx::RenderText> sign_in_promo_lines_;
511
512 // The type of the bubble to show (Browser Action, Omnibox keyword, etc).
513 ExtensionInstalledBubble::BubbleType type_;
514
515 // A bitmask containing the various flavors of bubble sections to show.
516 int flavors_;
517
518 // The height, in pixels, of the sign-in promo.
519 size_t height_of_signin_promo_;
520
521 views::ImageView* icon_;
522 views::Label* heading_;
523 views::Label* how_to_use_;
524 views::Link* sign_in_link_;
525 views::Label* manage_;
526 views::Link* manage_shortcut_;
527 views::ImageButton* close_button_;
528
529 DISALLOW_COPY_AND_ASSIGN(InstalledBubbleContent);
530 };
531
532 void ExtensionInstalledBubble::Show(const Extension* extension,
533 Browser *browser,
534 const SkBitmap& icon) {
535 new ExtensionInstalledBubble(extension, browser, icon);
536 }
537
538 ExtensionInstalledBubble::ExtensionInstalledBubble(const Extension* extension,
539 Browser *browser,
540 const SkBitmap& icon)
541 : extension_(extension),
542 browser_(browser),
543 icon_(icon),
544 animation_wait_retries_(0),
545 weak_factory_(this) {
546 if (!extensions::OmniboxInfo::GetKeyword(extension).empty())
547 type_ = OMNIBOX_KEYWORD;
548 else if (extensions::ActionInfo::GetBrowserActionInfo(extension))
549 type_ = BROWSER_ACTION;
550 else if (extensions::ActionInfo::GetPageActionInfo(extension) &&
551 extensions::ActionInfo::IsVerboseInstallMessage(extension))
552 type_ = PAGE_ACTION;
553 else
554 type_ = GENERIC;
555
556 // |extension| has been initialized but not loaded at this point. We need
557 // to wait on showing the Bubble until not only the EXTENSION_LOADED gets
558 // fired, but all of the EXTENSION_LOADED Observers have run. Only then can we
559 // be sure that a BrowserAction or PageAction has had views created which we
560 // can inspect for the purpose of previewing of pointing to them.
561 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
562 content::Source<Profile>(browser->profile()));
563 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
564 content::Source<Profile>(browser->profile()));
565 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSING,
566 content::Source<Browser>(browser));
567 }
568
569 ExtensionInstalledBubble::~ExtensionInstalledBubble() {}
570
571 void ExtensionInstalledBubble::Observe(
572 int type,
573 const content::NotificationSource& source,
574 const content::NotificationDetails& details) {
575 if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
576 const Extension* extension =
577 content::Details<const Extension>(details).ptr();
578 if (extension == extension_) {
579 animation_wait_retries_ = 0;
580 // PostTask to ourself to allow all EXTENSION_LOADED Observers to run.
581 base::MessageLoopForUI::current()->PostTask(
582 FROM_HERE,
583 base::Bind(&ExtensionInstalledBubble::ShowInternal,
584 weak_factory_.GetWeakPtr()));
585 }
586 } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
587 const Extension* extension =
588 content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
589 if (extension == extension_) {
590 // Extension is going away, make sure ShowInternal won't be called.
591 weak_factory_.InvalidateWeakPtrs();
592 extension_ = NULL;
593 }
594 } else if (type == chrome::NOTIFICATION_BROWSER_CLOSING) {
595 delete this;
596 } else {
597 NOTREACHED() << L"Received unexpected notification";
598 }
599 }
600
601 void ExtensionInstalledBubble::ShowInternal() {
602 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
603 extensions::ExtensionActionManager* extension_action_manager =
604 extensions::ExtensionActionManager::Get(browser_->profile());
605
606 views::View* reference_view = NULL;
607 if (type_ == BROWSER_ACTION) {
608 BrowserActionsContainer* container =
609 browser_view->GetToolbarView()->browser_actions();
610 if (container->animating() &&
611 animation_wait_retries_++ < kAnimationWaitMaxRetry) {
612 // We don't know where the view will be until the container has stopped
613 // animating, so check back in a little while.
614 base::MessageLoopForUI::current()->PostDelayedTask(
615 FROM_HERE,
616 base::Bind(&ExtensionInstalledBubble::ShowInternal,
617 weak_factory_.GetWeakPtr()),
618 base::TimeDelta::FromMilliseconds(kAnimationWaitTime));
619 return;
620 }
621 reference_view = container->GetBrowserActionView(
622 extension_action_manager->GetBrowserAction(*extension_));
623 // If the view is not visible then it is in the chevron, so point the
624 // install bubble to the chevron instead. If this is an incognito window,
625 // both could be invisible.
626 if (!reference_view || !reference_view->visible()) {
627 reference_view = container->chevron();
628 if (!reference_view || !reference_view->visible())
629 reference_view = NULL; // fall back to app menu below.
630 }
631 } else if (type_ == PAGE_ACTION) {
632 LocationBarView* location_bar_view = browser_view->GetLocationBarView();
633 ExtensionAction* page_action =
634 extension_action_manager->GetPageAction(*extension_);
635 location_bar_view->SetPreviewEnabledPageAction(page_action,
636 true); // preview_enabled
637 reference_view = location_bar_view->GetPageActionView(page_action);
638 DCHECK(reference_view);
639 } else if (type_ == OMNIBOX_KEYWORD) {
640 LocationBarView* location_bar_view = browser_view->GetLocationBarView();
641 reference_view = location_bar_view;
642 DCHECK(reference_view);
643 }
644
645 // Default case.
646 if (reference_view == NULL)
647 reference_view = browser_view->GetToolbarView()->app_menu();
648 set_anchor_view(reference_view);
649
650 set_arrow(type_ == OMNIBOX_KEYWORD ? views::BubbleBorder::TOP_LEFT :
651 views::BubbleBorder::TOP_RIGHT);
652 SetLayoutManager(new views::FillLayout());
653 AddChildView(
654 new InstalledBubbleContent(browser_, extension_, type_, &icon_, this));
655
656 views::BubbleDelegateView::CreateBubble(this);
657
658 // The bubble widget is now the parent and owner of |this| and takes care of
659 // deletion when the bubble or browser go away.
660 registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_CLOSING,
661 content::Source<Browser>(browser_));
662
663 StartFade(true);
664 }
665
666 gfx::Rect ExtensionInstalledBubble::GetAnchorRect() {
667 // For omnibox keyword bubbles, move the arrow to point to the left edge
668 // of the omnibox, just to the right of the icon.
669 if (type_ == OMNIBOX_KEYWORD) {
670 LocationBarView* location_bar_view =
671 BrowserView::GetBrowserViewForBrowser(browser_)->GetLocationBarView();
672 return gfx::Rect(location_bar_view->GetLocationEntryOrigin(),
673 gfx::Size(0, location_bar_view->location_entry_view()->height()));
674 }
675 return views::BubbleDelegateView::GetAnchorRect();
676 }
677
678 void ExtensionInstalledBubble::WindowClosing() {
679 if (extension_ && type_ == PAGE_ACTION) {
680 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
681 browser_view->GetLocationBarView()->SetPreviewEnabledPageAction(
682 extensions::ExtensionActionManager::Get(browser_->profile())->
683 GetPageAction(*extension_),
684 false); // preview_enabled
685 }
686 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698