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

Side by Side Diff: chrome/browser/ui/views/message_center/web_notification_tray.cc

Issue 18003003: Message center re-organized (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/message_center/web_notification_tray.h" 5 #include "chrome/browser/ui/views/message_center/web_notification_tray.h"
6 6
7 #include "base/i18n/number_formatting.h" 7 #include "base/i18n/number_formatting.h"
8 #include "base/strings/string16.h" 8 #include "base/strings/string16.h"
9 #include "base/strings/utf_string_conversions.h" 9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/browser_process.h" 10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/status_icons/status_icon.h" 11 #include "chrome/browser/status_icons/status_icon.h"
12 #include "chrome/browser/status_icons/status_tray.h" 12 #include "chrome/browser/status_icons/status_tray.h"
13 #include "chrome/browser/ui/views/message_center/notification_bubble_wrapper.h"
14 #include "content/public/browser/user_metrics.h" 13 #include "content/public/browser/user_metrics.h"
15 #include "grit/chromium_strings.h" 14 #include "grit/chromium_strings.h"
16 #include "grit/theme_resources.h" 15 #include "grit/theme_resources.h"
17 #include "grit/ui_strings.h" 16 #include "grit/ui_strings.h"
18 #include "ui/base/l10n/l10n_util.h" 17 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/base/models/simple_menu_model.h"
20 #include "ui/base/resource/resource_bundle.h" 18 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/gfx/canvas.h" 19 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/image/image_skia_operations.h" 20 #include "ui/gfx/image/image_skia_operations.h"
23 #include "ui/gfx/rect.h" 21 #include "ui/gfx/rect.h"
24 #include "ui/gfx/screen.h" 22 #include "ui/gfx/screen.h"
25 #include "ui/gfx/size.h" 23 #include "ui/gfx/size.h"
26 #include "ui/message_center/message_center_tray.h" 24 #include "ui/message_center/message_center_tray.h"
27 #include "ui/message_center/message_center_tray_delegate.h" 25 #include "ui/message_center/message_center_tray_delegate.h"
28 #include "ui/message_center/views/message_bubble_base.h"
29 #include "ui/message_center/views/message_center_bubble.h"
30 #include "ui/message_center/views/message_popup_collection.h" 26 #include "ui/message_center/views/message_popup_collection.h"
31 #include "ui/views/widget/widget.h" 27 #include "ui/views/widget/widget.h"
32 28
33 namespace { 29 namespace {
34 30
35 // Tray constants 31 // Tray constants
36 const int kScreenEdgePadding = 2; 32 const int kScreenEdgePadding = 2;
37 33
38 const int kSystemTrayWidth = 16; 34 const int kSystemTrayWidth = 16;
39 const int kSystemTrayHeight = 16; 35 const int kSystemTrayHeight = 16;
40 const int kNumberOfSystemTraySprites = 10; 36 const int kNumberOfSystemTraySprites = 10;
41 37
42 gfx::Rect GetCornerAnchorRect() { 38 gfx::ImageSkia GetIcon(int unread_count) {
43 // TODO(dewittj): Use the preference to determine which corner to anchor from. 39 bool has_unread = unread_count > 0;
44 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); 40 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
45 gfx::Rect rect = screen->GetPrimaryDisplay().work_area(); 41 if (!has_unread)
46 rect.Inset(kScreenEdgePadding, kScreenEdgePadding); 42 return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_EMPTY);
47 return gfx::Rect(rect.bottom_right(), gfx::Size()); 43
44 return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_ATTENTION);
48 } 45 }
49 46
50 gfx::Point GetClosestCorner(gfx::Rect rect, gfx::Point query) { 47 } // namespace
48
49 using content::UserMetricsAction;
50
51 namespace message_center {
52
53 namespace internal {
54
55 // Gets the position of the taskbar from the work area bounds. Returns
56 // ALIGNMENT_NONE if position cannot be found.
57 Alignment GetSystrayAlignment() {
58 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
59 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong.
60 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds();
61 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
62 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
63
64 // Comparing the work area to the screen bounds gives us the location of the
65 // taskbar. If the work area is exactly the same as the screen bounds,
66 // we are unable to locate the taskbar so we say we don't know it's alignment.
67 if (work_area.height() < screen_bounds.height()) {
68 if (work_area.y() > screen_bounds.y())
69 return ALIGNMENT_TOP;
70 return ALIGNMENT_BOTTOM;
71 }
72 if (work_area.width() < screen_bounds.width()) {
73 if (work_area.x() > screen_bounds.x())
74 return ALIGNMENT_LEFT;
75 return ALIGNMENT_RIGHT;
76 }
77
78 return ALIGNMENT_NONE;
79 }
80
81 gfx::Point GetClosestCorner(const gfx::Rect& rect, const gfx::Point& query) {
51 gfx::Point center_point = rect.CenterPoint(); 82 gfx::Point center_point = rect.CenterPoint();
52 gfx::Point rv; 83 gfx::Point rv;
53 84
54 if (query.x() > center_point.x()) 85 if (query.x() > center_point.x())
55 rv.set_x(rect.right()); 86 rv.set_x(rect.right());
56 else 87 else
57 rv.set_x(rect.x()); 88 rv.set_x(rect.x());
58 89
59 if (query.y() > center_point.y()) 90 if (query.y() > center_point.y())
60 rv.set_y(rect.bottom()); 91 rv.set_y(rect.bottom());
61 else 92 else
62 rv.set_y(rect.y()); 93 rv.set_y(rect.y());
63 94
64 return rv; 95 return rv;
65 } 96 }
66 97
67 // GetMouseAnchorRect returns a rectangle that has one corner where the mouse 98 // Gets the corner of the screen where the message center should pop up.
68 // clicked, and the opposite corner at the closest corner of the work area 99 Alignment GetAnchorAlignment(const gfx::Rect& work_area, gfx::Point corner) {
69 // (inset by an appropriate margin.) 100 gfx::Point center = work_area.CenterPoint();
70 gfx::Rect GetMouseAnchorRect(gfx::Point cursor) {
71 // TODO(dewittj): GetNativeScreen could be wrong for Aura.
72 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
73 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
74 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
75 gfx::Point corner = GetClosestCorner(work_area, cursor);
76 101
77 gfx::Rect mouse_anchor_rect(gfx::BoundingRect(cursor, corner)); 102 Alignment anchor_alignment =
78 return mouse_anchor_rect; 103 center.y() > corner.y() ? ALIGNMENT_TOP : ALIGNMENT_BOTTOM;
104 anchor_alignment =
105 (Alignment)(anchor_alignment |
106 (center.x() > corner.x() ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT));
107
108 return anchor_alignment;
79 } 109 }
80 110
81 gfx::ImageSkia GetIcon(int unread_count) { 111 } // namespace internal
82 bool has_unread = unread_count > 0;
83 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
84 if (!has_unread)
85 return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_EMPTY);
86
87 return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_TRAY_ATTENTION);
88 }
89
90 } // namespace
91
92 using content::UserMetricsAction;
93
94 namespace message_center {
95 112
96 MessageCenterTrayDelegate* CreateMessageCenterTray() { 113 MessageCenterTrayDelegate* CreateMessageCenterTray() {
97 return new WebNotificationTray(); 114 return new WebNotificationTray();
98 } 115 }
99 116
100 WebNotificationTray::WebNotificationTray() 117 WebNotificationTray::WebNotificationTray()
101 : status_icon_(NULL), 118 : message_center_delegate_(NULL),
119 status_icon_(NULL),
102 message_center_visible_(false), 120 message_center_visible_(false),
103 should_update_tray_content_(true) { 121 should_update_tray_content_(true) {
104 message_center_tray_.reset( 122 message_center_tray_.reset(
105 new MessageCenterTray(this, g_browser_process->message_center())); 123 new MessageCenterTray(this, g_browser_process->message_center()));
106 UpdateStatusIcon(); 124 UpdateStatusIcon();
107 } 125 }
108 126
109 WebNotificationTray::~WebNotificationTray() { 127 WebNotificationTray::~WebNotificationTray() {
110 // Reset this early so that delegated events during destruction don't cause 128 // Reset this early so that delegated events during destruction don't cause
111 // problems. 129 // problems.
112 message_center_tray_.reset(); 130 message_center_tray_.reset();
113 DestroyStatusIcon(); 131 DestroyStatusIcon();
114 } 132 }
115 133
116 message_center::MessageCenter* WebNotificationTray::message_center() { 134 message_center::MessageCenter* WebNotificationTray::message_center() {
117 return message_center_tray_->message_center(); 135 return message_center_tray_->message_center();
118 } 136 }
119 137
120 bool WebNotificationTray::ShowPopups() { 138 bool WebNotificationTray::ShowPopups() {
121 popup_collection_.reset(new message_center::MessagePopupCollection( 139 popup_collection_.reset(new message_center::MessagePopupCollection(
122 NULL, message_center(), message_center_tray_.get())); 140 NULL, message_center(), message_center_tray_.get()));
123 return true; 141 return true;
124 } 142 }
125 143
126 void WebNotificationTray::HidePopups() { popup_collection_.reset(); } 144 void WebNotificationTray::HidePopups() { popup_collection_.reset(); }
127 145
128 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) { 146 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) {
129 content::RecordAction(UserMetricsAction("Notifications.ShowMessageCenter")); 147 content::RecordAction(UserMetricsAction("Notifications.ShowMessageCenter"));
130 148
131 // Using MessageBubbleBase instead of MessageCenterBubble to 149 message_center_delegate_ = new MessageCenterWidgetDelegate(
132 // remove dependence on implicit type conversion 150 this,
133 scoped_ptr<message_center::MessageCenterBubble> bubble( 151 message_center_tray_.get(),
134 new message_center::MessageCenterBubble(message_center(), 152 show_settings, /* settings initally invisible */
dewittj 2013/07/16 18:24:01 -> // style
sidharthms 2013/07/16 21:31:23 Done.
135 message_center_tray_.get())); 153 GetPositionInfo());
dewittj 2013/07/16 00:21:29 Now you have a potential use-after-free bug becaus
sidharthms 2013/07/16 01:03:20 Comment added. Done.
136 154
137 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
138 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
139 views::TrayBubbleView::AnchorAlignment alignment = GetAnchorAlignment();
140
141 int max_height = work_area.height();
142
143 // If the alignment is left- or right-oriented, the bubble can fill up the
144 // entire vertical height of the screen since the bubble is rendered to the
145 // side of the clicked icon. Otherwise we have to adjust for the arrow's
146 // height.
147 if (alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM ||
148 alignment == views::TrayBubbleView::ANCHOR_ALIGNMENT_TOP) {
149 max_height -= 2 * kScreenEdgePadding;
150
151 // If the work area contains the click point, then we know that the icon is
152 // not in the taskbar. Then we need to subtract the distance of the click
153 // point from the edge of the work area so we can see the whole bubble.
154 if (work_area.Contains(mouse_click_point_)) {
155 max_height -= std::min(mouse_click_point_.y() - work_area.y(),
156 work_area.bottom() - mouse_click_point_.y());
157 }
158 }
159 bubble->SetMaxHeight(max_height);
160 if (show_settings)
161 bubble->SetSettingsVisible();
162
163 message_center_bubble_.reset(new internal::NotificationBubbleWrapper(
164 this,
165 bubble.PassAs<message_center::MessageBubbleBase>(),
166 internal::NotificationBubbleWrapper::BUBBLE_TYPE_MESSAGE_CENTER));
167 return true; 155 return true;
168 } 156 }
169 157
170 bool WebNotificationTray::ShowMessageCenter() { 158 bool WebNotificationTray::ShowMessageCenter() {
171 return ShowMessageCenterInternal(false /* show_settings */); 159 return ShowMessageCenterInternal(false /* show_settings */);
dewittj 2013/07/16 18:24:01 /*show_settings=*/ false please :)
sidharthms 2013/07/16 21:31:23 Done.
172 } 160 }
173 161
174 void WebNotificationTray::HideMessageCenter() { 162 void WebNotificationTray::HideMessageCenter() {
175 message_center_bubble_.reset(); 163 if (message_center_delegate_) {
164 views::Widget* widget = message_center_delegate_->GetWidget();
165 if (widget)
166 widget->Close();
167 }
176 } 168 }
177 169
178 bool WebNotificationTray::ShowNotifierSettings() { 170 bool WebNotificationTray::ShowNotifierSettings() {
179 if (message_center_bubble_) { 171 if (message_center_delegate_) {
180 static_cast<MessageCenterBubble*>( 172 message_center_delegate_->SetSettingsVisible(true);
181 message_center_bubble_->bubble())->SetSettingsVisible();
182 return true; 173 return true;
183 } 174 }
184 return ShowMessageCenterInternal(true /* show_settings */); 175 return ShowMessageCenterInternal(true /* show_settings */);
185 } 176 }
186 177
187 void WebNotificationTray::OnMessageCenterTrayChanged() { 178 void WebNotificationTray::OnMessageCenterTrayChanged() {
188 // See the comments in ash/system/web_notification/web_notification_tray.cc 179 // See the comments in ash/system/web_notification/web_notification_tray.cc
189 // for why PostTask. 180 // for why PostTask.
190 should_update_tray_content_ = true; 181 should_update_tray_content_ = true;
191 base::MessageLoop::current()->PostTask( 182 base::MessageLoop::current()->PostTask(
192 FROM_HERE, 183 FROM_HERE,
193 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr())); 184 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr()));
194 } 185 }
195 186
196 gfx::Rect WebNotificationTray::GetMessageCenterAnchor() { 187 void WebNotificationTray::OnStatusIconClicked() {
197 return GetMouseAnchorRect(mouse_click_point_); 188 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura.
189 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
190 mouse_click_point_ = screen->GetCursorScreenPoint();
191 message_center_tray_->ToggleMessageCenterBubble();
198 } 192 }
199 193
200 gfx::Rect WebNotificationTray::GetPopupAnchor() {
201 return GetCornerAnchorRect();
202 }
203
204 views::TrayBubbleView::AnchorAlignment
205 WebNotificationTray::GetAnchorAlignment() {
206 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
207 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong.
208 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds();
209 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
210
211 // Comparing the work area to the screen bounds gives us the location of the
212 // taskbar. If the work area is less tall than the screen, assume the taskbar
213 // is on the bottom, and cause the arrow to be displayed on the bottom of the
214 // bubble. Otherwise, cause the arrow to be displayed on the side of the
215 // bubble that the taskbar is on.
216 if (work_area.width() < screen_bounds.width()) {
217 if (work_area.x() > screen_bounds.x())
218 return views::TrayBubbleView::ANCHOR_ALIGNMENT_LEFT;
219 return views::TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT;
220 }
221 return views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM;
222 }
223
224 gfx::NativeView WebNotificationTray::GetBubbleWindowContainer() { return NULL; }
225
226 void WebNotificationTray::UpdateStatusIcon() { 194 void WebNotificationTray::UpdateStatusIcon() {
227 if (!should_update_tray_content_) 195 if (!should_update_tray_content_)
228 return; 196 return;
229 should_update_tray_content_ = false; 197 should_update_tray_content_ = false;
230 198
231 int total_notifications = message_center()->NotificationCount(); 199 int total_notifications = message_center()->NotificationCount();
232 if (total_notifications == 0) { 200 if (total_notifications == 0) {
233 DestroyStatusIcon(); 201 DestroyStatusIcon();
234 return; 202 return;
235 } 203 }
236 204
237 int unread_notifications = message_center()->UnreadNotificationCount(); 205 int unread_notifications = message_center()->UnreadNotificationCount();
238 StatusIcon* status_icon = GetStatusIcon(); 206 StatusIcon* status_icon = GetStatusIcon();
239 if (!status_icon) 207 if (!status_icon)
240 return; 208 return;
241 209
242 status_icon->SetImage(GetIcon(unread_notifications)); 210 status_icon->SetImage(GetIcon(unread_notifications));
243 211
244 string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); 212 string16 product_name(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
245 if (unread_notifications > 0) { 213 if (unread_notifications > 0) {
246 string16 str_unread_count = base::FormatNumber(unread_notifications); 214 string16 str_unread_count = base::FormatNumber(unread_notifications);
247 status_icon->SetToolTip(l10n_util::GetStringFUTF16( 215 status_icon->SetToolTip(l10n_util::GetStringFUTF16(
248 IDS_MESSAGE_CENTER_TOOLTIP_UNREAD, product_name, str_unread_count)); 216 IDS_MESSAGE_CENTER_TOOLTIP_UNREAD, product_name, str_unread_count));
249 } else { 217 } else {
250 status_icon->SetToolTip( 218 status_icon->SetToolTip(
251 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP, product_name)); 219 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP, product_name));
252 } 220 }
253 } 221 }
254 222
255 void WebNotificationTray::OnStatusIconClicked() { 223 void WebNotificationTray::SendHideMessageCenter() {
256 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura. 224 message_center_tray_->HideMessageCenterBubble();
257 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
258 mouse_click_point_ = screen->GetCursorScreenPoint();
259 message_center_tray_->ToggleMessageCenterBubble();
260 } 225 }
261 226
262 void WebNotificationTray::HideBubbleWithView( 227 void WebNotificationTray::MarkMessageCenterHidden() {
263 const views::TrayBubbleView* bubble_view) { 228 if (message_center_delegate_) {
264 if (message_center_bubble_.get() && 229 message_center_tray_->MarkMessageCenterHidden();
265 bubble_view == message_center_bubble_->bubble_view()) { 230 message_center_delegate_ = NULL;
266 message_center_tray_->HideMessageCenterBubble();
267 } 231 }
268 } 232 }
269 233
234 PositionInfo WebNotificationTray::GetPositionInfo() {
235 PositionInfo pos_info;
236
237 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
238 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
239 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
240
241 gfx::Point corner = internal::GetClosestCorner(work_area, mouse_click_point_);
242
243 pos_info.taskbar_alignment = internal::GetSystrayAlignment();
244
245 // We assume the taskbar is either at the top or at the bottom if we are not
246 // able to find it.
247 if (pos_info.taskbar_alignment == ALIGNMENT_NONE) {
248 if (mouse_click_point_.y() > corner.y())
249 pos_info.taskbar_alignment = ALIGNMENT_TOP;
250 else
251 pos_info.taskbar_alignment = ALIGNMENT_BOTTOM;
252 }
253
254 pos_info.message_center_alignment =
255 internal::GetAnchorAlignment(work_area, corner);
256
257 pos_info.inital_anchor_point = corner;
258 pos_info.max_height = work_area.height();
259
260 if (work_area.Contains(mouse_click_point_)) {
261 pos_info.max_height -= std::abs(mouse_click_point_.y() - corner.y());
262
263 // Message center is in the work area. So position it few pixels above the
264 // mouse click point if alignemnt is towards bottom and few pixels below if
265 // alignment is towards top.
266 pos_info.inital_anchor_point
267 .set_y(mouse_click_point_.y() +
268 (pos_info.message_center_alignment & ALIGNMENT_BOTTOM ? -5 : 5));
269 }
270 return pos_info;
271 }
272
270 StatusIcon* WebNotificationTray::GetStatusIcon() { 273 StatusIcon* WebNotificationTray::GetStatusIcon() {
271 if (status_icon_) 274 if (status_icon_)
272 return status_icon_; 275 return status_icon_;
273 276
274 StatusTray* status_tray = g_browser_process->status_tray(); 277 StatusTray* status_tray = g_browser_process->status_tray();
275 if (!status_tray) 278 if (!status_tray)
276 return NULL; 279 return NULL;
277 280
278 StatusIcon* status_icon = status_tray->CreateStatusIcon(); 281 StatusIcon* status_icon = status_tray->CreateStatusIcon();
279 if (!status_icon) 282 if (!status_icon)
(...skipping 15 matching lines...) Expand all
295 if (status_tray) 298 if (status_tray)
296 status_tray->RemoveStatusIcon(status_icon_); 299 status_tray->RemoveStatusIcon(status_icon_);
297 status_icon_ = NULL; 300 status_icon_ = NULL;
298 } 301 }
299 302
300 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) { 303 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) {
301 DCHECK(status_icon); 304 DCHECK(status_icon);
302 status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu()); 305 status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu());
303 } 306 }
304 307
305 message_center::MessageCenterBubble* 308 MessageCenterWidgetDelegate*
306 WebNotificationTray::GetMessageCenterBubbleForTest() { 309 WebNotificationTray::GetMessageCenterWidgetDelegateForTest() {
307 if (!message_center_bubble_.get()) 310 return message_center_delegate_;
308 return NULL;
309 return static_cast<message_center::MessageCenterBubble*>(
310 message_center_bubble_->bubble());
311 } 311 }
312 312
313 } // namespace message_center 313 } // namespace message_center
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698