extension_message_bubble_view.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
1b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// Use of this source code is governed by a BSD-style license that can be
3b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// found in the LICENSE file.
4b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
5b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h"
6b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
7b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "base/strings/string_number_conversions.h"
8b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "base/strings/string_util.h"
9b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "base/strings/utf_string_conversions.h"
10b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/extensions/dev_mode_bubble_controller.h"
119fb16139d917ba32720e031d3c871987d418668fpbos@webrtc.org#include "chrome/browser/extensions/extension_action_manager.h"
12b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/extensions/extension_message_bubble_controller.h"
133f45c2e0ac4cb280f941efa3a3476895795e3dd6pbos@webrtc.org#include "chrome/browser/extensions/extension_service.h"
14b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/extensions/proxy_overridden_bubble_controller.h"
15a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org#include "chrome/browser/extensions/settings_api_bubble_controller.h"
169fb16139d917ba32720e031d3c871987d418668fpbos@webrtc.org#include "chrome/browser/extensions/settings_api_helpers.h"
179fb16139d917ba32720e031d3c871987d418668fpbos@webrtc.org#include "chrome/browser/extensions/suspicious_extension_bubble_controller.h"
18b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/profiles/profile.h"
19b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/ui/view_ids.h"
20b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/ui/views/frame/browser_view.h"
21b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
22b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
23b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
243f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org#include "chrome/grit/locale_settings.h"
25b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "extensions/browser/extension_prefs.h"
26b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "extensions/browser/extension_system.h"
27b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/accessibility/ax_view_state.h"
28b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/base/resource/resource_bundle.h"
29b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/views/controls/button/label_button.h"
30b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/views/controls/label.h"
31b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/views/controls/link.h"
32b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/views/layout/grid_layout.h"
33b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/views/view.h"
34b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#include "ui/views/widget/widget.h"
35b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
36b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgnamespace {
37b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
38a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.orgbase::LazyInstance<std::set<Profile*> > g_profiles_evaluated =
39a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org    LAZY_INSTANCE_INITIALIZER;
40a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org
41b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// Layout constants.
42a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.orgconst int kExtensionListPadding = 10;
43b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst int kInsetBottomRight = 13;
44b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst int kInsetLeft = 14;
45b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst int kInsetTop = 9;
46b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst int kHeadlineMessagePadding = 4;
47b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst int kHeadlineRowPadding = 10;
48b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst int kMessageBubblePadding = 11;
49b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
50b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// How many extensions to show in the bubble (max).
51b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst size_t kMaxExtensionsToShow = 7;
52b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
53b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// How long to wait until showing the bubble (in seconds).
54b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgconst int kBubbleAppearanceWaitTime = 5;
55b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
56b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}  // namespace
57b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
58b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgnamespace extensions {
59b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
60b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgExtensionMessageBubbleView::ExtensionMessageBubbleView(
61b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    views::View* anchor_view,
62b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    views::BubbleBorder::Arrow arrow_location,
63b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    scoped_ptr<extensions::ExtensionMessageBubbleController> controller)
64b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    : BubbleDelegateView(anchor_view, arrow_location),
65b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      weak_factory_(this),
66eb15100c9bdb4c97ffda2c05a934aab270795c27aluebs@webrtc.org      controller_(controller.Pass()),
673f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org      anchor_view_(anchor_view),
68b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      headline_(NULL),
69b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      learn_more_(NULL),
70b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      dismiss_button_(NULL),
71b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      link_clicked_(false),
72b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      action_taken_(false) {
73b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  DCHECK(anchor_view->GetWidget());
74b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  set_close_on_deactivate(controller_->CloseOnDeactivate());
75b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  set_close_on_esc(true);
76b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
77b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // Compensate for built-in vertical padding in the anchor view's image.
78b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
79b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
80b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
81b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::OnActionButtonClicked(
82b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    const base::Closure& callback) {
83b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  action_callback_ = callback;
84b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
85b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
86b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::OnDismissButtonClicked(
87b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    const base::Closure& callback) {
88e95dc25b14845cbf00ae363e88459c44e2341c47andrew@webrtc.org  dismiss_callback_ = callback;
89b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
90b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
91b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::OnLinkClicked(
92b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    const base::Closure& callback) {
93b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  link_callback_ = callback;
94b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
953f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org
96b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::Show() {
97b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // Not showing the bubble right away (during startup) has a few benefits:
98b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // We don't have to worry about focus being lost due to the Omnibox (or to
99b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // other things that want focus at startup). This allows Esc to work to close
100b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // the bubble and also solves the keyboard accessibility problem that comes
101b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // with focus being lost (we don't have a good generic mechanism of injecting
102b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // bubbles into the focus cycle). Another benefit of delaying the show is
103b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // that fade-in works (the fade-in isn't apparent if the the bubble appears at
104b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // startup).
1053f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org  base::MessageLoop::current()->PostDelayedTask(
106b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      FROM_HERE,
107b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      base::Bind(&ExtensionMessageBubbleView::ShowBubble,
108b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org                 weak_factory_.GetWeakPtr()),
109b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      base::TimeDelta::FromSeconds(kBubbleAppearanceWaitTime));
110b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
1113f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org
112b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::OnWidgetDestroying(views::Widget* widget) {
113b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // To catch Esc, we monitor destroy message. Unless the link has been clicked,
114b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // we assume Dismiss was the action taken.
115b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!link_clicked_ && !action_taken_)
116b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    dismiss_callback_.Run();
117b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
118b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
119b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org////////////////////////////////////////////////////////////////////////////////
120b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org// ExtensionMessageBubbleView - private.
121b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
122b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgExtensionMessageBubbleView::~ExtensionMessageBubbleView() {}
123b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
124b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::ShowBubble() {
125b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  GetWidget()->Show();
126b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
127b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
128b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::Init() {
129b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
130b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
131b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
132b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->SetInsets(kInsetTop, kInsetLeft,
133b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org                    kInsetBottomRight, kInsetBottomRight);
134b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  SetLayoutManager(layout);
135b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
136b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ExtensionMessageBubbleController::Delegate* delegate =
137b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      controller_->delegate();
138b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
139b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  const int headline_column_set_id = 0;
140b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  views::ColumnSet* top_columns = layout->AddColumnSet(headline_column_set_id);
141b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  top_columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
1423f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org                         0, views::GridLayout::USE_PREF, 0, 0);
1433f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org  top_columns->AddPaddingColumn(1, 0);
144b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->StartRow(0, headline_column_set_id);
145b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
146b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  headline_ = new views::Label(delegate->GetTitle(),
147b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org                               rb.GetFontList(ui::ResourceBundle::MediumFont));
148b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->AddView(headline_);
1493f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org
150b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->AddPaddingRow(0, kHeadlineRowPadding);
151b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
152b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  const int text_column_set_id = 1;
153b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  views::ColumnSet* upper_columns = layout->AddColumnSet(text_column_set_id);
154b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  upper_columns->AddColumn(
155b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      views::GridLayout::LEADING, views::GridLayout::LEADING,
156b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      0, views::GridLayout::USE_PREF, 0, 0);
157b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->StartRow(0, text_column_set_id);
158b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
159b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  views::Label* message = new views::Label();
160b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  message->SetMultiLine(true);
161b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  message->SetHorizontalAlignment(gfx::ALIGN_LEFT);
162b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  message->SetText(delegate->GetMessageBody(
163b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      anchor_view_->id() == VIEW_ID_BROWSER_ACTION));
164b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  message->SizeToFit(views::Widget::GetLocalizedContentsWidth(
165b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      IDS_EXTENSION_WIPEOUT_BUBBLE_WIDTH_CHARS));
166b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->AddView(message);
167b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
168b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (delegate->ShouldShowExtensionList()) {
169b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    const int extension_list_column_set_id = 2;
170b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    views::ColumnSet* middle_columns =
171b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org        layout->AddColumnSet(extension_list_column_set_id);
172b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    middle_columns->AddPaddingColumn(0, kExtensionListPadding);
173b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    middle_columns->AddColumn(
174b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org        views::GridLayout::LEADING, views::GridLayout::CENTER,
175b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org        0, views::GridLayout::USE_PREF, 0, 0);
176b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
177b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    layout->StartRowWithPadding(0, extension_list_column_set_id,
178b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org        0, kHeadlineMessagePadding);
179b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    views::Label* extensions = new views::Label();
180b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    extensions->SetMultiLine(true);
181b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    extensions->SetHorizontalAlignment(gfx::ALIGN_LEFT);
182b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
183b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    std::vector<base::string16> extension_list;
184b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    base::char16 bullet_point = 0x2022;
185b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
186b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    std::vector<base::string16> suspicious = controller_->GetExtensionList();
187b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    size_t i = 0;
188b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    for (; i < suspicious.size() && i < kMaxExtensionsToShow; ++i) {
189b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      // Add each extension with bullet point.
190b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      extension_list.push_back(
191b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org          bullet_point + base::ASCIIToUTF16(" ") + suspicious[i]);
192b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    }
193b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
194b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    if (i > kMaxExtensionsToShow) {
195b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      base::string16 difference = base::IntToString16(i - kMaxExtensionsToShow);
196b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      extension_list.push_back(bullet_point + base::ASCIIToUTF16(" ") +
197b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org          delegate->GetOverflowText(difference));
198b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    }
199b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
200a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org    extensions->SetText(JoinString(extension_list, base::ASCIIToUTF16("\n")));
201b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    extensions->SizeToFit(views::Widget::GetLocalizedContentsWidth(
202b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org        IDS_EXTENSION_WIPEOUT_BUBBLE_WIDTH_CHARS));
203b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    layout->AddView(extensions);
204b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
205b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
206b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  base::string16 action_button = delegate->GetActionButtonLabel();
207b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
208b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  const int action_row_column_set_id = 3;
209a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org  views::ColumnSet* bottom_columns =
210b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      layout->AddColumnSet(action_row_column_set_id);
211b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  bottom_columns->AddColumn(views::GridLayout::LEADING,
212b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
213b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  bottom_columns->AddPaddingColumn(1, 0);
214b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  bottom_columns->AddColumn(views::GridLayout::TRAILING,
215b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
216b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!action_button.empty()) {
217b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    bottom_columns->AddColumn(views::GridLayout::TRAILING,
218b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org        views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
219b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
220b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->StartRowWithPadding(0, action_row_column_set_id,
221b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org                              0, kMessageBubblePadding);
222b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
223b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  learn_more_ = new views::Link(delegate->GetLearnMoreLabel());
224a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org  learn_more_->set_listener(this);
225b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->AddView(learn_more_);
226b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
227b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!action_button.empty()) {
228b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    action_button_ = new views::LabelButton(this, action_button.c_str());
229b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    action_button_->SetStyle(views::Button::STYLE_BUTTON);
230b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    layout->AddView(action_button_);
231b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
232b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
233b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  dismiss_button_ = new views::LabelButton(this,
234b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      delegate->GetDismissButtonLabel());
235b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  dismiss_button_->SetStyle(views::Button::STYLE_BUTTON);
236b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  layout->AddView(dismiss_button_);
237b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
238b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
239b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::ButtonPressed(views::Button* sender,
240b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org                                               const ui::Event& event) {
241b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (sender == action_button_) {
242b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    action_taken_ = true;
243b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    action_callback_.Run();
244b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  } else {
245b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    DCHECK_EQ(dismiss_button_, sender);
246b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  }
247b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  GetWidget()->Close();
248b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
249b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
250b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::LinkClicked(views::Link* source,
251b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org                                             int event_flags) {
252b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  DCHECK_EQ(learn_more_, source);
253b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  link_clicked_ = true;
254b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  link_callback_.Run();
255b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  GetWidget()->Close();
256a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org}
257b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
258b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::GetAccessibleState(
259b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    ui::AXViewState* state) {
260b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  state->role = ui::AX_ROLE_ALERT;
261b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
262b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
263b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleView::ViewHierarchyChanged(
264b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    const ViewHierarchyChangedDetails& details) {
265b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (details.is_add && details.child == this)
266b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
267b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
268b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
269b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org////////////////////////////////////////////////////////////////////////////////
270a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org// ExtensionMessageBubbleFactory
271b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
272b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgExtensionMessageBubbleFactory::ExtensionMessageBubbleFactory(
273b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    Profile* profile,
274b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    ToolbarView* toolbar_view)
275b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    : profile_(profile),
276b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      toolbar_view_(toolbar_view),
277b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      shown_suspicious_extensions_bubble_(false),
278b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      shown_startup_override_extensions_bubble_(false),
279b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      shown_proxy_override_extensions_bubble_(false),
280b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      shown_dev_mode_extensions_bubble_(false),
281b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      is_observing_(false),
282b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      stage_(STAGE_START),
283b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      container_(NULL),
284a1a60018a1f1ec863451ad0ed4eae58239882920andrew@webrtc.org      anchor_view_(NULL) {}
285b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
286b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgExtensionMessageBubbleFactory::~ExtensionMessageBubbleFactory() {
287b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  MaybeStopObserving();
288b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
289b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
290b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgvoid ExtensionMessageBubbleFactory::MaybeShow(views::View* anchor_view) {
291b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#if defined(OS_WIN)
292b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  bool is_initial_check = IsInitialProfileCheck(profile_->GetOriginalProfile());
293b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  RecordProfileCheck(profile_->GetOriginalProfile());
294b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
295b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // The list of suspicious extensions takes priority over the dev mode bubble
296b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // and the settings API bubble, since that needs to be shown as soon as we
297b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // disable something. The settings API bubble is shown on first startup after
298b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // an extension has changed the startup pages and it is acceptable if that
29948b9892684682b60b8c4fbc01f1998ace1d32663andrew@webrtc.org  // waits until the next startup because of the suspicious extension bubble.
300b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // The dev mode bubble is not time sensitive like the other two so we'll catch
301b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // the dev mode extensions on the next startup/next window that opens. That
302b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  // way, we're not too spammy with the bubbles.
303b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!shown_suspicious_extensions_bubble_ &&
304b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      MaybeShowSuspiciousExtensionsBubble(anchor_view))
305b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    return;
306b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
307b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!shown_startup_override_extensions_bubble_ &&
308b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      is_initial_check &&
309b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      MaybeShowStartupOverrideExtensionsBubble(anchor_view))
310b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    return;
311b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
312b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!shown_proxy_override_extensions_bubble_ &&
313b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      MaybeShowProxyOverrideExtensionsBubble(anchor_view))
314ffc2de0133683fb103eef61f21134f469bc099dbbjornv@webrtc.org    return;
315ffc2de0133683fb103eef61f21134f469bc099dbbjornv@webrtc.org
316b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!shown_dev_mode_extensions_bubble_)
317b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    MaybeShowDevModeExtensionsBubble(anchor_view);
318b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#endif  // OS_WIN
319b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
320b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
321b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgbool ExtensionMessageBubbleFactory::MaybeShowSuspiciousExtensionsBubble(
322b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    views::View* anchor_view) {
323467f7567c8befea153861e09f048f69932d6e3bdandrew@webrtc.org  DCHECK(!shown_suspicious_extensions_bubble_);
324b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
325b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  scoped_ptr<SuspiciousExtensionBubbleController> suspicious_extensions(
326b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      new SuspiciousExtensionBubbleController(profile_));
327b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  if (!suspicious_extensions->ShouldShow())
328b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    return false;
329b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
330b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  shown_suspicious_extensions_bubble_ = true;
3313f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org  SuspiciousExtensionBubbleController* weak_controller =
3323f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org      suspicious_extensions.get();
333b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  ExtensionMessageBubbleView* bubble_delegate = new ExtensionMessageBubbleView(
3343f6d5e0bded85b8b0d055da8fa49e8d7137fe8edpbos@webrtc.org      anchor_view,
335b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      views::BubbleBorder::TOP_RIGHT,
336b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org      suspicious_extensions.PassAs<ExtensionMessageBubbleController>());
337b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
338b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  views::BubbleDelegateView::CreateBubble(bubble_delegate);
339b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  weak_controller->Show(bubble_delegate);
340b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
341b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  return true;
342b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org}
343b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
344b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.orgbool ExtensionMessageBubbleFactory::MaybeShowStartupOverrideExtensionsBubble(
345b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org    views::View* anchor_view) {
346b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#if !defined(OS_WIN)
347b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  return false;
348b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org#else
349b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org  DCHECK(!shown_startup_override_extensions_bubble_);
350b015cbede88899f67a53fbbe581b02ce8e32794andrew@webrtc.org
351  const Extension* extension = GetExtensionOverridingStartupPages(profile_);
352  if (!extension)
353    return false;
354
355  scoped_ptr<SettingsApiBubbleController> settings_api_bubble(
356      new SettingsApiBubbleController(profile_,
357                                      BUBBLE_TYPE_STARTUP_PAGES));
358  if (!settings_api_bubble->ShouldShow(extension->id()))
359    return false;
360
361  shown_startup_override_extensions_bubble_ = true;
362  PrepareToHighlightExtensions(
363      settings_api_bubble.PassAs<ExtensionMessageBubbleController>(),
364      anchor_view);
365  return true;
366#endif
367}
368
369bool ExtensionMessageBubbleFactory::MaybeShowProxyOverrideExtensionsBubble(
370    views::View* anchor_view) {
371#if !defined(OS_WIN)
372  return false;
373#else
374  DCHECK(!shown_proxy_override_extensions_bubble_);
375
376  const Extension* extension = GetExtensionOverridingProxy(profile_);
377  if (!extension)
378    return false;
379
380  scoped_ptr<ProxyOverriddenBubbleController> proxy_bubble(
381      new ProxyOverriddenBubbleController(profile_));
382  if (!proxy_bubble->ShouldShow(extension->id()))
383    return false;
384
385  shown_proxy_override_extensions_bubble_ = true;
386  PrepareToHighlightExtensions(
387      proxy_bubble.PassAs<ExtensionMessageBubbleController>(), anchor_view);
388  return true;
389#endif
390}
391
392bool ExtensionMessageBubbleFactory::MaybeShowDevModeExtensionsBubble(
393    views::View* anchor_view) {
394  DCHECK(!shown_dev_mode_extensions_bubble_);
395
396  // Check the Developer Mode extensions.
397  scoped_ptr<DevModeBubbleController> dev_mode_extensions(
398      new DevModeBubbleController(profile_));
399
400  // Return early if we have none to show.
401  if (!dev_mode_extensions->ShouldShow())
402    return false;
403
404  shown_dev_mode_extensions_bubble_ = true;
405  PrepareToHighlightExtensions(
406      dev_mode_extensions.PassAs<ExtensionMessageBubbleController>(),
407      anchor_view);
408  return true;
409}
410
411void ExtensionMessageBubbleFactory::MaybeObserve() {
412  if (!is_observing_) {
413    is_observing_ = true;
414    container_->AddObserver(this);
415  }
416}
417
418void ExtensionMessageBubbleFactory::MaybeStopObserving() {
419  if (is_observing_) {
420    is_observing_ = false;
421    container_->RemoveObserver(this);
422  }
423}
424
425void ExtensionMessageBubbleFactory::RecordProfileCheck(Profile* profile) {
426  g_profiles_evaluated.Get().insert(profile);
427}
428
429bool ExtensionMessageBubbleFactory::IsInitialProfileCheck(Profile* profile) {
430  return g_profiles_evaluated.Get().count(profile) == 0;
431}
432
433void ExtensionMessageBubbleFactory::OnBrowserActionsContainerAnimationEnded() {
434  MaybeStopObserving();
435  if (stage_ == STAGE_START) {
436    HighlightExtensions();
437  } else if (stage_ == STAGE_HIGHLIGHTED) {
438    ShowHighlightingBubble();
439  } else {  // We shouldn't be observing if we've completed the process.
440    NOTREACHED();
441    Finish();
442  }
443}
444
445void ExtensionMessageBubbleFactory::OnBrowserActionsContainerDestroyed() {
446  // If the container associated with the bubble is destroyed, abandon the
447  // process.
448  Finish();
449}
450
451void ExtensionMessageBubbleFactory::PrepareToHighlightExtensions(
452    scoped_ptr<ExtensionMessageBubbleController> controller,
453    views::View* anchor_view) {
454  // We should be in the start stage (i.e., should not have a pending attempt to
455  // show a bubble).
456  DCHECK_EQ(stage_, STAGE_START);
457
458  // Prepare to display and highlight the extensions before showing the bubble.
459  // Since this is an asynchronous process, set member variables for later use.
460  controller_ = controller.Pass();
461  anchor_view_ = anchor_view;
462  container_ = toolbar_view_->browser_actions();
463
464  if (container_->animating())
465    MaybeObserve();
466  else
467    HighlightExtensions();
468}
469
470void ExtensionMessageBubbleFactory::HighlightExtensions() {
471  DCHECK_EQ(STAGE_START, stage_);
472  stage_ = STAGE_HIGHLIGHTED;
473
474  const ExtensionIdList extension_list = controller_->GetExtensionIdList();
475  DCHECK(!extension_list.empty());
476  ExtensionToolbarModel::Get(profile_)->HighlightExtensions(extension_list);
477  if (container_->animating())
478    MaybeObserve();
479  else
480    ShowHighlightingBubble();
481}
482
483void ExtensionMessageBubbleFactory::ShowHighlightingBubble() {
484  DCHECK_EQ(stage_, STAGE_HIGHLIGHTED);
485  stage_ = STAGE_COMPLETE;
486
487  views::View* reference_view = NULL;
488  if (container_->num_browser_actions() > 0u)
489    reference_view = container_->GetBrowserActionViewAt(0);
490  if (reference_view && reference_view->visible())
491    anchor_view_ = reference_view;
492
493  ExtensionMessageBubbleController* weak_controller = controller_.get();
494  ExtensionMessageBubbleView* bubble_delegate =
495      new ExtensionMessageBubbleView(
496          anchor_view_,
497          views::BubbleBorder::TOP_RIGHT,
498          scoped_ptr<ExtensionMessageBubbleController>(
499              controller_.release()));
500  views::BubbleDelegateView::CreateBubble(bubble_delegate);
501  weak_controller->Show(bubble_delegate);
502
503  Finish();
504}
505
506void ExtensionMessageBubbleFactory::Finish() {
507  MaybeStopObserving();
508  controller_.reset();
509  anchor_view_ = NULL;
510  container_ = NULL;
511}
512
513}  // namespace extensions
514