From a036cfeb69399f6ac2a16ff58d663af9be716313 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Mon, 21 Jul 2025 11:08:16 +0200 Subject: [PATCH] Issue #2714 - Implement the Visual Viewport API This DOM/Web API is entirely irrelevant for desktop use. We give back the width/height of the root scrollframe of content and for scale we pass forward our dpp resolution (usually 1.0). Since we have no "no touch" zones in our content on desktop,. the rest (origin and offset) is hard-coded to (0,0). --- dom/base/VisualViewport.cpp | 144 +++++++++++++++++++++++++++++++ dom/base/VisualViewport.h | 49 +++++++++++ dom/base/moz.build | 2 + dom/base/nsGlobalWindow.cpp | 11 +++ dom/base/nsGlobalWindow.h | 4 + dom/bindings/Bindings.conf | 4 + dom/webidl/VisualViewport.webidl | 21 +++++ dom/webidl/Window.webidl | 6 ++ dom/webidl/moz.build | 1 + gfx/thebes/gfxPrefs.h | 1 + modules/libpref/init/all.js | 3 + 11 files changed, 246 insertions(+) create mode 100644 dom/base/VisualViewport.cpp create mode 100644 dom/base/VisualViewport.h create mode 100644 dom/webidl/VisualViewport.webidl diff --git a/dom/base/VisualViewport.cpp b/dom/base/VisualViewport.cpp new file mode 100644 index 0000000000..c33b1dbf5f --- /dev/null +++ b/dom/base/VisualViewport.cpp @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VisualViewport.h" +#include "nsIScrollableFrame.h" +#include "nsIDocShell.h" + +using namespace mozilla; +using namespace mozilla::dom; + +VisualViewport::VisualViewport(nsPIDOMWindowInner* aWindow) + : DOMEventTargetHelper(aWindow) +{ +} + +VisualViewport::VisualViewport(nsIGlobalObject* aGlobal) + : DOMEventTargetHelper(aGlobal) +{ +} + +VisualViewport::~VisualViewport() +{ +} + +/* virtual */ +JSObject* +VisualViewport::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return VisualViewportBinding::Wrap(aCx, this, aGivenProto); +} + +/* XXXMC: We are effectively getting the size from the root scrollframe of the content + * and the scale from our DPP resolution. + * Since we are only on desktop, by definition have full view size (no "notch" or + * other mobile "don't touch" areas), we are hard-coding: + * - VisualViewportOffset = (0,0) + * - PageLeft/PageTop = 0.0 + * - OffsetLeft/OffsetTop = 0.0 + */ + +CSSSize +VisualViewport::VisualViewportSize() const +{ + CSSSize size = CSSSize(0,0); + + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); + if (sf) { + size = CSSRect::FromAppUnits(sf->GetScrollPortRect().Size()); + } + } + return size; +} + +double +VisualViewport::Width() const +{ + CSSSize size = VisualViewportSize(); + return size.width; +} + +double +VisualViewport::Height() const +{ + CSSSize size = VisualViewportSize(); + return size.height; +} + +double +VisualViewport::Scale() const +{ + double scale = 1; + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + scale = presShell->GetResolution(); + } + return scale; +} + +CSSPoint +VisualViewport::VisualViewportOffset() const +{ + CSSPoint offset = CSSPoint(0,0); + return offset; +} + +CSSPoint +VisualViewport::LayoutViewportOffset() const +{ + CSSPoint offset = CSSPoint(0,0); + + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); + if (sf) { + offset = CSSPoint::FromAppUnits(sf->GetScrollPosition()); + } + } + return offset; +} + +double +VisualViewport::PageLeft() const +{ + return 0.0; +} + +double +VisualViewport::PageTop() const +{ + return 0.0; +} + +double +VisualViewport::OffsetLeft() const +{ + return 0.0; +} + +double +VisualViewport::OffsetTop() const +{ + return 0.0; +} + +nsIPresShell* +VisualViewport::GetPresShell() const +{ + nsCOMPtr window = GetOwner(); + if (!window) { + return nullptr; + } + + nsIDocShell* docShell = window->GetDocShell(); + if (!docShell) { + return nullptr; + } + + return docShell->GetPresShell(); +} diff --git a/dom/base/VisualViewport.h b/dom/base/VisualViewport.h new file mode 100644 index 0000000000..720b368726 --- /dev/null +++ b/dom/base/VisualViewport.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#ifndef mozilla_dom_VisualViewport_h +#define mozilla_dom_VisualViewport_h + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/VisualViewportBinding.h" +#include "Units.h" +#include "nsIPresShell.h" + +namespace mozilla { +namespace dom { + +/* Visual Viewport API spec: https://wicg.github.io/visual-viewport/#the-visualviewport-interface */ +class VisualViewport final: public mozilla::DOMEventTargetHelper +{ + +public: + explicit VisualViewport(nsPIDOMWindowInner* aWindow); + explicit VisualViewport(nsIGlobalObject* aGlobal); + + double OffsetLeft() const; + double OffsetTop() const; + double PageLeft() const; + double PageTop() const; + double Width() const; + double Height() const; + double Scale() const; + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + +private: + virtual ~VisualViewport(); + + CSSSize VisualViewportSize() const; + CSSPoint VisualViewportOffset() const; + CSSPoint LayoutViewportOffset() const; + nsIPresShell* GetPresShell() const; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_VisualViewport_h diff --git a/dom/base/moz.build b/dom/base/moz.build index 04f66ba0f8..f3676239b2 100755 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -217,6 +217,7 @@ EXPORTS.mozilla.dom += [ 'Timeout.h', 'TimeoutHandler.h', 'TreeWalker.h', + 'VisualViewport.h', 'WebKitCSSMatrix.h', 'WebSocket.h', 'WindowOrientationObserver.h', @@ -365,6 +366,7 @@ UNIFIED_SOURCES += [ 'TimeoutHandler.cpp', 'TimerClamping.cpp', 'TreeWalker.cpp', + 'VisualViewport.cpp', 'WebKitCSSMatrix.cpp', 'WebSocket.cpp', 'WindowNamedPropertiesHandler.cpp', diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 30554486e5..c528fee14a 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -23,6 +23,7 @@ #include "mozilla/dom/StorageEventBinding.h" #include "mozilla/dom/Timeout.h" #include "mozilla/dom/TimeoutHandler.h" +#include "mozilla/dom/VisualViewport.h" #include "mozilla/IntegerPrintfMacros.h" #include "nsDOMOfflineResourceList.h" #include "nsError.h" @@ -4156,6 +4157,16 @@ nsGlobalWindow::GetNavigator() return navigator; } +VisualViewport* +nsGlobalWindow::VisualViewport() +{ + if (!mVisualViewport) { + mVisualViewport = new mozilla::dom::VisualViewport(this); + } + + return mVisualViewport; +} + nsScreen* nsGlobalWindow::GetScreen(ErrorResult& aError) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 564dcee500..ba4b521c50 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -134,6 +134,7 @@ class SpeechSynthesis; class TabGroup; class Timeout; class U2F; +class VisualViewport; class WakeLock; struct WindowPostMessageOptions; class Worklet; @@ -974,6 +975,7 @@ public: already_AddRefed GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt, mozilla::ErrorResult& aError) override; + mozilla::dom::VisualViewport* VisualViewport(); already_AddRefed MatchMediaOuter(const nsAString& aQuery); already_AddRefed MatchMedia(const nsAString& aQuery, mozilla::ErrorResult& aError); @@ -1854,6 +1856,8 @@ protected: RefPtr mHistory; RefPtr mCustomElements; + RefPtr mVisualViewport; + // These member variables are used on both inner and the outer windows. nsCOMPtr mDocumentPrincipal; diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index f3bfc1db9e..5312cc0261 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1162,6 +1162,10 @@ DOMInterfaces = { 'wrapperCache': False, }, +'VisualViewport': { + 'nativeType': 'mozilla::dom::VisualViewport', +}, + 'VTTCue': { 'nativeType': 'mozilla::dom::TextTrackCue' }, diff --git a/dom/webidl/VisualViewport.webidl b/dom/webidl/VisualViewport.webidl new file mode 100644 index 0000000000..25c25cd189 --- /dev/null +++ b/dom/webidl/VisualViewport.webidl @@ -0,0 +1,21 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is: + * https://wicg.github.io/visual-viewport/#the-visualviewport-interface + */ + +interface VisualViewport : EventTarget { + readonly attribute double offsetLeft; + readonly attribute double offsetTop; + + readonly attribute double pageLeft; + readonly attribute double pageTop; + + readonly attribute double width; + readonly attribute double height; + + readonly attribute double scale; +}; diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl index 2ba9f4f124..1d8f4b92eb 100644 --- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -15,6 +15,7 @@ * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html * https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object * https://w3c.github.io/requestidlecallback/ + * https://wicg.github.io/visual-viewport/#the-visualviewport-interface */ interface ApplicationCache; @@ -492,3 +493,8 @@ partial interface Window { dictionary WindowPostMessageOptions : StructuredSerializeOptions { USVString targetOrigin = "/"; }; + +partial interface Window { + [SameObject, Pref="dom.visualviewport.enabled", Replaceable] + readonly attribute VisualViewport visualViewport; +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 560e1600aa..ee8b779ae8 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -557,6 +557,7 @@ WEBIDL_FILES = [ 'VideoStreamTrack.webidl', 'VideoTrack.webidl', 'VideoTrackList.webidl', + 'VisualViewport.webidl', 'VTTCue.webidl', 'VTTRegion.webidl', 'WaveShaperNode.webidl', diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index e24e7eb037..d403854661 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -317,6 +317,7 @@ private: DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled", PluginAsyncDrawingEnabled, bool, false); DECL_GFX_PREF(Live, "dom.meta-viewport.enabled", MetaViewportEnabled, bool, false); + DECL_GFX_PREF(Live, "dom.visualviewport.enabled", VisualViewportEnabled, bool, false); DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false); DECL_GFX_PREF(Once, "dom.vr.oculus.enabled", VROculusEnabled, bool, true); DECL_GFX_PREF(Once, "dom.vr.openvr.enabled", VROpenVREnabled, bool, false); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index cbffe84c23..5f270a7a8e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5045,6 +5045,9 @@ pref("intl.allow-insecure-text-input", false); // Enable meta-viewport support in remote APZ-enabled frames. pref("dom.meta-viewport.enabled", false); +// Disable the Visual Viewport API +pref("dom.visualviewport.enabled", false); + // Disable support. See m-c bug 1457503 / UXP #1102. pref("dom.meta-set-cookie.enabled", false);