• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/chromeos/notifications/balloon_view.h"
6 
7 #include <vector>
8 
9 #include "base/message_loop.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/chromeos/notifications/balloon_view_host.h"
12 #include "chrome/browser/chromeos/notifications/notification_panel.h"
13 #include "chrome/browser/notifications/balloon.h"
14 #include "chrome/browser/notifications/desktop_notification_service.h"
15 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
16 #include "chrome/browser/notifications/notification.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/views/notifications/balloon_view_host.h"
19 #include "content/browser/renderer_host/render_view_host.h"
20 #include "content/browser/renderer_host/render_widget_host_view.h"
21 #include "content/common/notification_details.h"
22 #include "content/common/notification_source.h"
23 #include "content/common/notification_type.h"
24 #include "grit/generated_resources.h"
25 #include "grit/theme_resources.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/models/simple_menu_model.h"
28 #include "ui/base/resource/resource_bundle.h"
29 #include "views/background.h"
30 #include "views/controls/button/button.h"
31 #include "views/controls/button/image_button.h"
32 #include "views/controls/button/menu_button.h"
33 #include "views/controls/label.h"
34 #include "views/controls/menu/menu_2.h"
35 #include "views/controls/menu/view_menu_delegate.h"
36 #include "views/widget/root_view.h"
37 #include "views/widget/widget_gtk.h"
38 
39 namespace {
40 // Menu commands
41 const int kNoopCommand = 0;
42 const int kRevokePermissionCommand = 1;
43 
44 // Vertical margin between close button and menu button.
45 const int kControlButtonsMargin = 6;
46 
47 // Top, Right margin for notification control view.
48 const int kControlViewTopMargin = 4;
49 const int kControlViewRightMargin = 6;
50 }  // namespace
51 
52 namespace chromeos {
53 
54 // NotificationControlView has close and menu buttons and
55 // overlays on top of renderer view.
56 class NotificationControlView : public views::View,
57                                 public views::ViewMenuDelegate,
58                                 public ui::SimpleMenuModel::Delegate,
59                                 public views::ButtonListener {
60  public:
NotificationControlView(BalloonViewImpl * view)61   explicit NotificationControlView(BalloonViewImpl* view)
62       : balloon_view_(view),
63         close_button_(NULL),
64         options_menu_contents_(NULL),
65         options_menu_menu_(NULL),
66         options_menu_button_(NULL) {
67     // TODO(oshima): make background transparent.
68     set_background(views::Background::CreateSolidBackground(SK_ColorWHITE));
69 
70     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
71 
72     SkBitmap* close_button_n = rb.GetBitmapNamed(IDR_TAB_CLOSE);
73     SkBitmap* close_button_m = rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK);
74     SkBitmap* close_button_h = rb.GetBitmapNamed(IDR_TAB_CLOSE_H);
75     SkBitmap* close_button_p = rb.GetBitmapNamed(IDR_TAB_CLOSE_P);
76 
77     close_button_ = new views::ImageButton(this);
78     close_button_->SetImage(views::CustomButton::BS_NORMAL, close_button_n);
79     close_button_->SetImage(views::CustomButton::BS_HOT, close_button_h);
80     close_button_->SetImage(views::CustomButton::BS_PUSHED, close_button_p);
81     close_button_->SetBackground(
82         SK_ColorBLACK, close_button_n, close_button_m);
83 
84     AddChildView(close_button_);
85 
86     options_menu_button_
87         = new views::MenuButton(NULL, std::wstring(), this, false);
88     options_menu_button_->SetFont(rb.GetFont(ResourceBundle::SmallFont));
89     options_menu_button_->SetIcon(*rb.GetBitmapNamed(IDR_NOTIFICATION_MENU));
90     options_menu_button_->set_border(NULL);
91 
92     options_menu_button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT);
93     AddChildView(options_menu_button_);
94 
95     // The control view will never be resized, so just layout once.
96     gfx::Size options_size = options_menu_button_->GetPreferredSize();
97     gfx::Size button_size = close_button_->GetPreferredSize();
98 
99     int height = std::max(options_size.height(), button_size.height());
100     options_menu_button_->SetBounds(
101         0, (height - options_size.height()) / 2,
102         options_size.width(), options_size.height());
103 
104     close_button_->SetBounds(
105         options_size.width() + kControlButtonsMargin,
106         (height - button_size.height()) / 2,
107         button_size.width(), button_size.height());
108 
109     SizeToPreferredSize();
110   }
111 
GetPreferredSize()112   virtual gfx::Size GetPreferredSize() {
113     gfx::Rect total_bounds =
114         close_button_->bounds().Union(options_menu_button_->bounds());
115     return total_bounds.size();
116   }
117 
118   // views::ViewMenuDelegate implements.
RunMenu(views::View * source,const gfx::Point & pt)119   virtual void RunMenu(views::View* source, const gfx::Point& pt) {
120     CreateOptionsMenu();
121     options_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT);
122   }
123 
124   // views::ButtonListener implements.
ButtonPressed(views::Button * sender,const views::Event &)125   virtual void ButtonPressed(views::Button* sender, const views::Event&) {
126     balloon_view_->Close(true);
127   }
128 
129   // ui::SimpleMenuModel::Delegate impglements.
IsCommandIdChecked(int) const130   virtual bool IsCommandIdChecked(int /* command_id */) const {
131     // Nothing in the menu is checked.
132     return false;
133   }
134 
IsCommandIdEnabled(int) const135   virtual bool IsCommandIdEnabled(int /* command_id */) const {
136     // All the menu options are always enabled.
137     return true;
138   }
139 
GetAcceleratorForCommandId(int,ui::Accelerator *)140   virtual bool GetAcceleratorForCommandId(
141       int /* command_id */, ui::Accelerator* /* accelerator */) {
142     // Currently no accelerators.
143     return false;
144   }
145 
ExecuteCommand(int command_id)146   virtual void ExecuteCommand(int command_id) {
147     switch (command_id) {
148       case kRevokePermissionCommand:
149         balloon_view_->DenyPermission();
150       default:
151         NOTIMPLEMENTED();
152     }
153   }
154 
155  private:
CreateOptionsMenu()156   void CreateOptionsMenu() {
157     if (options_menu_contents_.get())
158       return;
159     const string16 source_label_text = l10n_util::GetStringFUTF16(
160         IDS_NOTIFICATION_BALLOON_SOURCE_LABEL,
161         balloon_view_->balloon_->notification().display_source());
162     const string16 label_text = l10n_util::GetStringFUTF16(
163         IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE,
164         balloon_view_->balloon_->notification().display_source());
165 
166     options_menu_contents_.reset(new ui::SimpleMenuModel(this));
167     // TODO(oshima): Showing the source info in the menu for now.
168     // Figure out where to show the source info.
169     options_menu_contents_->AddItem(kNoopCommand, source_label_text);
170     options_menu_contents_->AddItem(kRevokePermissionCommand, label_text);
171 
172     options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get()));
173   }
174 
175   BalloonViewImpl* balloon_view_;
176 
177   views::ImageButton* close_button_;
178 
179   // The options menu.
180   scoped_ptr<ui::SimpleMenuModel> options_menu_contents_;
181   scoped_ptr<views::Menu2> options_menu_menu_;
182   views::MenuButton* options_menu_button_;
183 
184   DISALLOW_COPY_AND_ASSIGN(NotificationControlView);
185 };
186 
BalloonViewImpl(bool sticky,bool controls,bool web_ui)187 BalloonViewImpl::BalloonViewImpl(bool sticky, bool controls, bool web_ui)
188     : balloon_(NULL),
189       html_contents_(NULL),
190       method_factory_(this),
191       stale_(false),
192       sticky_(sticky),
193       controls_(controls),
194       closed_(false),
195       web_ui_(web_ui) {
196   // This object is not to be deleted by the views hierarchy,
197   // as it is owned by the balloon.
198   set_parent_owned(false);
199 }
200 
~BalloonViewImpl()201 BalloonViewImpl::~BalloonViewImpl() {
202   if (control_view_host_.get()) {
203     control_view_host_->CloseNow();
204   }
205   if (html_contents_) {
206     html_contents_->Shutdown();
207   }
208 }
209 
210 ////////////////////////////////////////////////////////////////////////////////
211 // BallonViewImpl, BalloonView implementation.
212 
Show(Balloon * balloon)213 void BalloonViewImpl::Show(Balloon* balloon) {
214   balloon_ = balloon;
215   html_contents_ = new BalloonViewHost(balloon);
216   if (web_ui_)
217     html_contents_->EnableWebUI();
218   AddChildView(html_contents_->view());
219   notification_registrar_.Add(this,
220     NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon));
221 }
222 
Update()223 void BalloonViewImpl::Update() {
224   stale_ = false;
225   if (html_contents_->render_view_host())
226     html_contents_->render_view_host()->NavigateToURL(
227         balloon_->notification().content_url());
228 }
229 
Close(bool by_user)230 void BalloonViewImpl::Close(bool by_user) {
231   closed_ = true;
232   MessageLoop::current()->PostTask(
233       FROM_HERE,
234       method_factory_.NewRunnableMethod(
235           &BalloonViewImpl::DelayedClose, by_user));
236 }
237 
GetSize() const238 gfx::Size BalloonViewImpl::GetSize() const {
239   // Not used. The layout is managed by the Panel.
240   return gfx::Size(0, 0);
241 }
242 
GetHost() const243 BalloonHost* BalloonViewImpl::GetHost() const {
244   return html_contents_;
245 }
246 
RepositionToBalloon()247 void BalloonViewImpl::RepositionToBalloon() {
248   // Not used. The layout is managed by the Panel.
249 }
250 
251 ////////////////////////////////////////////////////////////////////////////////
252 // views::View interface overrides.
253 
Layout()254 void BalloonViewImpl::Layout() {
255   gfx::Size size = balloon_->content_size();
256 
257   SetBounds(x(), y(), size.width(), size.height());
258 
259   html_contents_->view()->SetBounds(0, 0, size.width(), size.height());
260   if (html_contents_->render_view_host()) {
261     RenderWidgetHostView* view = html_contents_->render_view_host()->view();
262     if (view)
263       view->SetSize(size);
264   }
265 }
266 
ViewHierarchyChanged(bool is_add,View * parent,View * child)267 void BalloonViewImpl::ViewHierarchyChanged(
268     bool is_add, View* parent, View* child) {
269   if (is_add && GetWidget() && !control_view_host_.get() && controls_) {
270     views::Widget::CreateParams params(
271         views::Widget::CreateParams::TYPE_CONTROL);
272     params.delete_on_destroy = false;
273     control_view_host_.reset(views::Widget::CreateWidget(params));
274     static_cast<views::WidgetGtk*>(control_view_host_.get())->
275         EnableDoubleBuffer(true);
276     control_view_host_->Init(GetParentNativeView(), gfx::Rect());
277     NotificationControlView* control = new NotificationControlView(this);
278     control_view_host_->SetContentsView(control);
279   }
280   if (!is_add && this == child && control_view_host_.get() && controls_) {
281     control_view_host_.release()->CloseNow();
282   }
283 }
284 
285 ////////////////////////////////////////////////////////////////////////////////
286 // NotificationObserver overrides.
287 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)288 void BalloonViewImpl::Observe(NotificationType type,
289                               const NotificationSource& source,
290                               const NotificationDetails& details) {
291   if (type != NotificationType::NOTIFY_BALLOON_DISCONNECTED) {
292     NOTREACHED();
293     return;
294   }
295 
296   // If the renderer process attached to this balloon is disconnected
297   // (e.g., because of a crash), we want to close the balloon.
298   notification_registrar_.Remove(this,
299       NotificationType::NOTIFY_BALLOON_DISCONNECTED, Source<Balloon>(balloon_));
300   Close(false);
301 }
302 
303 ////////////////////////////////////////////////////////////////////////////////
304 // BalloonViewImpl public.
305 
IsFor(const Notification & notification) const306 bool BalloonViewImpl::IsFor(const Notification& notification) const {
307   return balloon_->notification().notification_id() ==
308       notification.notification_id();
309 }
310 
Activated()311 void BalloonViewImpl::Activated() {
312   if (!control_view_host_.get())
313     return;
314 
315   // Get the size of Control View.
316   gfx::Size size =
317       control_view_host_->GetRootView()->GetChildViewAt(0)->GetPreferredSize();
318   control_view_host_->Show();
319   control_view_host_->SetBounds(
320       gfx::Rect(width() - size.width() - kControlViewRightMargin,
321                 kControlViewTopMargin,
322                 size.width(), size.height()));
323 }
324 
Deactivated()325 void BalloonViewImpl::Deactivated() {
326   if (control_view_host_.get()) {
327     control_view_host_->Hide();
328   }
329 }
330 
331 ////////////////////////////////////////////////////////////////////////////////
332 // BalloonViewImpl private.
333 
DelayedClose(bool by_user)334 void BalloonViewImpl::DelayedClose(bool by_user) {
335   html_contents_->Shutdown();
336   html_contents_ = NULL;
337   balloon_->OnClose(by_user);
338 }
339 
DenyPermission()340 void BalloonViewImpl::DenyPermission() {
341   DesktopNotificationService* service =
342       DesktopNotificationServiceFactory::GetForProfile(balloon_->profile());
343   service->DenyPermission(balloon_->notification().origin_url());
344 }
345 
GetParentNativeView()346 gfx::NativeView BalloonViewImpl::GetParentNativeView() {
347   RenderWidgetHostView* view = html_contents_->render_view_host()->view();
348   DCHECK(view);
349   return view->GetNativeView();
350 }
351 
352 }  // namespace chromeos
353