Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / base / VisualViewport.cpp
blob39dfb6ed88b19f4adccfd8d473d8d51cc06921e2
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/. */
7 #include "VisualViewport.h"
9 #include "mozilla/EventDispatcher.h"
10 #include "mozilla/PresShell.h"
11 #include "mozilla/ScrollContainerFrame.h"
12 #include "mozilla/ToString.h"
13 #include "nsIDocShell.h"
14 #include "nsGlobalWindowInner.h"
15 #include "nsPresContext.h"
16 #include "nsRefreshDriver.h"
17 #include "DocumentInlines.h"
19 static mozilla::LazyLogModule sVvpLog("visualviewport");
20 #define VVP_LOG(...) MOZ_LOG(sVvpLog, LogLevel::Debug, (__VA_ARGS__))
22 using namespace mozilla;
23 using namespace mozilla::dom;
25 VisualViewport::VisualViewport(nsPIDOMWindowInner* aWindow)
26 : DOMEventTargetHelper(aWindow) {}
28 VisualViewport::~VisualViewport() {
29 if (mResizeEvent) {
30 mResizeEvent->Revoke();
33 if (mScrollEvent) {
34 mScrollEvent->Revoke();
38 /* virtual */
39 JSObject* VisualViewport::WrapObject(JSContext* aCx,
40 JS::Handle<JSObject*> aGivenProto) {
41 return VisualViewport_Binding::Wrap(aCx, this, aGivenProto);
44 /* virtual */
45 void VisualViewport::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
46 EventMessage msg = aVisitor.mEvent->mMessage;
48 aVisitor.mCanHandle = true;
49 EventTarget* parentTarget = nullptr;
50 // Only our special internal events are allowed to escape the
51 // Visual Viewport and be dispatched further up the DOM tree.
52 if (msg == eMozVisualScroll || msg == eMozVisualResize) {
53 if (nsPIDOMWindowInner* win = GetOwnerWindow()) {
54 if (Document* doc = win->GetExtantDoc()) {
55 parentTarget = doc;
59 aVisitor.SetParentTarget(parentTarget, false);
62 CSSSize VisualViewport::VisualViewportSize() const {
63 CSSSize size = CSSSize(0, 0);
65 // Flush layout, as that may affect the answer below (e.g. scrollbars
66 // may have appeared, decreasing the available viewport size).
67 RefPtr<const VisualViewport> kungFuDeathGrip(this);
68 if (Document* doc = GetDocument()) {
69 doc->FlushPendingNotifications(FlushType::Layout);
72 // Fetch the pres shell after the layout flush, as it might have destroyed it.
73 if (PresShell* presShell = GetPresShell()) {
74 if (presShell->IsVisualViewportSizeSet()) {
75 DynamicToolbarState state = presShell->GetDynamicToolbarState();
76 size = CSSRect::FromAppUnits(
77 (state == DynamicToolbarState::InTransition ||
78 state == DynamicToolbarState::Collapsed)
79 ? presShell->GetVisualViewportSizeUpdatedByDynamicToolbar()
80 : presShell->GetVisualViewportSize());
81 } else {
82 ScrollContainerFrame* sf = presShell->GetRootScrollContainerFrame();
83 if (sf) {
84 size = CSSRect::FromAppUnits(sf->GetScrollPortRect().Size());
88 return size;
91 double VisualViewport::Width() const {
92 CSSSize size = VisualViewportSize();
93 return size.width;
96 double VisualViewport::Height() const {
97 CSSSize size = VisualViewportSize();
98 return size.height;
101 double VisualViewport::Scale() const {
102 double scale = 1;
103 if (PresShell* presShell = GetPresShell()) {
104 scale = presShell->GetResolution();
106 return scale;
109 CSSPoint VisualViewport::VisualViewportOffset() const {
110 CSSPoint offset = CSSPoint(0, 0);
112 if (PresShell* presShell = GetPresShell()) {
113 offset = CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset());
115 return offset;
118 CSSPoint VisualViewport::LayoutViewportOffset() const {
119 CSSPoint offset = CSSPoint(0, 0);
121 if (PresShell* presShell = GetPresShell()) {
122 offset = CSSPoint::FromAppUnits(presShell->GetLayoutViewportOffset());
124 return offset;
127 double VisualViewport::PageLeft() const { return VisualViewportOffset().X(); }
129 double VisualViewport::PageTop() const { return VisualViewportOffset().Y(); }
131 double VisualViewport::OffsetLeft() const {
132 return PageLeft() - LayoutViewportOffset().X();
135 double VisualViewport::OffsetTop() const {
136 return PageTop() - LayoutViewportOffset().Y();
139 Document* VisualViewport::GetDocument() const {
140 nsCOMPtr<nsPIDOMWindowInner> window = GetOwnerWindow();
141 if (!window) {
142 return nullptr;
145 nsIDocShell* docShell = window->GetDocShell();
146 if (!docShell) {
147 return nullptr;
150 return docShell->GetDocument();
153 PresShell* VisualViewport::GetPresShell() const {
154 RefPtr<Document> document = GetDocument();
155 return document ? document->GetPresShell() : nullptr;
158 nsPresContext* VisualViewport::GetPresContext() const {
159 RefPtr<Document> document = GetDocument();
160 return document ? document->GetPresContext() : nullptr;
163 /* ================= Resize event handling ================= */
165 void VisualViewport::PostResizeEvent() {
166 VVP_LOG("%p: PostResizeEvent (pre-existing: %d)\n", this, !!mResizeEvent);
167 nsPresContext* presContext = GetPresContext();
168 if (mResizeEvent && mResizeEvent->HasPresContext(presContext)) {
169 return;
171 if (mResizeEvent) {
172 // prescontext changed, so discard the old resize event and queue a new one
173 mResizeEvent->Revoke();
174 mResizeEvent = nullptr;
177 // The event constructor will register itself with the refresh driver.
178 if (presContext) {
179 mResizeEvent = new VisualViewportResizeEvent(this, presContext);
180 VVP_LOG("%p: PostResizeEvent, created new event\n", this);
184 VisualViewport::VisualViewportResizeEvent::VisualViewportResizeEvent(
185 VisualViewport* aViewport, nsPresContext* aPresContext)
186 : Runnable("VisualViewport::VisualViewportResizeEvent"),
187 mViewport(aViewport),
188 mPresContext(aPresContext) {
189 VVP_LOG("%p: Registering PostResize on %p %p\n", aViewport, aPresContext,
190 aPresContext->RefreshDriver());
191 aPresContext->RefreshDriver()->PostVisualViewportResizeEvent(this);
194 bool VisualViewport::VisualViewportResizeEvent::HasPresContext(
195 nsPresContext* aContext) const {
196 return mPresContext.get() == aContext;
199 void VisualViewport::VisualViewportResizeEvent::Revoke() {
200 mViewport = nullptr;
201 mPresContext = nullptr;
204 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
205 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
206 VisualViewport::VisualViewportResizeEvent::Run() {
207 if (RefPtr<VisualViewport> viewport = mViewport) {
208 viewport->FireResizeEvent();
210 return NS_OK;
213 void VisualViewport::FireResizeEvent() {
214 MOZ_ASSERT(mResizeEvent);
215 mResizeEvent->Revoke();
216 mResizeEvent = nullptr;
218 RefPtr<nsPresContext> presContext = GetPresContext();
220 VVP_LOG("%p, FireResizeEvent, fire mozvisualresize\n", this);
221 WidgetEvent mozEvent(true, eMozVisualResize);
222 mozEvent.mFlags.mOnlySystemGroupDispatch = true;
223 EventDispatcher::Dispatch(this, presContext, &mozEvent);
225 VVP_LOG("%p, FireResizeEvent, fire VisualViewport resize\n", this);
226 WidgetEvent event(true, eResize);
227 event.mFlags.mBubbles = false;
228 event.mFlags.mCancelable = false;
229 EventDispatcher::Dispatch(this, presContext, &event);
232 /* ================= Scroll event handling ================= */
234 void VisualViewport::PostScrollEvent(const nsPoint& aPrevVisualOffset,
235 const nsPoint& aPrevLayoutOffset) {
236 VVP_LOG("%p: PostScrollEvent, prevRelativeOffset=%s (pre-existing: %d)\n",
237 this, ToString(aPrevVisualOffset - aPrevLayoutOffset).c_str(),
238 !!mScrollEvent);
239 nsPresContext* presContext = GetPresContext();
240 if (mScrollEvent && mScrollEvent->HasPresContext(presContext)) {
241 return;
244 if (mScrollEvent) {
245 // prescontext changed, so discard the old scroll event and queue a new one
246 mScrollEvent->Revoke();
247 mScrollEvent = nullptr;
250 // The event constructor will register itself with the refresh driver.
251 if (presContext) {
252 mScrollEvent = new VisualViewportScrollEvent(
253 this, presContext, aPrevVisualOffset, aPrevLayoutOffset);
254 VVP_LOG("%p: PostScrollEvent, created new event\n", this);
258 VisualViewport::VisualViewportScrollEvent::VisualViewportScrollEvent(
259 VisualViewport* aViewport, nsPresContext* aPresContext,
260 const nsPoint& aPrevVisualOffset, const nsPoint& aPrevLayoutOffset)
261 : Runnable("VisualViewport::VisualViewportScrollEvent"),
262 mViewport(aViewport),
263 mPresContext(aPresContext),
264 mPrevVisualOffset(aPrevVisualOffset),
265 mPrevLayoutOffset(aPrevLayoutOffset) {
266 VVP_LOG("%p: Registering PostScroll on %p %p\n", aViewport, aPresContext,
267 aPresContext->RefreshDriver());
268 aPresContext->RefreshDriver()->PostVisualViewportScrollEvent(this);
271 bool VisualViewport::VisualViewportScrollEvent::HasPresContext(
272 nsPresContext* aContext) const {
273 return mPresContext.get() == aContext;
276 void VisualViewport::VisualViewportScrollEvent::Revoke() {
277 mViewport = nullptr;
278 mPresContext = nullptr;
281 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
282 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
283 VisualViewport::VisualViewportScrollEvent::Run() {
284 if (RefPtr<VisualViewport> viewport = mViewport) {
285 viewport->FireScrollEvent();
287 return NS_OK;
290 void VisualViewport::FireScrollEvent() {
291 MOZ_ASSERT(mScrollEvent);
292 nsPoint prevVisualOffset = mScrollEvent->PrevVisualOffset();
293 nsPoint prevLayoutOffset = mScrollEvent->PrevLayoutOffset();
294 mScrollEvent->Revoke();
295 mScrollEvent = nullptr;
297 if (RefPtr<PresShell> presShell = GetPresShell()) {
298 RefPtr<nsPresContext> presContext = GetPresContext();
300 if (presShell->GetVisualViewportOffset() != prevVisualOffset) {
301 // The internal event will be fired whenever the visual viewport's
302 // *absolute* offset changed, i.e. relative to the page.
303 VVP_LOG("%p: FireScrollEvent, fire mozvisualscroll\n", this);
304 WidgetEvent mozEvent(true, eMozVisualScroll);
305 mozEvent.mFlags.mOnlySystemGroupDispatch = true;
306 EventDispatcher::Dispatch(this, presContext, &mozEvent);
309 // Check whether the relative visual viewport offset actually changed -
310 // maybe both visual and layout viewport scrolled together and there was no
311 // change after all.
312 nsPoint curRelativeOffset =
313 presShell->GetVisualViewportOffsetRelativeToLayoutViewport();
314 nsPoint prevRelativeOffset = prevVisualOffset - prevLayoutOffset;
315 VVP_LOG(
316 "%p: FireScrollEvent, curRelativeOffset %s, "
317 "prevRelativeOffset %s\n",
318 this, ToString(curRelativeOffset).c_str(),
319 ToString(prevRelativeOffset).c_str());
320 if (curRelativeOffset != prevRelativeOffset) {
321 VVP_LOG("%p, FireScrollEvent, fire VisualViewport scroll\n", this);
322 WidgetGUIEvent event(true, eScroll, nullptr);
323 event.mFlags.mBubbles = false;
324 event.mFlags.mCancelable = false;
325 EventDispatcher::Dispatch(this, presContext, &event);