[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / panels / panel_cocoa.mm
blob9ab696e3355fd06cb4da613fae56e597e8221916
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.
5 #include "chrome/browser/ui/cocoa/panels/panel_cocoa.h"
7 #include "base/logging.h"
8 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
9 #import "chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.h"
10 #import "chrome/browser/ui/cocoa/panels/panel_utils_cocoa.h"
11 #import "chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h"
12 #include "chrome/browser/ui/panels/panel.h"
13 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
14 #include "content/public/browser/native_web_keyboard_event.h"
16 using content::NativeWebKeyboardEvent;
17 using content::WebContents;
19 // This creates a shim window class, which in turn creates a Cocoa window
20 // controller which in turn creates actual NSWindow by loading a nib.
21 // Overall chain of ownership is:
22 // PanelWindowControllerCocoa -> PanelCocoa -> Panel.
23 // static
24 NativePanel* Panel::CreateNativePanel(Panel* panel,
25                                       const gfx::Rect& bounds,
26                                       bool always_on_top) {
27   return new PanelCocoa(panel, bounds, always_on_top);
30 PanelCocoa::PanelCocoa(Panel* panel,
31                        const gfx::Rect& bounds,
32                        bool always_on_top)
33     : panel_(panel),
34       bounds_(bounds),
35       always_on_top_(always_on_top),
36       is_shown_(false),
37       attention_request_id_(0),
38       corner_style_(panel::ALL_ROUNDED) {
39   controller_ = [[PanelWindowControllerCocoa alloc] initWithPanel:this];
42 PanelCocoa::~PanelCocoa() {
45 bool PanelCocoa::IsClosed() const {
46   return !controller_;
49 void PanelCocoa::ShowPanel() {
50   ShowPanelInactive();
51   ActivatePanel();
53   // |-makeKeyAndOrderFront:| won't send |-windowDidBecomeKey:| until we
54   // return to the runloop. This causes extension tests that wait for the
55   // active status change notification to fail, so we send an active status
56   // notification here.
57   panel_->OnActiveStateChanged(true);
60 void PanelCocoa::ShowPanelInactive() {
61   if (IsClosed())
62     return;
64   // This method may be called several times, meaning 'ensure it's shown'.
65   // Animations don't look good when repeated - hence this flag is needed.
66   if (is_shown_) {
67     return;
68   }
69   // A call to SetPanelBounds() before showing it is required to set
70   // the panel frame properly.
71   SetPanelBoundsInstantly(bounds_);
72   is_shown_ = true;
74   NSRect finalFrame = cocoa_utils::ConvertRectToCocoaCoordinates(bounds_);
75   [controller_ revealAnimatedWithFrame:finalFrame];
78 gfx::Rect PanelCocoa::GetPanelBounds() const {
79   return bounds_;
82 // |bounds| is the platform-independent screen coordinates, with (0,0) at
83 // top-left of the primary screen.
84 void PanelCocoa::SetPanelBounds(const gfx::Rect& bounds) {
85   setBoundsInternal(bounds, true);
88 void PanelCocoa::SetPanelBoundsInstantly(const gfx::Rect& bounds) {
89   setBoundsInternal(bounds, false);
92 void PanelCocoa::setBoundsInternal(const gfx::Rect& bounds, bool animate) {
93   // We will call SetPanelBoundsInstantly() once before showing the panel
94   // and it should set the panel frame correctly.
95   if (bounds_ == bounds && is_shown_)
96     return;
98   bounds_ = bounds;
100   // Safely ignore calls to animate bounds before the panel is shown to
101   // prevent the window from loading prematurely.
102   if (animate && !is_shown_)
103     return;
105   NSRect frame = cocoa_utils::ConvertRectToCocoaCoordinates(bounds);
106   [controller_ setPanelFrame:frame animate:animate];
109 void PanelCocoa::ClosePanel() {
110   if (IsClosed())
111       return;
113   NSWindow* window = [controller_ window];
114   // performClose: contains a nested message loop which can cause reentrancy
115   // if the browser is terminating and closing all the windows.
116   // Use this version that corresponds to protocol of performClose: but does not
117   // spin a nested loop.
118   // TODO(dimich): refactor similar method from BWC and reuse here.
119   if ([controller_ windowShouldClose:window]) {
120     // Make sure that the panel window is not associated with the underlying
121     // stack window because otherwise hiding the panel window could cause all
122     // other panel windows in the same stack to disappear.
123     NSWindow* stackWindow = [window parentWindow];
124     if (stackWindow)
125       [stackWindow removeChildWindow:window];
127     [window orderOut:nil];
128     [window close];
129   }
132 void PanelCocoa::ActivatePanel() {
133   if (!is_shown_)
134     return;
136   [controller_ activate];
139 void PanelCocoa::DeactivatePanel() {
140   [controller_ deactivate];
143 bool PanelCocoa::IsPanelActive() const {
144   // TODO(dcheng): It seems like a lot of these methods can be called before
145   // our NSWindow is created. Do we really need to check in every one of these
146   // methods if the NSWindow is created, or is there a better way to
147   // gracefully handle this?
148   if (!is_shown_)
149     return false;
150   return [[controller_ window] isMainWindow];
153 void PanelCocoa::PreventActivationByOS(bool prevent_activation) {
154   [controller_ preventBecomingKeyWindow:prevent_activation];
155   return;
158 gfx::NativeWindow PanelCocoa::GetNativePanelWindow() {
159   return [controller_ window];
162 void PanelCocoa::UpdatePanelTitleBar() {
163   if (!is_shown_)
164     return;
165   [controller_ updateTitleBar];
168 void PanelCocoa::UpdatePanelLoadingAnimations(bool should_animate) {
169   [controller_ updateThrobber:should_animate];
172 void PanelCocoa::PanelWebContentsFocused(content::WebContents* contents) {
173   // Nothing to do.
176 void PanelCocoa::PanelCut() {
177   // Nothing to do since we do not have panel-specific system menu on Mac.
180 void PanelCocoa::PanelCopy() {
181   // Nothing to do since we do not have panel-specific system menu on Mac.
184 void PanelCocoa::PanelPaste() {
185   // Nothing to do since we do not have panel-specific system menu on Mac.
188 void PanelCocoa::DrawAttention(bool draw_attention) {
189   DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0);
191   PanelTitlebarViewCocoa* titlebar = [controller_ titlebarView];
192   if (draw_attention)
193     [titlebar drawAttention];
194   else
195     [titlebar stopDrawingAttention];
197   if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) {
198     if (draw_attention) {
199       DCHECK(!attention_request_id_);
200       attention_request_id_ = [NSApp requestUserAttention:NSCriticalRequest];
201     } else {
202       [NSApp cancelUserAttentionRequest:attention_request_id_];
203       attention_request_id_ = 0;
204     }
205   }
208 bool PanelCocoa::IsDrawingAttention() const {
209   PanelTitlebarViewCocoa* titlebar = [controller_ titlebarView];
210   return [titlebar isDrawingAttention];
213 void PanelCocoa::HandlePanelKeyboardEvent(
214     const NativeWebKeyboardEvent& event) {
215   if (event.skip_in_browser || event.type == NativeWebKeyboardEvent::Char)
216     return;
218   ChromeEventProcessingWindow* event_window =
219       static_cast<ChromeEventProcessingWindow*>([controller_ window]);
220   DCHECK([event_window isKindOfClass:[ChromeEventProcessingWindow class]]);
221   [event_window redispatchKeyEvent:event.os_event];
224 void PanelCocoa::FullScreenModeChanged(bool is_full_screen) {
225   if (!is_shown_) {
226     // If the panel window is not shown due to that a Chrome tab window is in
227     // fullscreen mode when the panel is being created, we need to show the
228     // panel window now. In addition, its titlebar needs to be updated since it
229     // is not done at the panel creation time.
230     if (!is_full_screen) {
231       ShowPanelInactive();
232       UpdatePanelTitleBar();
233     }
235     // No need to proceed when the panel window was not shown previously.
236     // We either show the panel window or do not show it depending on current
237     // full screen state.
238     return;
239   }
240   [controller_ fullScreenModeChanged:is_full_screen];
243 bool PanelCocoa::IsPanelAlwaysOnTop() const {
244   return always_on_top_;
247 void PanelCocoa::SetPanelAlwaysOnTop(bool on_top) {
248   if (always_on_top_ == on_top)
249     return;
250   always_on_top_ = on_top;
251   [controller_ updateWindowLevel];
252   [controller_ updateWindowCollectionBehavior];
255 void PanelCocoa::UpdatePanelMinimizeRestoreButtonVisibility() {
256   [controller_ updateTitleBarMinimizeRestoreButtonVisibility];
259 void PanelCocoa::SetWindowCornerStyle(panel::CornerStyle corner_style) {
260   corner_style_ = corner_style;
262   // TODO(dimich): investigate how to support it on Mac.
265 void PanelCocoa::MinimizePanelBySystem() {
266   [controller_ miniaturize];
269 bool PanelCocoa::IsPanelMinimizedBySystem() const {
270   return [controller_ isMiniaturized];
273 bool PanelCocoa::IsPanelShownOnActiveDesktop() const {
274   return [[controller_ window] isOnActiveSpace];
277 void PanelCocoa::ShowShadow(bool show) {
278   [controller_ showShadow:show];
281 void PanelCocoa::PanelExpansionStateChanging(
282     Panel::ExpansionState old_state, Panel::ExpansionState new_state) {
283   [controller_ updateWindowLevel:(new_state != Panel::EXPANDED)];
286 void PanelCocoa::AttachWebContents(content::WebContents* contents) {
287   [controller_ webContentsInserted:contents];
290 void PanelCocoa::DetachWebContents(content::WebContents* contents) {
291   [controller_ webContentsDetached:contents];
294 gfx::Size PanelCocoa::WindowSizeFromContentSize(
295     const gfx::Size& content_size) const {
296   NSRect content = NSMakeRect(0, 0,
297                               content_size.width(), content_size.height());
298   NSRect frame = [controller_ frameRectForContentRect:content];
299   return gfx::Size(NSWidth(frame), NSHeight(frame));
302 gfx::Size PanelCocoa::ContentSizeFromWindowSize(
303     const gfx::Size& window_size) const {
304   NSRect frame = NSMakeRect(0, 0, window_size.width(), window_size.height());
305   NSRect content = [controller_ contentRectForFrameRect:frame];
306   return gfx::Size(NSWidth(content), NSHeight(content));
309 int PanelCocoa::TitleOnlyHeight() const {
310   return [controller_ titlebarHeightInScreenCoordinates];
313 Panel* PanelCocoa::panel() const {
314   return panel_.get();
317 void PanelCocoa::DidCloseNativeWindow() {
318   DCHECK(!IsClosed());
319   controller_ = NULL;
320   panel_->OnNativePanelClosed();
323 // NativePanelTesting implementation.
324 class CocoaNativePanelTesting : public NativePanelTesting {
325  public:
326   CocoaNativePanelTesting(NativePanel* native_panel);
327   virtual ~CocoaNativePanelTesting() { }
328   // Overridden from NativePanelTesting
329   virtual void PressLeftMouseButtonTitlebar(
330       const gfx::Point& mouse_location, panel::ClickModifier modifier) OVERRIDE;
331   virtual void ReleaseMouseButtonTitlebar(
332       panel::ClickModifier modifier) OVERRIDE;
333   virtual void DragTitlebar(const gfx::Point& mouse_location) OVERRIDE;
334   virtual void CancelDragTitlebar() OVERRIDE;
335   virtual void FinishDragTitlebar() OVERRIDE;
336   virtual bool VerifyDrawingAttention() const OVERRIDE;
337   virtual bool VerifyActiveState(bool is_active) OVERRIDE;
338   virtual bool VerifyAppIcon() const OVERRIDE;
339   virtual bool VerifySystemMinimizeState() const OVERRIDE;
340   virtual bool IsWindowVisible() const OVERRIDE;
341   virtual bool IsWindowSizeKnown() const OVERRIDE;
342   virtual bool IsAnimatingBounds() const OVERRIDE;
343   virtual bool IsButtonVisible(
344       panel::TitlebarButtonType button_type) const OVERRIDE;
345   virtual panel::CornerStyle GetWindowCornerStyle() const OVERRIDE;
346   virtual bool EnsureApplicationRunOnForeground() OVERRIDE;
348  private:
349   PanelTitlebarViewCocoa* titlebar() const;
350   // Weak, assumed always to outlive this test API object.
351   PanelCocoa* native_panel_window_;
354 NativePanelTesting* PanelCocoa::CreateNativePanelTesting() {
355   return new CocoaNativePanelTesting(this);
358 CocoaNativePanelTesting::CocoaNativePanelTesting(NativePanel* native_panel)
359   : native_panel_window_(static_cast<PanelCocoa*>(native_panel)) {
362 PanelTitlebarViewCocoa* CocoaNativePanelTesting::titlebar() const {
363   return [native_panel_window_->controller_ titlebarView];
366 void CocoaNativePanelTesting::PressLeftMouseButtonTitlebar(
367     const gfx::Point& mouse_location, panel::ClickModifier modifier) {
368   // Convert from platform-indepedent screen coordinates to Cocoa's screen
369   // coordinates because PanelTitlebarViewCocoa method takes Cocoa's screen
370   // coordinates.
371   int modifierFlags =
372       (modifier == panel::APPLY_TO_ALL ? NSShiftKeyMask : 0);
373   [titlebar() pressLeftMouseButtonTitlebar:
374       cocoa_utils::ConvertPointToCocoaCoordinates(mouse_location)
375            modifiers:modifierFlags];
378 void CocoaNativePanelTesting::ReleaseMouseButtonTitlebar(
379     panel::ClickModifier modifier) {
380   int modifierFlags =
381       (modifier == panel::APPLY_TO_ALL ? NSShiftKeyMask : 0);
382   [titlebar() releaseLeftMouseButtonTitlebar:modifierFlags];
385 void CocoaNativePanelTesting::DragTitlebar(const gfx::Point& mouse_location) {
386   // Convert from platform-indepedent screen coordinates to Cocoa's screen
387   // coordinates because PanelTitlebarViewCocoa method takes Cocoa's screen
388   // coordinates.
389   [titlebar() dragTitlebar:
390       cocoa_utils::ConvertPointToCocoaCoordinates(mouse_location)];
393 void CocoaNativePanelTesting::CancelDragTitlebar() {
394   [titlebar() cancelDragTitlebar];
397 void CocoaNativePanelTesting::FinishDragTitlebar() {
398   [titlebar() finishDragTitlebar];
401 bool CocoaNativePanelTesting::VerifyDrawingAttention() const {
402   return [titlebar() isDrawingAttention];
405 bool CocoaNativePanelTesting::VerifyActiveState(bool is_active) {
406   // TODO(jianli): to be implemented.
407   return false;
410 bool CocoaNativePanelTesting::VerifyAppIcon() const {
411   // Nothing to do since panel does not show dock icon.
412   return true;
415 bool CocoaNativePanelTesting::VerifySystemMinimizeState() const {
416   // TODO(jianli): to be implemented.
417   return true;
420 bool CocoaNativePanelTesting::IsWindowVisible() const {
421   return [[native_panel_window_->controller_ window] isVisible];
424 bool CocoaNativePanelTesting::IsWindowSizeKnown() const {
425   return true;
428 bool CocoaNativePanelTesting::IsAnimatingBounds() const {
429   if ([native_panel_window_->controller_ isAnimatingBounds])
430     return true;
431   StackedPanelCollection* stack = native_panel_window_->panel()->stack();
432   if (!stack)
433     return false;
434   return stack->IsAnimatingPanelBounds(native_panel_window_->panel());
437 bool CocoaNativePanelTesting::IsButtonVisible(
438     panel::TitlebarButtonType button_type) const {
439   switch (button_type) {
440     case panel::CLOSE_BUTTON:
441       return ![[titlebar() closeButton] isHidden];
442     case panel::MINIMIZE_BUTTON:
443       return ![[titlebar() minimizeButton] isHidden];
444     case panel::RESTORE_BUTTON:
445       return ![[titlebar() restoreButton] isHidden];
446     default:
447       NOTREACHED();
448   }
449   return false;
452 panel::CornerStyle CocoaNativePanelTesting::GetWindowCornerStyle() const {
453   return native_panel_window_->corner_style_;
456 bool CocoaNativePanelTesting::EnsureApplicationRunOnForeground() {
457   if ([NSApp isActive])
458     return true;
459   [NSApp activateIgnoringOtherApps:YES];
460   return [NSApp isActive];