1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsCOMPtr.h"
8 #include "nsTitleBarFrame.h"
9 #include "nsIContent.h"
10 #include "mozilla/dom/Document.h"
11 #include "nsGkAtoms.h"
12 #include "nsIWidget.h"
13 #include "nsMenuPopupFrame.h"
14 #include "nsPresContext.h"
15 #include "nsIDocShell.h"
16 #include "nsPIDOMWindow.h"
17 #include "nsDisplayList.h"
18 #include "nsContentUtils.h"
19 #include "mozilla/MouseEvents.h"
20 #include "mozilla/PresShell.h"
21 #include "mozilla/dom/MouseEventBinding.h"
22 
23 using namespace mozilla;
24 
25 //
26 // NS_NewTitleBarFrame
27 //
28 // Creates a new TitleBar frame and returns it
29 //
NS_NewTitleBarFrame(PresShell * aPresShell,ComputedStyle * aStyle)30 nsIFrame* NS_NewTitleBarFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
31   return new (aPresShell) nsTitleBarFrame(aStyle, aPresShell->GetPresContext());
32 }
33 
NS_IMPL_FRAMEARENA_HELPERS(nsTitleBarFrame)34 NS_IMPL_FRAMEARENA_HELPERS(nsTitleBarFrame)
35 
36 nsTitleBarFrame::nsTitleBarFrame(ComputedStyle* aStyle,
37                                  nsPresContext* aPresContext, ClassID aID)
38     : nsBoxFrame(aStyle, aPresContext, aID, false) {
39   mTrackingMouseMove = false;
40 }
41 
BuildDisplayListForChildren(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)42 void nsTitleBarFrame::BuildDisplayListForChildren(
43     nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
44   // override, since we don't want children to get events
45   if (aBuilder->IsForEventDelivery()) {
46     if (!mContent->AsElement()->AttrValueIs(kNameSpaceID_None,
47                                             nsGkAtoms::allowevents,
48                                             nsGkAtoms::_true, eCaseMatters))
49       return;
50   }
51   nsBoxFrame::BuildDisplayListForChildren(aBuilder, aLists);
52 }
53 
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)54 nsresult nsTitleBarFrame::HandleEvent(nsPresContext* aPresContext,
55                                       WidgetGUIEvent* aEvent,
56                                       nsEventStatus* aEventStatus) {
57   NS_ENSURE_ARG_POINTER(aEventStatus);
58   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
59     return NS_OK;
60   }
61 
62   bool doDefault = true;
63 
64   switch (aEvent->mMessage) {
65     case eMouseDown: {
66       if (aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) {
67         // titlebar has no effect in non-chrome shells
68         if (aPresContext->IsChrome()) {
69           // we're tracking.
70           mTrackingMouseMove = true;
71 
72           // start capture.
73           PresShell::SetCapturingContent(GetContent(),
74                                          CaptureFlags::IgnoreAllowedState);
75 
76           // remember current mouse coordinates.
77           mLastPoint = aEvent->mRefPoint;
78         }
79 
80         *aEventStatus = nsEventStatus_eConsumeNoDefault;
81         doDefault = false;
82       }
83     } break;
84 
85     case eMouseUp: {
86       if (mTrackingMouseMove &&
87           aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) {
88         // we're done tracking.
89         mTrackingMouseMove = false;
90 
91         // end capture
92         PresShell::ReleaseCapturingContent();
93 
94         *aEventStatus = nsEventStatus_eConsumeNoDefault;
95         doDefault = false;
96       }
97     } break;
98 
99     case eMouseMove: {
100       if (mTrackingMouseMove) {
101         LayoutDeviceIntPoint nsMoveBy = aEvent->mRefPoint - mLastPoint;
102 
103         nsIFrame* parent = GetParent();
104         while (parent) {
105           nsMenuPopupFrame* popupFrame = do_QueryFrame(parent);
106           if (popupFrame) break;
107           parent = parent->GetParent();
108         }
109 
110         // if the titlebar is in a popup, move the popup frame, otherwise
111         // move the widget associated with the window
112         if (parent) {
113           nsMenuPopupFrame* menuPopupFrame =
114               static_cast<nsMenuPopupFrame*>(parent);
115           nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
116           LayoutDeviceIntRect bounds = widget->GetScreenBounds();
117 
118           CSSPoint cssPos = (bounds.TopLeft() + nsMoveBy) /
119                             aPresContext->CSSToDevPixelScale();
120           menuPopupFrame->MoveTo(RoundedToInt(cssPos), false);
121         } else {
122           mozilla::PresShell* presShell = aPresContext->PresShell();
123           nsPIDOMWindowOuter* window = presShell->GetDocument()->GetWindow();
124           if (window) {
125             window->MoveBy(nsMoveBy.x, nsMoveBy.y);
126           }
127         }
128 
129         *aEventStatus = nsEventStatus_eConsumeNoDefault;
130 
131         doDefault = false;
132       }
133     } break;
134 
135     case eMouseClick: {
136       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
137       if (mouseEvent->IsLeftClickEvent()) {
138         MouseClicked(mouseEvent);
139       }
140       break;
141     }
142 
143     default:
144       break;
145   }
146 
147   if (doDefault)
148     return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
149   else
150     return NS_OK;
151 }
152 
MouseClicked(WidgetMouseEvent * aEvent)153 void nsTitleBarFrame::MouseClicked(WidgetMouseEvent* aEvent) {
154   // Execute the oncommand event handler.
155   nsCOMPtr<nsIContent> content = mContent;
156   nsContentUtils::DispatchXULCommand(content, false, nullptr, nullptr,
157                                      aEvent->IsControl(), aEvent->IsAlt(),
158                                      aEvent->IsShift(), aEvent->IsMeta(),
159                                      aEvent->mInputSource, aEvent->mButton);
160 }
161