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