• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/panels/panel_manager.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/ui/panels/detached_panel_collection.h"
14 #include "chrome/browser/ui/panels/docked_panel_collection.h"
15 #include "chrome/browser/ui/panels/panel_drag_controller.h"
16 #include "chrome/browser/ui/panels/panel_mouse_watcher.h"
17 #include "chrome/browser/ui/panels/panel_resize_controller.h"
18 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/chrome_version_info.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 
24 #if defined(TOOLKIT_GTK)
25 #include "base/environment.h"
26 #include "base/nix/xdg_util.h"
27 #include "ui/base/x/x11_util.h"
28 #endif
29 
30 #if defined(OS_WIN)
31 #include "win8/util/win8_util.h"
32 #endif
33 
34 namespace {
35 // Maxmium width of a panel is based on a factor of the working area.
36 #if defined(OS_CHROMEOS)
37 // ChromeOS device screens are relatively small and limiting the width
38 // interferes with some apps (e.g. http://crbug.com/111121).
39 const double kPanelMaxWidthFactor = 0.80;
40 #else
41 const double kPanelMaxWidthFactor = 0.35;
42 #endif
43 
44 // Maxmium height of a panel is based on a factor of the working area.
45 const double kPanelMaxHeightFactor = 0.5;
46 
47 // Width to height ratio is used to compute the default width or height
48 // when only one value is provided.
49 const double kPanelDefaultWidthToHeightRatio = 1.62;  // golden ratio
50 
51 // The test code could call PanelManager::SetDisplaySettingsProviderForTesting
52 // to set this for testing purpose.
53 DisplaySettingsProvider* display_settings_provider_for_testing;
54 
55 // The following comparers are used by std::list<>::sort to determine which
56 // stack or panel we want to seacrh first for adding new panel.
ComparePanelsByPosition(Panel * panel1,Panel * panel2)57 bool ComparePanelsByPosition(Panel* panel1, Panel* panel2) {
58   gfx::Rect bounds1 = panel1->GetBounds();
59   gfx::Rect bounds2 = panel2->GetBounds();
60 
61   // When there're ties, the right-most stack will appear first.
62   if (bounds1.x() > bounds2.x())
63     return true;
64   if (bounds1.x() < bounds2.x())
65     return false;
66 
67   // In the event of another draw, the top-most stack will appear first.
68   return bounds1.y() < bounds2.y();
69 }
70 
ComparerNumberOfPanelsInStack(StackedPanelCollection * stack1,StackedPanelCollection * stack2)71 bool ComparerNumberOfPanelsInStack(StackedPanelCollection* stack1,
72                                    StackedPanelCollection* stack2) {
73   // The stack with more panels will appear first.
74   int num_panels_in_stack1 = stack1->num_panels();
75   int num_panels_in_stack2 = stack2->num_panels();
76   if (num_panels_in_stack1 > num_panels_in_stack2)
77     return true;
78   if (num_panels_in_stack1 < num_panels_in_stack2)
79     return false;
80 
81   DCHECK(num_panels_in_stack1);
82 
83   return ComparePanelsByPosition(stack1->top_panel(), stack2->top_panel());
84 }
85 
CompareDetachedPanels(Panel * panel1,Panel * panel2)86 bool CompareDetachedPanels(Panel* panel1, Panel* panel2) {
87   return ComparePanelsByPosition(panel1, panel2);
88 }
89 
90 }  // namespace
91 
92 // static
93 bool PanelManager::shorten_time_intervals_ = false;
94 
95 // static
GetInstance()96 PanelManager* PanelManager::GetInstance() {
97   static base::LazyInstance<PanelManager> instance = LAZY_INSTANCE_INITIALIZER;
98   return instance.Pointer();
99 }
100 
101 // static
SetDisplaySettingsProviderForTesting(DisplaySettingsProvider * provider)102 void PanelManager::SetDisplaySettingsProviderForTesting(
103     DisplaySettingsProvider* provider) {
104   display_settings_provider_for_testing = provider;
105 }
106 
107 // static
ShouldUsePanels(const std::string & extension_id)108 bool PanelManager::ShouldUsePanels(const std::string& extension_id) {
109 #if defined(TOOLKIT_GTK)
110   // If --enable-panels is on, always use panels on Linux.
111   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePanels))
112     return true;
113 
114   // Otherwise, panels are only supported on tested window managers.
115   ui::WindowManagerName wm_type = ui::GuessWindowManager();
116   if (wm_type != ui::WM_COMPIZ &&
117       wm_type != ui::WM_ICE_WM &&
118       wm_type != ui::WM_KWIN &&
119       wm_type != ui::WM_METACITY &&
120       wm_type != ui::WM_MUFFIN &&
121       wm_type != ui::WM_MUTTER &&
122       wm_type != ui::WM_XFWM4) {
123     return false;
124   }
125 #endif  // TOOLKIT_GTK
126 
127 #if defined(OS_WIN)
128   // No panels in Metro mode.
129   if (win8::IsSingleWindowMetroMode())
130     return false;
131 #endif // OS_WIN
132 
133   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
134   if (channel == chrome::VersionInfo::CHANNEL_STABLE ||
135       channel == chrome::VersionInfo::CHANNEL_BETA) {
136     return CommandLine::ForCurrentProcess()->HasSwitch(
137         switches::kEnablePanels) ||
138         extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd") ||
139         extension_id == std::string("ljclpkphhpbpinifbeabbhlfddcpfdde") ||
140         extension_id == std::string("ppleadejekpmccmnpjdimmlfljlkdfej") ||
141         extension_id == std::string("eggnbpckecmjlblplehfpjjdhhidfdoj");
142   }
143 
144   return true;
145 }
146 
147 // static
IsPanelStackingEnabled()148 bool PanelManager::IsPanelStackingEnabled() {
149   return true;
150 }
151 
152 // static
CanUseSystemMinimize()153 bool PanelManager::CanUseSystemMinimize() {
154 #if defined(TOOLKIT_GTK)
155   static base::nix::DesktopEnvironment desktop_env =
156       base::nix::DESKTOP_ENVIRONMENT_OTHER;
157   if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_OTHER) {
158     scoped_ptr<base::Environment> env(base::Environment::Create());
159     desktop_env = base::nix::GetDesktopEnvironment(env.get());
160   }
161   return desktop_env != base::nix::DESKTOP_ENVIRONMENT_UNITY;
162 #else
163   return true;
164 #endif
165 }
166 
PanelManager()167 PanelManager::PanelManager()
168     : panel_mouse_watcher_(PanelMouseWatcher::Create()),
169       auto_sizing_enabled_(true) {
170   // DisplaySettingsProvider should be created before the creation of
171   // collections since some collection might depend on it.
172   if (display_settings_provider_for_testing)
173     display_settings_provider_.reset(display_settings_provider_for_testing);
174   else
175     display_settings_provider_.reset(DisplaySettingsProvider::Create());
176   display_settings_provider_->AddDisplayObserver(this);
177 
178   detached_collection_.reset(new DetachedPanelCollection(this));
179   docked_collection_.reset(new DockedPanelCollection(this));
180   drag_controller_.reset(new PanelDragController(this));
181   resize_controller_.reset(new PanelResizeController(this));
182 }
183 
~PanelManager()184 PanelManager::~PanelManager() {
185   display_settings_provider_->RemoveDisplayObserver(this);
186 
187   // Docked collection should be disposed explicitly before
188   // DisplaySettingsProvider is gone since docked collection needs to remove
189   // the observer from DisplaySettingsProvider.
190   docked_collection_.reset();
191 }
192 
GetDefaultDetachedPanelOrigin()193 gfx::Point PanelManager::GetDefaultDetachedPanelOrigin() {
194   return detached_collection_->GetDefaultPanelOrigin();
195 }
196 
OnDisplayChanged()197 void PanelManager::OnDisplayChanged() {
198   docked_collection_->OnDisplayChanged();
199   detached_collection_->OnDisplayChanged();
200   for (Stacks::const_iterator iter = stacks_.begin();
201        iter != stacks_.end(); iter++)
202     (*iter)->OnDisplayChanged();
203 }
204 
OnFullScreenModeChanged(bool is_full_screen)205 void PanelManager::OnFullScreenModeChanged(bool is_full_screen) {
206   std::vector<Panel*> all_panels = panels();
207   for (std::vector<Panel*>::const_iterator iter = all_panels.begin();
208        iter != all_panels.end(); ++iter) {
209     (*iter)->FullScreenModeChanged(is_full_screen);
210   }
211 }
212 
GetMaxPanelWidth(const gfx::Rect & work_area) const213 int PanelManager::GetMaxPanelWidth(const gfx::Rect& work_area) const {
214   return static_cast<int>(work_area.width() * kPanelMaxWidthFactor);
215 }
216 
GetMaxPanelHeight(const gfx::Rect & work_area) const217 int PanelManager::GetMaxPanelHeight(const gfx::Rect& work_area) const {
218   return static_cast<int>(work_area.height() * kPanelMaxHeightFactor);
219 }
220 
CreatePanel(const std::string & app_name,Profile * profile,const GURL & url,const gfx::Rect & requested_bounds,CreateMode mode)221 Panel* PanelManager::CreatePanel(const std::string& app_name,
222                                  Profile* profile,
223                                  const GURL& url,
224                                  const gfx::Rect& requested_bounds,
225                                  CreateMode mode) {
226   // Need to sync the display area if no panel is present. This is because:
227   // 1) Display area is not initialized until first panel is created.
228   // 2) On windows, display settings notification is tied to a window. When
229   //    display settings are changed at the time that no panel exists, we do
230   //    not receive any notification.
231   if (num_panels() == 0) {
232     display_settings_provider_->OnDisplaySettingsChanged();
233     display_settings_provider_->AddFullScreenObserver(this);
234   }
235 
236   // Compute initial bounds for the panel.
237   int width = requested_bounds.width();
238   int height = requested_bounds.height();
239   if (width == 0)
240     width = height * kPanelDefaultWidthToHeightRatio;
241   else if (height == 0)
242     height = width / kPanelDefaultWidthToHeightRatio;
243 
244   gfx::Rect work_area =
245       display_settings_provider_->GetWorkAreaMatching(requested_bounds);
246   gfx::Size min_size(panel::kPanelMinWidth, panel::kPanelMinHeight);
247   gfx::Size max_size(GetMaxPanelWidth(work_area), GetMaxPanelHeight(work_area));
248   if (width < min_size.width())
249     width = min_size.width();
250   else if (width > max_size.width())
251     width = max_size.width();
252 
253   if (height < min_size.height())
254     height = min_size.height();
255   else if (height > max_size.height())
256     height = max_size.height();
257 
258   // Create the panel.
259   Panel* panel = new Panel(profile, app_name, min_size, max_size);
260 
261   // Find the appropriate panel collection to hold the new panel.
262   gfx::Rect adjusted_requested_bounds(
263       requested_bounds.x(), requested_bounds.y(), width, height);
264   PanelCollection::PositioningMask positioning_mask;
265   PanelCollection* collection = GetCollectionForNewPanel(
266       panel, adjusted_requested_bounds, mode, &positioning_mask);
267 
268   // Let the panel collection decide the initial bounds.
269   gfx::Rect bounds = collection->GetInitialPanelBounds(
270       adjusted_requested_bounds);
271   bounds.AdjustToFit(work_area);
272 
273   panel->Initialize(url, bounds, collection->UsesAlwaysOnTopPanels());
274 
275   // Auto resizable feature is enabled only if no initial size is requested.
276   if (auto_sizing_enabled() && requested_bounds.width() == 0 &&
277       requested_bounds.height() == 0) {
278     panel->SetAutoResizable(true);
279   }
280 
281   // Add the panel to the panel collection.
282   collection->AddPanel(panel, positioning_mask);
283   collection->UpdatePanelOnCollectionChange(panel);
284 
285   return panel;
286 }
287 
GetCollectionForNewPanel(Panel * new_panel,const gfx::Rect & bounds,CreateMode mode,PanelCollection::PositioningMask * positioning_mask)288 PanelCollection* PanelManager::GetCollectionForNewPanel(
289     Panel* new_panel,
290     const gfx::Rect& bounds,
291     CreateMode mode,
292     PanelCollection::PositioningMask* positioning_mask) {
293   if (mode == CREATE_AS_DOCKED) {
294     // Delay layout refreshes in case multiple panels are created within
295     // a short time of one another or the focus changes shortly after panel
296     // is created to avoid excessive screen redraws.
297     *positioning_mask = PanelCollection::DELAY_LAYOUT_REFRESH;
298     return docked_collection_.get();
299   }
300 
301   DCHECK_EQ(CREATE_AS_DETACHED, mode);
302   *positioning_mask = PanelCollection::DEFAULT_POSITION;
303 
304   // If the stacking support is not enabled, new panel will still be created as
305   // detached.
306   if (!IsPanelStackingEnabled())
307     return detached_collection_.get();
308 
309   // If there're stacks, try to find a stack that can fit new panel.
310   if (!stacks_.empty()) {
311     // Perform the search as:
312     // 1) Search from the stack with more panels to the stack with least panels.
313     // 2) Amongs the stacks with same number of panels, search from the right-
314     //    most stack to the left-most stack.
315     // 3) Among the stack with same number of panels and same x position,
316     //    search from the top-most stack to the bottom-most stack.
317     // 4) If there is not enough space to fit new panel even with all inactive
318     //    panels being collapsed, move to next stack.
319     stacks_.sort(ComparerNumberOfPanelsInStack);
320     for (Stacks::const_iterator iter = stacks_.begin();
321          iter != stacks_.end(); iter++) {
322       StackedPanelCollection* stack = *iter;
323 
324       // Do not add to other stack that is from differnt extension or profile.
325       // Note that the check is based on bottom panel.
326       Panel* panel = stack->bottom_panel();
327       if (panel->profile() != new_panel->profile() ||
328           panel->extension_id() != new_panel->extension_id())
329         continue;
330 
331       // Do not add to the stack that is minimized by the system.
332       if (stack->IsMinimized())
333         continue;
334 
335       // Do not stack with the panel that is not shown in current virtual
336       // desktop.
337       if (!panel->IsShownOnActiveDesktop())
338         continue;
339 
340       if (bounds.height() <= stack->GetMaximiumAvailableBottomSpace()) {
341         *positioning_mask = static_cast<PanelCollection::PositioningMask>(
342             *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
343         return stack;
344       }
345     }
346   }
347 
348   // Then try to find a detached panel to which new panel can stack.
349   if (detached_collection_->num_panels()) {
350     // Perform the search as:
351     // 1) Search from the right-most detached panel to the left-most detached
352     //    panel.
353     // 2) Among the detached panels with same x position, search from the
354     //    top-most detached panel to the bottom-most deatched panel.
355     // 3) If there is not enough space beneath the detached panel, even by
356     //    collapsing it if it is inactive, to fit new panel, move to next
357     //    detached panel.
358     detached_collection_->SortPanels(CompareDetachedPanels);
359 
360     for (DetachedPanelCollection::Panels::const_iterator iter =
361              detached_collection_->panels().begin();
362          iter != detached_collection_->panels().end(); ++iter) {
363       Panel* panel = *iter;
364 
365       // Do not stack with other panel that is from differnt extension or
366       // profile.
367       if (panel->profile() != new_panel->profile() ||
368           panel->extension_id() != new_panel->extension_id())
369         continue;
370 
371       // Do not stack with the panel that is minimized by the system.
372       if (panel->IsMinimizedBySystem())
373         continue;
374 
375       // Do not stack with the panel that is not shown in the active desktop.
376       if (!panel->IsShownOnActiveDesktop())
377         continue;
378 
379       gfx::Rect work_area =
380           display_settings_provider_->GetWorkAreaMatching(panel->GetBounds());
381       int max_available_space =
382           work_area.bottom() - panel->GetBounds().y() -
383           (panel->IsActive() ? panel->GetBounds().height()
384                              : panel::kTitlebarHeight);
385       if (bounds.height() <= max_available_space) {
386         StackedPanelCollection* new_stack = CreateStack();
387         MovePanelToCollection(panel,
388                               new_stack,
389                               PanelCollection::DEFAULT_POSITION);
390         *positioning_mask = static_cast<PanelCollection::PositioningMask>(
391             *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
392         return new_stack;
393       }
394     }
395   }
396 
397   return detached_collection_.get();
398 }
399 
OnPanelClosed(Panel * panel)400 void PanelManager::OnPanelClosed(Panel* panel) {
401   if (num_panels() == 1) {
402     display_settings_provider_->RemoveFullScreenObserver(this);
403   }
404 
405   drag_controller_->OnPanelClosed(panel);
406   resize_controller_->OnPanelClosed(panel);
407 
408   // Note that we need to keep track of panel's collection since it will be
409   // gone once RemovePanel is called.
410   PanelCollection* collection = panel->collection();
411   collection->RemovePanel(panel, PanelCollection::PANEL_CLOSED);
412 
413   // If only one panel is left in the stack, move it out of the stack.
414   // Also make sure that this detached panel will be expanded if not yet.
415   if (collection->type() == PanelCollection::STACKED) {
416     StackedPanelCollection* stack =
417         static_cast<StackedPanelCollection*>(collection);
418     DCHECK_GE(stack->num_panels(), 1);
419     if (stack->num_panels() == 1) {
420       Panel* top_panel = stack->top_panel();
421       MovePanelToCollection(top_panel,
422                             detached_collection(),
423                             PanelCollection::DEFAULT_POSITION);
424       if (top_panel->expansion_state() != Panel::EXPANDED)
425         top_panel->SetExpansionState(Panel::EXPANDED);
426       RemoveStack(stack);
427     }
428   }
429 
430   content::NotificationService::current()->Notify(
431       chrome::NOTIFICATION_PANEL_CLOSED,
432       content::Source<Panel>(panel),
433       content::NotificationService::NoDetails());
434 }
435 
CreateStack()436 StackedPanelCollection* PanelManager::CreateStack() {
437   StackedPanelCollection* stack = new StackedPanelCollection(this);
438   stacks_.push_back(stack);
439   return stack;
440 }
441 
RemoveStack(StackedPanelCollection * stack)442 void PanelManager::RemoveStack(StackedPanelCollection* stack) {
443   DCHECK_EQ(0, stack->num_panels());
444   stacks_.remove(stack);
445   stack->CloseAll();
446   delete stack;
447 }
448 
StartDragging(Panel * panel,const gfx::Point & mouse_location)449 void PanelManager::StartDragging(Panel* panel,
450                                  const gfx::Point& mouse_location) {
451   drag_controller_->StartDragging(panel, mouse_location);
452 }
453 
Drag(const gfx::Point & mouse_location)454 void PanelManager::Drag(const gfx::Point& mouse_location) {
455   drag_controller_->Drag(mouse_location);
456 }
457 
EndDragging(bool cancelled)458 void PanelManager::EndDragging(bool cancelled) {
459   drag_controller_->EndDragging(cancelled);
460 }
461 
StartResizingByMouse(Panel * panel,const gfx::Point & mouse_location,panel::ResizingSides sides)462 void PanelManager::StartResizingByMouse(Panel* panel,
463     const gfx::Point& mouse_location,
464     panel::ResizingSides sides) {
465   if (panel->CanResizeByMouse() != panel::NOT_RESIZABLE &&
466       sides != panel::RESIZE_NONE)
467     resize_controller_->StartResizing(panel, mouse_location, sides);
468 }
469 
ResizeByMouse(const gfx::Point & mouse_location)470 void PanelManager::ResizeByMouse(const gfx::Point& mouse_location) {
471   if (resize_controller_->IsResizing())
472     resize_controller_->Resize(mouse_location);
473 }
474 
EndResizingByMouse(bool cancelled)475 void PanelManager::EndResizingByMouse(bool cancelled) {
476   if (resize_controller_->IsResizing()) {
477     Panel* resized_panel = resize_controller_->EndResizing(cancelled);
478     if (!cancelled && resized_panel->collection())
479       resized_panel->collection()->RefreshLayout();
480   }
481 }
482 
OnPanelExpansionStateChanged(Panel * panel)483 void PanelManager::OnPanelExpansionStateChanged(Panel* panel) {
484   panel->collection()->OnPanelExpansionStateChanged(panel);
485 }
486 
MovePanelToCollection(Panel * panel,PanelCollection * target_collection,PanelCollection::PositioningMask positioning_mask)487 void PanelManager::MovePanelToCollection(
488     Panel* panel,
489     PanelCollection* target_collection,
490     PanelCollection::PositioningMask positioning_mask) {
491   DCHECK(panel);
492   PanelCollection* current_collection = panel->collection();
493   DCHECK(current_collection);
494   DCHECK_NE(current_collection, target_collection);
495   current_collection->RemovePanel(panel,
496                                   PanelCollection::PANEL_CHANGED_COLLECTION);
497 
498   target_collection->AddPanel(panel, positioning_mask);
499   target_collection->UpdatePanelOnCollectionChange(panel);
500   panel->SetAlwaysOnTop(target_collection->UsesAlwaysOnTopPanels());
501 }
502 
ShouldBringUpTitlebars(int mouse_x,int mouse_y) const503 bool PanelManager::ShouldBringUpTitlebars(int mouse_x, int mouse_y) const {
504   return docked_collection_->ShouldBringUpTitlebars(mouse_x, mouse_y);
505 }
506 
BringUpOrDownTitlebars(bool bring_up)507 void PanelManager::BringUpOrDownTitlebars(bool bring_up) {
508   docked_collection_->BringUpOrDownTitlebars(bring_up);
509 }
510 
CloseAll()511 void PanelManager::CloseAll() {
512   DCHECK(!drag_controller_->is_dragging());
513 
514   detached_collection_->CloseAll();
515   docked_collection_->CloseAll();
516 }
517 
num_panels() const518 int PanelManager::num_panels() const {
519   int count = detached_collection_->num_panels() +
520               docked_collection_->num_panels();
521   for (Stacks::const_iterator iter = stacks_.begin();
522        iter != stacks_.end(); iter++)
523     count += (*iter)->num_panels();
524   return count;
525 }
526 
panels() const527 std::vector<Panel*> PanelManager::panels() const {
528   std::vector<Panel*> panels;
529   for (DetachedPanelCollection::Panels::const_iterator iter =
530            detached_collection_->panels().begin();
531        iter != detached_collection_->panels().end(); ++iter)
532     panels.push_back(*iter);
533   for (DockedPanelCollection::Panels::const_iterator iter =
534            docked_collection_->panels().begin();
535        iter != docked_collection_->panels().end(); ++iter)
536     panels.push_back(*iter);
537   for (Stacks::const_iterator stack_iter = stacks_.begin();
538        stack_iter != stacks_.end(); stack_iter++) {
539     for (StackedPanelCollection::Panels::const_iterator iter =
540              (*stack_iter)->panels().begin();
541          iter != (*stack_iter)->panels().end(); ++iter) {
542       panels.push_back(*iter);
543     }
544   }
545   return panels;
546 }
547 
GetDetachedAndStackedPanels() const548 std::vector<Panel*> PanelManager::GetDetachedAndStackedPanels() const {
549   std::vector<Panel*> panels;
550   for (DetachedPanelCollection::Panels::const_iterator iter =
551            detached_collection_->panels().begin();
552        iter != detached_collection_->panels().end(); ++iter)
553     panels.push_back(*iter);
554   for (Stacks::const_iterator stack_iter = stacks_.begin();
555        stack_iter != stacks_.end(); stack_iter++) {
556     for (StackedPanelCollection::Panels::const_iterator iter =
557              (*stack_iter)->panels().begin();
558          iter != (*stack_iter)->panels().end(); ++iter) {
559       panels.push_back(*iter);
560     }
561   }
562   return panels;
563 }
564 
SetMouseWatcher(PanelMouseWatcher * watcher)565 void PanelManager::SetMouseWatcher(PanelMouseWatcher* watcher) {
566   panel_mouse_watcher_.reset(watcher);
567 }
568 
OnPanelAnimationEnded(Panel * panel)569 void PanelManager::OnPanelAnimationEnded(Panel* panel) {
570   content::NotificationService::current()->Notify(
571       chrome::NOTIFICATION_PANEL_BOUNDS_ANIMATIONS_FINISHED,
572       content::Source<Panel>(panel),
573       content::NotificationService::NoDetails());
574 }
575