From d883c3ef989e8736a09600d59486f09ac89947b4 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 16 Sep 2020 13:30:35 +0000 Subject: [PATCH 1/7] Issue #1643 - Part 1: Add GetNodeDepth() to nsContentUtils. --- dom/base/nsContentUtils.cpp | 14 ++++++++++++++ dom/base/nsContentUtils.h | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 3568ced90d..20b6beffa1 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -9843,3 +9843,17 @@ nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement) } return e; } + +/* static */ uint32_t +nsContentUtils::GetNodeDepth(nsINode* aNode) +{ + uint32_t depth = 1; + + MOZ_ASSERT(aNode, "Node shouldn't be null"); + + while ((aNode = aNode->GetParentNode())) { + ++depth; + } + + return depth; +} \ No newline at end of file diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 8cf105bb3e..db9558d06c 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2763,6 +2763,14 @@ public: static bool IsCustomElementsEnabled() { return sIsCustomElementsEnabled; } + /** + * Returns the length of the parent-traversal path (in terms of the number of + * nodes) to an unparented/root node from aNode. An unparented/root node is + * considered to have a depth of 1, its children have a depth of 2, etc. + * aNode is expected to be non-null. + */ + static uint32_t GetNodeDepth(nsINode* aNode); + private: static bool InitializeEventTable(); From abb3630983aab709ceaf55f72842783b4e52364e Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 16 Sep 2020 16:39:15 +0000 Subject: [PATCH 2/7] Issue #1643 - Part 2: Implement ResizeObserver API Implements ResizeObserver, ResizeObserverEntry and ResizeObservation --- dom/base/ResizeObserver.cpp | 304 +++++++++++++++++++++++++++++++ dom/base/ResizeObserver.h | 254 ++++++++++++++++++++++++++ dom/base/moz.build | 2 + dom/bindings/Bindings.conf | 15 ++ dom/webidl/ResizeObserver.webidl | 39 ++++ dom/webidl/moz.build | 1 + modules/libpref/init/all.js | 3 + 7 files changed, 618 insertions(+) create mode 100644 dom/base/ResizeObserver.cpp create mode 100644 dom/base/ResizeObserver.h create mode 100644 dom/webidl/ResizeObserver.webidl diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp new file mode 100644 index 0000000000..aefcddd9d4 --- /dev/null +++ b/dom/base/ResizeObserver.cpp @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 8; 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/. */ + +#include "mozilla/dom/ResizeObserver.h" + +#include "mozilla/dom/DOMRect.h" +#include "nsContentUtils.h" +#include "nsIFrame.h" +#include "nsSVGUtils.h" + +namespace mozilla { +namespace dom { + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserver) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserver) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserver) + +NS_IMPL_CYCLE_COLLECTION_CLASS(ResizeObserver) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResizeObserver) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObserver) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservationMap) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObserver) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservationMap) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +already_AddRefed +ResizeObserver::Constructor(const GlobalObject& aGlobal, + ResizeObserverCallback& aCb, + ErrorResult& aRv) +{ + nsCOMPtr window = + do_QueryInterface(aGlobal.GetAsSupports()); + + if (!window) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr document = window->GetExtantDoc(); + + if (!document) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr observer = new ResizeObserver(window.forget(), aCb); + // TODO: Add the new ResizeObserver to document here in the later patch. + + return observer.forget(); +} + +void +ResizeObserver::Observe(Element* aTarget, + ErrorResult& aRv) +{ + if (!aTarget) { + aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + RefPtr observation; + + if (!mObservationMap.Get(aTarget, getter_AddRefs(observation))) { + observation = new ResizeObservation(this, aTarget); + + mObservationMap.Put(aTarget, observation); + mObservationList.insertBack(observation); + + // Per the spec, we need to trigger notification in event loop that + // contains ResizeObserver observe call even when resize/reflow does + // not happen. + // TODO: Implement the notification scheduling in the later patch. + } +} + +void +ResizeObserver::Unobserve(Element* aTarget, + ErrorResult& aRv) +{ + if (!aTarget) { + aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + RefPtr observation; + + if (mObservationMap.Get(aTarget, getter_AddRefs(observation))) { + mObservationMap.Remove(aTarget); + + MOZ_ASSERT(!mObservationList.isEmpty(), + "If ResizeObservation found for an element, observation list " + "must be not empty."); + + observation->remove(); + } +} + +void +ResizeObserver::Disconnect() +{ + mObservationMap.Clear(); + mObservationList.clear(); + mActiveTargets.Clear(); +} + +void +ResizeObserver::GatherActiveObservations(uint32_t aDepth) +{ + mActiveTargets.Clear(); + mHasSkippedTargets = false; + + for (auto observation : mObservationList) { + if (observation->IsActive()) { + uint32_t targetDepth = + nsContentUtils::GetNodeDepth(observation->Target()); + + if (targetDepth > aDepth) { + mActiveTargets.AppendElement(observation); + } else { + mHasSkippedTargets = true; + } + } + } +} + +bool +ResizeObserver::HasActiveObservations() const +{ + return !mActiveTargets.IsEmpty(); +} + +bool +ResizeObserver::HasSkippedObservations() const +{ + return mHasSkippedTargets; +} + +uint32_t +ResizeObserver::BroadcastActiveObservations() +{ + uint32_t shallowestTargetDepth = UINT32_MAX; + + if (HasActiveObservations()) { + Sequence> entries; + + for (auto observation : mActiveTargets) { + RefPtr entry = + new ResizeObserverEntry(this, observation->Target()); + + nsRect rect = observation->GetTargetRect(); + entry->SetContentRect(rect); + + if (!entries.AppendElement(entry.forget(), fallible)) { + // Out of memory. + break; + } + + // Sync the broadcast size of observation so the next size inspection + // will be based on the updated size from last delivered observations. + observation->UpdateBroadcastSize(rect); + + uint32_t targetDepth = + nsContentUtils::GetNodeDepth(observation->Target()); + + if (targetDepth < shallowestTargetDepth) { + shallowestTargetDepth = targetDepth; + } + } + + mCallback->Call(this, entries, *this); + mActiveTargets.Clear(); + mHasSkippedTargets = false; + } + + return shallowestTargetDepth; +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverEntry) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverEntry) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverEntry) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverEntry, + mTarget, mContentRect, + mOwner) + +already_AddRefed +ResizeObserverEntry::Constructor(const GlobalObject& aGlobal, + Element* aTarget, + ErrorResult& aRv) +{ + RefPtr observerEntry = + new ResizeObserverEntry(aGlobal.GetAsSupports(), aTarget); + return observerEntry.forget(); +} + +void +ResizeObserverEntry::SetContentRect(nsRect aRect) +{ + RefPtr contentRect = new DOMRect(mTarget); + nsIFrame* frame = mTarget->GetPrimaryFrame(); + + if (frame) { + nsMargin padding = frame->GetUsedPadding(); + + // Per the spec, we need to include padding in contentRect of + // ResizeObserverEntry. + aRect.x = padding.left; + aRect.y = padding.top; + } + + contentRect->SetLayoutRect(aRect); + mContentRect = contentRect.forget(); +} + +ResizeObserverEntry::~ResizeObserverEntry() +{ +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObservation) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObservation) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObservation) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObservation, + mTarget, mOwner) + +already_AddRefed +ResizeObservation::Constructor(const GlobalObject& aGlobal, + Element* aTarget, + ErrorResult& aRv) +{ + RefPtr observation = + new ResizeObservation(aGlobal.GetAsSupports(), aTarget); + return observation.forget(); +} + +bool +ResizeObservation::IsActive() const +{ + nsRect rect = GetTargetRect(); + return (rect.width != mBroadcastWidth || rect.height != mBroadcastHeight); +} + +void +ResizeObservation::UpdateBroadcastSize(nsRect aRect) +{ + mBroadcastWidth = aRect.width; + mBroadcastHeight = aRect.height; +} + +nsRect +ResizeObservation::GetTargetRect() const +{ + nsRect rect; + nsIFrame* frame = mTarget->GetPrimaryFrame(); + + if (frame) { + if (mTarget->IsSVGElement()) { + gfxRect bbox = nsSVGUtils::GetBBox(frame); + rect.width = NSFloatPixelsToAppUnits(bbox.width, AppUnitsPerCSSPixel()); + rect.height = NSFloatPixelsToAppUnits(bbox.height, AppUnitsPerCSSPixel()); + } else { + // Per the spec, non-replaced inline Elements will always have an empty + // content rect. + if (frame->IsFrameOfType(nsIFrame::eReplaced) || + !frame->IsFrameOfType(nsIFrame::eLineParticipant)) { + rect = frame->GetContentRectRelativeToSelf(); + } + } + } + + return rect; +} + +ResizeObservation::~ResizeObservation() +{ +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/ResizeObserver.h b/dom/base/ResizeObserver.h new file mode 100644 index 0000000000..2f56c580f4 --- /dev/null +++ b/dom/base/ResizeObserver.h @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 8; 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/. */ + +#ifndef mozilla_dom_ResizeObserver_h +#define mozilla_dom_ResizeObserver_h + +#include "mozilla/dom/ResizeObserverBinding.h" + +namespace mozilla { +namespace dom { + +/** + * ResizeObserver interfaces and algorithms are based on + * https://wicg.github.io/ResizeObserver/#api + */ +class ResizeObserver final + : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserver) + + ResizeObserver(already_AddRefed&& aOwner, + ResizeObserverCallback& aCb) + : mOwner(aOwner) + , mCallback(&aCb) + { + MOZ_ASSERT(mOwner, "Need a non-null owner window"); + } + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + ResizeObserverCallback& aCb, + ErrorResult& aRv); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override + { + return ResizeObserverBinding::Wrap(aCx, this, aGivenProto); + } + + nsISupports* GetParentObject() const + { + return mOwner; + } + + void Observe(Element* aTarget, ErrorResult& aRv); + + void Unobserve(Element* aTarget, ErrorResult& aRv); + + void Disconnect(); + + /* + * Gather all observations which have an observed target with size changed + * since last BroadcastActiveObservations() in this ResizeObserver. + * An observation will be skipped if the depth of its observed target is less + * or equal than aDepth. All gathered observations will be added to + * mActiveTargets. + */ + void GatherActiveObservations(uint32_t aDepth); + + /* + * Returns whether this ResizeObserver has any active observations + * since last GatherActiveObservations(). + */ + bool HasActiveObservations() const; + + /* + * Returns whether this ResizeObserver has any skipped observations + * since last GatherActiveObservations(). + */ + bool HasSkippedObservations() const; + + /* + * Deliver the callback function in JavaScript for all active observations + * and pass the sequence of ResizeObserverEntry so JavaScript can access them. + * The broadcast size of observations will be updated and mActiveTargets will + * be cleared. It also returns the shallowest depth of elements from active + * observations or UINT32_MAX if there is no any active observations. + */ + uint32_t BroadcastActiveObservations(); + +protected: + ~ResizeObserver() + { + mObservationList.clear(); + } + + nsCOMPtr mOwner; + RefPtr mCallback; + nsTArray> mActiveTargets; + bool mHasSkippedTargets; + + // Combination of HashTable and LinkedList so we can iterate through + // the elements of HashTable in order of insertion time. + // Will be nice if we have our own data structure for this in the future. + nsRefPtrHashtable, ResizeObservation> mObservationMap; + LinkedList mObservationList; +}; + +/** + * ResizeObserverEntry is the entry that contains the information for observed + * elements. This object is the one that visible to JavaScript in callback + * function that is fired by ResizeObserver. + */ +class ResizeObserverEntry final + : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverEntry) + + ResizeObserverEntry(nsISupports* aOwner, Element* aTarget) + : mOwner(aOwner) + , mTarget(aTarget) + { + MOZ_ASSERT(mOwner, "Need a non-null owner"); + MOZ_ASSERT(mTarget, "Need a non-null target element"); + } + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + Element* aTarget, + ErrorResult& aRv); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override + { + return ResizeObserverEntryBinding::Wrap(aCx, this, + aGivenProto); + } + + nsISupports* GetParentObject() const + { + return mOwner; + } + + Element* Target() const + { + return mTarget; + } + + /* + * Returns the DOMRectReadOnly of target's content rect so it can be + * accessed from JavaScript in callback function of ResizeObserver. + */ + DOMRectReadOnly* GetContentRect() const + { + return mContentRect; + } + + void SetContentRect(nsRect aRect); + +protected: + ~ResizeObserverEntry(); + + nsCOMPtr mOwner; + nsCOMPtr mTarget; + RefPtr mContentRect; +}; + +/** + * We use ResizeObservation to store and sync the size information of one + * observed element so we can decide whether an observation should be fired + * or not. + */ +class ResizeObservation final + : public nsISupports + , public nsWrapperCache + , public LinkedListElement +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObservation) + + ResizeObservation(nsISupports* aOwner, Element* aTarget) + : mOwner(aOwner) + , mTarget(aTarget) + , mBroadcastWidth(0) + , mBroadcastHeight(0) + { + MOZ_ASSERT(mOwner, "Need a non-null owner"); + MOZ_ASSERT(mTarget, "Need a non-null target element"); + } + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + Element* aTarget, + ErrorResult& aRv); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override + { + return ResizeObservationBinding::Wrap(aCx, this, aGivenProto); + } + + nsISupports* GetParentObject() const + { + return mOwner; + } + + Element* Target() const + { + return mTarget; + } + + nscoord BroadcastWidth() const + { + return mBroadcastWidth; + } + + nscoord BroadcastHeight() const + { + return mBroadcastHeight; + } + + /* + * Returns whether the observed target element size differs from current + * BroadcastWidth and BroadcastHeight + */ + bool IsActive() const; + + /* + * Update current BroadcastWidth and BroadcastHeight with size from aRect. + */ + void UpdateBroadcastSize(nsRect aRect); + + /* + * Returns the target's rect in the form of nsRect. + * If the target is SVG, width and height are determined from bounding box. + */ + nsRect GetTargetRect() const; + +protected: + ~ResizeObservation(); + + nsCOMPtr mOwner; + nsCOMPtr mTarget; + + // Broadcast width and broadcast height are the latest recorded size + // of observed target. + nscoord mBroadcastWidth; + nscoord mBroadcastHeight; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ResizeObserver_h + diff --git a/dom/base/moz.build b/dom/base/moz.build index fe65453fe9..27322475ed 100755 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -204,6 +204,7 @@ EXPORTS.mozilla.dom += [ 'PartialSHistory.h', 'Pose.h', 'ProcessGlobal.h', + 'ResizeObserver.h', 'ResponsiveImageSelector.h', 'SameProcessMessageQueue.h', 'ScreenOrientation.h', @@ -350,6 +351,7 @@ SOURCES += [ 'Pose.cpp', 'PostMessageEvent.cpp', 'ProcessGlobal.cpp', + 'ResizeObserver.cpp', 'ResponsiveImageSelector.cpp', 'SameProcessMessageQueue.cpp', 'ScreenOrientation.cpp', diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 56d220194a..fe4a4eaeff 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -722,6 +722,21 @@ DOMInterfaces = { }, }, +'ResizeObservation': { + 'nativeType': 'mozilla::dom::ResizeObservation', + 'headerFile': 'mozilla/dom/ResizeObserver.h', +}, + +'ResizeObserver': { + 'nativeType': 'mozilla::dom::ResizeObserver', + 'headerFile': 'mozilla/dom/ResizeObserver.h', +}, + +'ResizeObserverEntry': { + 'nativeType': 'mozilla::dom::ResizeObserverEntry', + 'headerFile': 'mozilla/dom/ResizeObserver.h', +}, + 'Response': { 'binaryNames': { 'headers': 'headers_' }, }, diff --git a/dom/webidl/ResizeObserver.webidl b/dom/webidl/ResizeObserver.webidl new file mode 100644 index 0000000000..98700f53c6 --- /dev/null +++ b/dom/webidl/ResizeObserver.webidl @@ -0,0 +1,39 @@ +/* -*- 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/ResizeObserver/ + */ + +[Constructor(ResizeObserverCallback callback), + Exposed=Window, + Pref="layout.css.resizeobserver.enabled"] +interface ResizeObserver { + [Throws] + void observe(Element? target); + [Throws] + void unobserve(Element? target); + void disconnect(); +}; + +callback ResizeObserverCallback = void (sequence entries, ResizeObserver observer); + +[Constructor(Element? target), + ChromeOnly, + Pref="layout.css.resizeobserver.enabled"] +interface ResizeObserverEntry { + readonly attribute Element target; + readonly attribute DOMRectReadOnly? contentRect; +}; + +[Constructor(Element? target), + ChromeOnly, + Pref="layout.css.resizeobserver.enabled"] +interface ResizeObservation { + readonly attribute Element target; + readonly attribute long broadcastWidth; + readonly attribute long broadcastHeight; + boolean isActive(); +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 15c8fb4429..53f38cebcf 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -365,6 +365,7 @@ WEBIDL_FILES = [ 'Range.webidl', 'Rect.webidl', 'Request.webidl', + 'ResizeObserver.webidl', 'Response.webidl', 'RGBColor.webidl', 'RTCStatsReport.webidl', diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index ef19c50e02..c672bd894c 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2652,6 +2652,9 @@ pref("layout.css.font-loading-api.enabled", true); // Should stray control characters be rendered visibly? pref("layout.css.control-characters.visible", false); +// Is support for ResizeObservers enabled? +pref("layout.css.resizeobserver.enabled", true); + // pref for which side vertical scrollbars should be on // 0 = end-side in UI direction // 1 = end-side in document/content direction From 96a75aa6484699cddd61385ceabce0f4017b4349 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 16 Sep 2020 18:21:14 +0000 Subject: [PATCH 3/7] Issue #1643 - Part 3: Implement ResizeObserverController --- dom/base/ResizeObserverController.cpp | 234 ++++++++++++++++++++++++++ dom/base/ResizeObserverController.h | 129 ++++++++++++++ dom/base/moz.build | 2 + 3 files changed, 365 insertions(+) create mode 100644 dom/base/ResizeObserverController.cpp create mode 100644 dom/base/ResizeObserverController.h diff --git a/dom/base/ResizeObserverController.cpp b/dom/base/ResizeObserverController.cpp new file mode 100644 index 0000000000..924bba10d1 --- /dev/null +++ b/dom/base/ResizeObserverController.cpp @@ -0,0 +1,234 @@ +/* -*- 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 "mozilla/dom/ResizeObserverController.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ErrorEvent.h" +#include "nsIPresShell.h" +#include "nsPresContext.h" + +namespace mozilla { +namespace dom { + +void +ResizeObserverNotificationHelper::WillRefresh(TimeStamp aTime) +{ + MOZ_ASSERT(mOwner, "Why is mOwner already dead when this RefreshObserver is still registered?"); + if (mOwner) { + mOwner->Notify(); + } +} + +nsRefreshDriver* +ResizeObserverNotificationHelper::GetRefreshDriver() const +{ + nsIPresShell* presShell = mOwner->GetShell(); + if (MOZ_UNLIKELY(!presShell)) { + return nullptr; + } + + nsPresContext* presContext = presShell->GetPresContext(); + if (MOZ_UNLIKELY(!presContext)) { + return nullptr; + } + + return presContext->RefreshDriver(); +} + +void +ResizeObserverNotificationHelper::Register() +{ + if (mRegistered) { + return; + } + + nsRefreshDriver* refreshDriver = GetRefreshDriver(); + if (!refreshDriver) { + // We maybe navigating away from this page or currently in an iframe with + // display: none. Just abort the Register(), no need to do notification. + return; + } + + refreshDriver->AddRefreshObserver(this, Flush_Display); + mRegistered = true; +} + +void +ResizeObserverNotificationHelper::Unregister() +{ + if (!mRegistered) { + return; + } + + nsRefreshDriver* refreshDriver = GetRefreshDriver(); + if (!refreshDriver) { + // We can't access RefreshDriver now. Just abort the Unregister(). + return; + } + + refreshDriver->RemoveRefreshObserver(this, Flush_Display); + mRegistered = false; +} + +void +ResizeObserverNotificationHelper::Disconnect() +{ + Unregister(); + // Our owner is dying. Clear our pointer to it, in case we outlive it. + mOwner = nullptr; +} + +ResizeObserverNotificationHelper::~ResizeObserverNotificationHelper() +{ + Unregister(); +} + +void +ResizeObserverController::Traverse(nsCycleCollectionTraversalCallback& aCb) +{ + ImplCycleCollectionTraverse(aCb, mResizeObservers, "mResizeObservers"); +} + +void +ResizeObserverController::Unlink() +{ + mResizeObservers.Clear(); +} + +void +ResizeObserverController::AddResizeObserver(ResizeObserver* aObserver) +{ + MOZ_ASSERT(aObserver, "AddResizeObserver() should never be called with " + "a null parameter"); + mResizeObservers.AppendElement(aObserver); +} + +void +ResizeObserverController::Notify() +{ + if (mResizeObservers.IsEmpty()) { + return; + } + + uint32_t shallowestTargetDepth = 0; + + GatherAllActiveObservations(shallowestTargetDepth); + + while (HasAnyActiveObservations()) { + DebugOnly oldShallowestTargetDepth = shallowestTargetDepth; + shallowestTargetDepth = BroadcastAllActiveObservations(); + NS_ASSERTION(oldShallowestTargetDepth < shallowestTargetDepth, + "shallowestTargetDepth should be getting strictly deeper"); + + // Flush layout, so that any callback functions' style changes / resizes + // get a chance to take effect. + mDocument->FlushPendingNotifications(Flush_Layout); + + // To avoid infinite resize loop, we only gather all active observations + // that have the depth of observed target element more than current + // shallowestTargetDepth. + GatherAllActiveObservations(shallowestTargetDepth); + } + + mResizeObserverNotificationHelper->Unregister(); + + // Per spec, we deliver an error if the document has any skipped observations. + if (HasAnySkippedObservations()) { + RootedDictionary init(RootingCx()); + + init.mMessage.AssignLiteral("ResizeObserver loop completed with undelivered" + " notifications."); + init.mCancelable = true; + init.mBubbles = true; + + nsEventStatus status = nsEventStatus_eIgnore; + + nsCOMPtr window = + mDocument->GetWindow()->GetCurrentInnerWindow(); + + if (window) { + nsCOMPtr sgo = do_QueryInterface(window); + MOZ_ASSERT(sgo); + + if (NS_WARN_IF(NS_FAILED(sgo->HandleScriptError(init, &status)))) { + status = nsEventStatus_eIgnore; + } + } else { + // We don't fire error events at any global for non-window JS on the main + // thread. + } + + // We need to deliver pending notifications in next cycle. + ScheduleNotification(); + } +} + +void +ResizeObserverController::GatherAllActiveObservations(uint32_t aDepth) +{ + for (auto observer : mResizeObservers) { + observer->GatherActiveObservations(aDepth); + } +} + +uint32_t +ResizeObserverController::BroadcastAllActiveObservations() +{ + uint32_t shallowestTargetDepth = UINT32_MAX; + + for (auto observer : mResizeObservers) { + + uint32_t targetDepth = observer->BroadcastActiveObservations(); + + if (targetDepth < shallowestTargetDepth) { + shallowestTargetDepth = targetDepth; + } + } + + return shallowestTargetDepth; +} + +bool +ResizeObserverController::HasAnyActiveObservations() const +{ + for (auto observer : mResizeObservers) { + if (observer->HasActiveObservations()) { + return true; + } + } + return false; +} + +bool +ResizeObserverController::HasAnySkippedObservations() const +{ + for (auto observer : mResizeObservers) { + if (observer->HasSkippedObservations()) { + return true; + } + } + return false; +} + +void +ResizeObserverController::ScheduleNotification() +{ + mResizeObserverNotificationHelper->Register(); +} + +nsIPresShell* +ResizeObserverController::GetShell() const +{ + return mDocument->GetShell(); +} + +ResizeObserverController::~ResizeObserverController() +{ + mResizeObserverNotificationHelper->Disconnect(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/ResizeObserverController.h b/dom/base/ResizeObserverController.h new file mode 100644 index 0000000000..a775115874 --- /dev/null +++ b/dom/base/ResizeObserverController.h @@ -0,0 +1,129 @@ +/* -*- 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_ResizeObserverController_h +#define mozilla_dom_ResizeObserverController_h + +#include "mozilla/dom/ResizeObserver.h" +#include "mozilla/TimeStamp.h" +#include "nsRefreshDriver.h" + +namespace mozilla { +namespace dom { + +class ResizeObserverController; + +/* + * ResizeObserverNotificationHelper will trigger ResizeObserver notifications + * by registering with the Refresh Driver. +*/ +class ResizeObserverNotificationHelper final : public nsARefreshObserver +{ +public: + NS_INLINE_DECL_REFCOUNTING(ResizeObserverNotificationHelper, override) + + explicit ResizeObserverNotificationHelper(ResizeObserverController* aOwner) + : mOwner(aOwner) + , mRegistered(false) + { + MOZ_ASSERT(mOwner, "Need a non-null owner"); + } + + void WillRefresh(TimeStamp aTime) override; + + nsRefreshDriver* GetRefreshDriver() const; + + void Register(); + + void Unregister(); + + void Disconnect(); + +protected: + virtual ~ResizeObserverNotificationHelper(); + + ResizeObserverController* mOwner; + bool mRegistered; +}; + +/* + * ResizeObserverController contains the list of ResizeObservers and controls + * the flow of notification. +*/ +class ResizeObserverController final +{ +public: + explicit ResizeObserverController(nsIDocument* aDocument) + : mDocument(aDocument) + , mIsNotificationActive(false) + { + MOZ_ASSERT(mDocument, "Need a non-null document"); + mResizeObserverNotificationHelper = + new ResizeObserverNotificationHelper(this); + } + + // Methods for supporting cycle-collection + void Traverse(nsCycleCollectionTraversalCallback& aCb); + void Unlink(); + + void AddResizeObserver(ResizeObserver* aObserver); + + /* + * Schedule the notification via ResizeObserverNotificationHelper refresh + * observer. + */ + void ScheduleNotification(); + + /* + * Notify all ResizeObservers by gathering and broadcasting all active + * observations. + */ + void Notify(); + + nsIPresShell* GetShell() const; + + ~ResizeObserverController(); + +private: + /* + * Calls GatherActiveObservations(aDepth) for all ResizeObservers in this + * controller. All observations in each ResizeObserver with element's depth + * more than aDepth will be gathered. + */ + void GatherAllActiveObservations(uint32_t aDepth); + + /* + * Calls BroadcastActiveObservations() for all ResizeObservers in this + * controller. It also returns the shallowest depth of observed target + * elements from all ResizeObserver or UINT32_MAX if there is no any + * active obsevations at all. + */ + uint32_t BroadcastAllActiveObservations(); + + /* + * Returns whether there is any ResizeObserver that has active observations. + */ + bool HasAnyActiveObservations() const; + + /* + * Returns whether there is any ResizeObserver that has skipped observations. + */ + bool HasAnySkippedObservations() const; + +protected: + // Raw pointer is OK because mDocument strongly owns us & hence must outlive + // us. + nsIDocument* const mDocument; + + RefPtr mResizeObserverNotificationHelper; + nsTArray> mResizeObservers; + bool mIsNotificationActive; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ResizeObserverController_h diff --git a/dom/base/moz.build b/dom/base/moz.build index 27322475ed..aff13bee0c 100755 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -205,6 +205,7 @@ EXPORTS.mozilla.dom += [ 'Pose.h', 'ProcessGlobal.h', 'ResizeObserver.h', + 'ResizeObserverController.h', 'ResponsiveImageSelector.h', 'SameProcessMessageQueue.h', 'ScreenOrientation.h', @@ -352,6 +353,7 @@ SOURCES += [ 'PostMessageEvent.cpp', 'ProcessGlobal.cpp', 'ResizeObserver.cpp', + 'ResizeObserverController.cpp', 'ResponsiveImageSelector.cpp', 'SameProcessMessageQueue.cpp', 'ScreenOrientation.cpp', From 57d143dc6e5c0d1a3c22406fdd0e173cde1923f2 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 16 Sep 2020 19:39:33 +0000 Subject: [PATCH 4/7] Issue #1643 - Part 4: Hook up all the plumbing. --- dom/base/ResizeObserver.cpp | 4 ++-- dom/base/nsDocument.cpp | 26 ++++++++++++++++++++++++++ dom/base/nsDocument.h | 8 ++++++++ dom/base/nsIDocument.h | 5 +++++ layout/base/nsPresShell.cpp | 5 +++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp index aefcddd9d4..37d940c2b3 100644 --- a/dom/base/ResizeObserver.cpp +++ b/dom/base/ResizeObserver.cpp @@ -61,7 +61,7 @@ ResizeObserver::Constructor(const GlobalObject& aGlobal, } RefPtr observer = new ResizeObserver(window.forget(), aCb); - // TODO: Add the new ResizeObserver to document here in the later patch. + document->AddResizeObserver(observer); return observer.forget(); } @@ -86,7 +86,7 @@ ResizeObserver::Observe(Element* aTarget, // Per the spec, we need to trigger notification in event loop that // contains ResizeObserver observe call even when resize/reflow does // not happen. - // TODO: Implement the notification scheduling in the later patch. + aTarget->OwnerDoc()->ScheduleResizeObserversNotification(); } } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 76490e6b44..5bf89f26ae 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1681,6 +1681,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) cb.NoteXPCOMChild(mql); } } + + if (tmp->mResizeObserverController) { + tmp->mResizeObserverController->Traverse(cb); + } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument) @@ -1786,6 +1790,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) } tmp->mInUnlinkOrDeletion = false; + + if (tmp->mResizeObserverController) { + tmp->mResizeObserverController->Unlink(); + } NS_IMPL_CYCLE_COLLECTION_UNLINK_END nsresult @@ -11823,6 +11831,24 @@ nsDocument::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aRetur return nsINode::QuerySelectorAll(aSelector, aReturn); } +void +nsDocument::AddResizeObserver(ResizeObserver* aResizeObserver) +{ + if (!mResizeObserverController) { + mResizeObserverController = MakeUnique(this); + } + + mResizeObserverController->AddResizeObserver(aResizeObserver); +} + +void +nsDocument::ScheduleResizeObserversNotification() const +{ + if (mResizeObserverController) { + mResizeObserverController->ScheduleNotification(); + } +} + already_AddRefed nsIDocument::Constructor(const GlobalObject& aGlobal, ErrorResult& rv) diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 502ba0f133..017879b1fb 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -60,6 +60,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/PendingAnimationTracker.h" #include "mozilla/dom/DOMImplementation.h" +#include "mozilla/dom/ResizeObserverController.h" #include "mozilla/dom/ScriptLoader.h" #include "mozilla/dom/StyleSheetList.h" #include "nsDataHashtable.h" @@ -1024,6 +1025,10 @@ public: virtual void UnblockDOMContentLoaded() override; + void AddResizeObserver(mozilla::dom::ResizeObserver* aResizeObserver) override; + + void ScheduleResizeObserversNotification() const override; + protected: friend class nsNodeUtils; friend class nsDocumentOnStack; @@ -1160,6 +1165,9 @@ protected: nsTArray mCharSetObservers; + mozilla::UniquePtr + mResizeObserverController; + PLDHashTable *mSubDocuments; // Array of owning references to all children diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 21cd0aaf7c..5684490aa5 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -153,6 +153,7 @@ class ProcessingInstruction; class Promise; class Selection; class ScriptLoader; +class ResizeObserver; class StyleSheetList; class SVGDocument; class SVGSVGElement; @@ -2853,6 +2854,10 @@ public: bool ModuleScriptsEnabled(); + virtual void AddResizeObserver(mozilla::dom::ResizeObserver* aResizeObserver) = 0; + + virtual void ScheduleResizeObserversNotification() const = 0; + protected: bool GetUseCounter(mozilla::UseCounter aUseCounter) { diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index e8670ff3bf..de3dc1a256 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -9060,6 +9060,11 @@ PresShell::DidDoReflow(bool aInterruptible) docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now); } + // Notify resize observers on reflow. + if (!mPresContext->HasPendingInterrupt()) { + mDocument->ScheduleResizeObserversNotification(); + } + if (sSynthMouseMove) { SynthesizeMouseMove(false); } From f5f9c8c59735be7600db9cd77e753b969b5ec8a5 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Thu, 17 Sep 2020 23:48:58 +0000 Subject: [PATCH 5/7] Issue #1224 - Remove constant expressions from /dom This excludes DOMProxy handlers in dom bindings because that's intertwined with codegen and js that needs to be handled together. --- dom/animation/AnimationPerformanceWarning.h | 6 ++---- dom/base/WindowNamedPropertiesHandler.h | 2 +- dom/base/nsAttrValue.h | 4 ++-- dom/base/nsGlobalWindow.cpp | 4 ++-- dom/base/nsJSEnvironment.cpp | 2 +- dom/bindings/BindingUtils.h | 12 ++++++------ dom/bindings/DOMJSClass.h | 2 +- dom/bindings/ErrorResult.h | 2 +- dom/cache/DBSchema.cpp | 2 +- dom/events/EventStates.h | 8 ++++---- dom/heapsnapshot/HeapSnapshot.cpp | 2 +- dom/html/HTMLInputElement.cpp | 2 -- dom/html/HTMLTrackElement.cpp | 4 ++-- dom/indexedDB/ActorsParent.cpp | 12 ++++++------ dom/media/MediaCache.cpp | 2 +- dom/media/platforms/PDMFactory.h | 8 ++++---- dom/smil/SMILBoolType.h | 2 +- dom/smil/SMILEnumType.h | 2 +- dom/smil/SMILIntegerType.h | 3 ++- dom/smil/SMILStringType.h | 2 +- dom/smil/nsSMILCSSValueType.h | 2 +- dom/smil/nsSMILFloatType.h | 2 +- dom/smil/nsSMILNullType.h | 2 +- dom/svg/SVGIntegerPairSMILType.h | 2 +- dom/svg/SVGLengthListSMILType.h | 2 +- dom/svg/SVGMotionSMILType.h | 2 +- dom/svg/SVGNumberListSMILType.h | 2 +- dom/svg/SVGNumberPairSMILType.h | 2 +- dom/svg/SVGOrientSMILType.h | 2 +- dom/svg/SVGPathSegListSMILType.h | 2 +- dom/svg/SVGPointListSMILType.h | 2 +- dom/svg/SVGTransformListSMILType.h | 2 +- dom/svg/SVGViewBoxSMILType.h | 2 +- dom/xbl/nsXBLMaybeCompiled.h | 2 +- 34 files changed, 54 insertions(+), 57 deletions(-) diff --git a/dom/animation/AnimationPerformanceWarning.h b/dom/animation/AnimationPerformanceWarning.h index a1ac35cba0..582c48cc3f 100644 --- a/dom/animation/AnimationPerformanceWarning.h +++ b/dom/animation/AnimationPerformanceWarning.h @@ -38,8 +38,6 @@ struct AnimationPerformanceWarning std::initializer_list aParams) : mType(aType) { - // FIXME: Once std::initializer_list::size() become a constexpr function, - // we should use static_assert here. MOZ_ASSERT(aParams.size() <= kMaxParamsForLocalization, "The length of parameters should be less than " "kMaxParamsForLocalization"); @@ -49,11 +47,11 @@ struct AnimationPerformanceWarning // Maximum number of parameters passed to // nsContentUtils::FormatLocalizedString to localize warning messages. // - // NOTE: This constexpr can't be forward declared, so if you want to use + // NOTE: This can't be forward declared, so if you want to use // this variable, please include this header file directly. // This value is the same as the limit of nsStringBundle::FormatString. // See the implementation of nsStringBundle::FormatString. - static constexpr uint8_t kMaxParamsForLocalization = 10; + static const uint8_t kMaxParamsForLocalization = 10; // Indicates why this property could not be animated on the compositor. Type mType; diff --git a/dom/base/WindowNamedPropertiesHandler.h b/dom/base/WindowNamedPropertiesHandler.h index 227d8c946d..cafeadb85e 100644 --- a/dom/base/WindowNamedPropertiesHandler.h +++ b/dom/base/WindowNamedPropertiesHandler.h @@ -15,7 +15,7 @@ namespace dom { class WindowNamedPropertiesHandler : public BaseDOMProxyHandler { public: - constexpr WindowNamedPropertiesHandler() + WindowNamedPropertiesHandler() : BaseDOMProxyHandler(nullptr, /* hasPrototype = */ true) { } diff --git a/dom/base/nsAttrValue.h b/dom/base/nsAttrValue.h index 23f61a614a..33ee91afd5 100644 --- a/dom/base/nsAttrValue.h +++ b/dom/base/nsAttrValue.h @@ -268,7 +268,7 @@ public: // EnumTable can be initialized either with an int16_t value // or a value of an enumeration type that can fit within an int16_t. - constexpr EnumTable(const char* aTag, int16_t aValue) + EnumTable(const char* aTag, int16_t aValue) : tag(aTag) , value(aValue) { @@ -276,7 +276,7 @@ public: template::value>::type> - constexpr EnumTable(const char* aTag, T aValue) + EnumTable(const char* aTag, T aValue) : tag(aTag) , value(static_cast(aValue)) { diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index acd596a442..642681d910 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -956,7 +956,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder) class nsOuterWindowProxy : public js::Wrapper { public: - constexpr nsOuterWindowProxy() : js::Wrapper(0) { } + nsOuterWindowProxy() : js::Wrapper(0) { } virtual bool finalizeInBackground(const JS::Value& priv) const override { return false; @@ -1407,7 +1407,7 @@ nsOuterWindowProxy::singleton; class nsChromeOuterWindowProxy : public nsOuterWindowProxy { public: - constexpr nsChromeOuterWindowProxy() : nsOuterWindowProxy() { } + nsChromeOuterWindowProxy() : nsOuterWindowProxy() { } virtual const char *className(JSContext *cx, JS::Handle wrapper) const override; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 605b1917fd..0411bee80a 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -1161,7 +1161,7 @@ TimeUntilNow(TimeStamp start) struct CycleCollectorStats { - constexpr CycleCollectorStats() : + CycleCollectorStats() : mMaxGCDuration(0), mRanSyncForgetSkippable(false), mSuspected(0), mMaxSkippableDuration(0), mMaxSliceTime(0), mMaxSliceTimeSinceClear(0), mTotalSliceTime(0), mAnyLockedOut(false), mExtraForgetSkippableCalls(0), diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 356d3aa000..e97f29359b 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3073,21 +3073,21 @@ class GetCCParticipant { // Helper for GetCCParticipant for classes that participate in CC. template - static constexpr nsCycleCollectionParticipant* + static nsCycleCollectionParticipant* GetHelper(int, typename U::NS_CYCLE_COLLECTION_INNERCLASS* dummy=nullptr) { return T::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant(); } // Helper for GetCCParticipant for classes that don't participate in CC. template - static constexpr nsCycleCollectionParticipant* + static nsCycleCollectionParticipant* GetHelper(double) { return nullptr; } public: - static constexpr nsCycleCollectionParticipant* + static nsCycleCollectionParticipant* Get() { // Passing int() here will try to call the GetHelper that takes an int as @@ -3102,7 +3102,7 @@ template class GetCCParticipant { public: - static constexpr nsCycleCollectionParticipant* + static nsCycleCollectionParticipant* Get() { return nullptr; @@ -3125,7 +3125,7 @@ EnumerateGlobal(JSContext* aCx, JS::Handle aObj); template struct CreateGlobalOptions { - static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind = + static const ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind = ProtoAndIfaceCache::NonWindowLike; static void TraceGlobal(JSTracer* aTrc, JSObject* aObj) { @@ -3142,7 +3142,7 @@ struct CreateGlobalOptions template <> struct CreateGlobalOptions { - static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind = + static const ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind = ProtoAndIfaceCache::WindowLike; static void TraceGlobal(JSTracer* aTrc, JSObject* aObj); static bool PostCreateGlobal(JSContext* aCx, JS::Handle aGlobal); diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index 6e779840fe..c97cf93221 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -210,7 +210,7 @@ struct NativePropertiesN { const int32_t iteratorAliasMethodIndex; - constexpr const NativePropertiesN<7>* Upcast() const { + const NativePropertiesN<7>* Upcast() const { return reinterpret_cast*>(this); } diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h index da7ec30e71..15cf2aa036 100644 --- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -56,7 +56,7 @@ enum ErrNum { // Debug-only compile-time table of the number of arguments of each error, for use in static_assert. #if defined(DEBUG) && (defined(__clang__) || defined(__GNUC__)) -uint16_t constexpr ErrorFormatNumArgs[] = { +uint16_t ErrorFormatNumArgs[] = { #define MSG_DEF(_name, _argc, _exn, _str) \ _argc, #include "mozilla/dom/Errors.msg" diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 176e7b9d1d..37510e2e15 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -2460,7 +2460,7 @@ Validate(mozIStorageConnection* aConn) typedef nsresult (*MigrationFunc)(mozIStorageConnection*, bool&); struct Migration { - constexpr Migration(int32_t aFromVersion, MigrationFunc aFunc) + Migration(int32_t aFromVersion, MigrationFunc aFunc) : mFromVersion(aFromVersion) , mFunc(aFunc) { } diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h index 3397110bae..f23ae98731 100644 --- a/dom/events/EventStates.h +++ b/dom/events/EventStates.h @@ -27,7 +27,7 @@ public: typedef uint64_t InternalType; typedef uint8_t ServoType; - constexpr EventStates() + EventStates() : mStates(0) { } @@ -37,12 +37,12 @@ public: // In that case, we could be sure that only macros at the end were creating // EventStates instances with mStates set to something else than 0. // Unfortunately, this constructor is needed at at least two places now. - explicit constexpr EventStates(InternalType aStates) + explicit EventStates(InternalType aStates) : mStates(aStates) { } - EventStates constexpr operator|(const EventStates& aEventStates) const + EventStates operator|(const EventStates& aEventStates) const { return EventStates(mStates | aEventStates.mStates); } @@ -56,7 +56,7 @@ public: // NOTE: calling if (eventStates1 & eventStates2) will not build. // This might work correctly if operator bool() is defined // but using HasState, HasAllStates or HasAtLeastOneOfStates is recommended. - EventStates constexpr operator&(const EventStates& aEventStates) const + EventStates operator&(const EventStates& aEventStates) const { return EventStates(mStates & aEventStates.mStates); } diff --git a/dom/heapsnapshot/HeapSnapshot.cpp b/dom/heapsnapshot/HeapSnapshot.cpp index 299a96a9cc..668b7f5a5d 100644 --- a/dom/heapsnapshot/HeapSnapshot.cpp +++ b/dom/heapsnapshot/HeapSnapshot.cpp @@ -1455,7 +1455,7 @@ HeapSnapshot::CreateUniqueCoreDumpFile(ErrorResult& rv, class DeleteHeapSnapshotTempFileHelperChild { public: - constexpr DeleteHeapSnapshotTempFileHelperChild() { } + DeleteHeapSnapshotTempFileHelperChild() { } void operator()(PHeapSnapshotTempFileHelperChild* ptr) const { Unused << NS_WARN_IF(!HeapSnapshotTempFileHelperChild::Send__delete__(ptr)); diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index f2c14ff2dc..a1ffbb15df 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -5858,8 +5858,6 @@ HTMLInputElement::ParseAttribute(int32_t aNamespaceID, const nsAString& aValue, nsAttrValue& aResult) { - // We can't make these static_asserts because kInputDefaultType and - // kInputTypeTable aren't constexpr. MOZ_ASSERT(kInputDefaultType->value == NS_FORM_INPUT_TEXT, "Someone forgot to update kInputDefaultType when adding a new " "input type."); diff --git a/dom/html/HTMLTrackElement.cpp b/dom/html/HTMLTrackElement.cpp index 758018f8d2..59810a4447 100644 --- a/dom/html/HTMLTrackElement.cpp +++ b/dom/html/HTMLTrackElement.cpp @@ -57,7 +57,7 @@ namespace mozilla { namespace dom { // Map html attribute string values to TextTrackKind enums. -static constexpr nsAttrValue::EnumTable kKindTable[] = { +static nsAttrValue::EnumTable kKindTable[] = { { "subtitles", static_cast(TextTrackKind::Subtitles) }, { "captions", static_cast(TextTrackKind::Captions) }, { "descriptions", static_cast(TextTrackKind::Descriptions) }, @@ -68,7 +68,7 @@ static constexpr nsAttrValue::EnumTable kKindTable[] = { // Invalid values are treated as "metadata" in ParseAttribute, but if no value // at all is specified, it's treated as "subtitles" in GetKind -static constexpr const nsAttrValue::EnumTable* kKindTableInvalidValueDefault = &kKindTable[4]; +static const nsAttrValue::EnumTable* kKindTableInvalidValueDefault = &kKindTable[4]; class WindowDestroyObserver final : public nsIObserver { diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 74afef4523..c7feb8d867 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -270,7 +270,7 @@ const uint32_t kDEBUGTransactionThreadSleepMS = 0; #endif template -constexpr size_t +size_t LiteralStringLength(const char (&aArr)[N]) { static_assert(N, "Zero-length string literal?!"); @@ -2905,11 +2905,11 @@ UpgradeKeyFunction::CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource, MOZ_ASSERT(aDestination); MOZ_ASSERT(aTagOffset <= Key::kMaxArrayCollapse); - static constexpr uint8_t kOldNumberTag = 0x1; - static constexpr uint8_t kOldDateTag = 0x2; - static constexpr uint8_t kOldStringTag = 0x3; - static constexpr uint8_t kOldArrayTag = 0x4; - static constexpr uint8_t kOldMaxType = kOldArrayTag; + static uint8_t kOldNumberTag = 0x1; + static uint8_t kOldDateTag = 0x2; + static uint8_t kOldStringTag = 0x3; + static uint8_t kOldArrayTag = 0x4; + static uint8_t kOldMaxType = kOldArrayTag; if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) { IDB_REPORT_INTERNAL_ERR(); diff --git a/dom/media/MediaCache.cpp b/dom/media/MediaCache.cpp index 37399f8511..64523afcba 100644 --- a/dom/media/MediaCache.cpp +++ b/dom/media/MediaCache.cpp @@ -277,7 +277,7 @@ protected: }; struct BlockOwner { - constexpr BlockOwner() {} + BlockOwner() {} // The stream that owns this block, or null if the block is free. MediaCacheStream* mStream = nullptr; diff --git a/dom/media/platforms/PDMFactory.h b/dom/media/platforms/PDMFactory.h index 2b43fa1ab8..a13c99ac03 100644 --- a/dom/media/platforms/PDMFactory.h +++ b/dom/media/platforms/PDMFactory.h @@ -49,10 +49,10 @@ public: void SetCDMProxy(CDMProxy* aProxy); #endif - static constexpr int kYUV400 = 0; - static constexpr int kYUV420 = 1; - static constexpr int kYUV422 = 2; - static constexpr int kYUV444 = 3; + static const int kYUV400 = 0; + static const int kYUV420 = 1; + static const int kYUV422 = 2; + static const int kYUV444 = 3; private: virtual ~PDMFactory(); diff --git a/dom/smil/SMILBoolType.h b/dom/smil/SMILBoolType.h index 608a09ccf5..d0bbcf5ea2 100644 --- a/dom/smil/SMILBoolType.h +++ b/dom/smil/SMILBoolType.h @@ -42,7 +42,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SMILBoolType() {} + SMILBoolType() {} }; } // namespace mozilla diff --git a/dom/smil/SMILEnumType.h b/dom/smil/SMILEnumType.h index b6cda3ff9c..070ae60bb6 100644 --- a/dom/smil/SMILEnumType.h +++ b/dom/smil/SMILEnumType.h @@ -43,7 +43,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SMILEnumType() {} + SMILEnumType() {} }; } // namespace mozilla diff --git a/dom/smil/SMILIntegerType.h b/dom/smil/SMILIntegerType.h index 39560cc796..bf8030b6eb 100644 --- a/dom/smil/SMILIntegerType.h +++ b/dom/smil/SMILIntegerType.h @@ -38,7 +38,8 @@ public: } private: - constexpr SMILIntegerType() {} + // Private constructor: prevent instances beyond my singleton. + SMILIntegerType() {} }; } // namespace mozilla diff --git a/dom/smil/SMILStringType.h b/dom/smil/SMILStringType.h index 6f160cadbb..6fb51b7d9d 100644 --- a/dom/smil/SMILStringType.h +++ b/dom/smil/SMILStringType.h @@ -43,7 +43,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SMILStringType() {} + SMILStringType() {} }; } // namespace mozilla diff --git a/dom/smil/nsSMILCSSValueType.h b/dom/smil/nsSMILCSSValueType.h index 0c71605f05..0303e69e37 100644 --- a/dom/smil/nsSMILCSSValueType.h +++ b/dom/smil/nsSMILCSSValueType.h @@ -110,7 +110,7 @@ public: private: // Private constructor: prevent instances beyond my singleton. - constexpr nsSMILCSSValueType() {} + nsSMILCSSValueType() {} }; #endif // NS_SMILCSSVALUETYPE_H_ diff --git a/dom/smil/nsSMILFloatType.h b/dom/smil/nsSMILFloatType.h index fd57e4a770..470e935d37 100644 --- a/dom/smil/nsSMILFloatType.h +++ b/dom/smil/nsSMILFloatType.h @@ -41,7 +41,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr nsSMILFloatType() {} + nsSMILFloatType() {} }; #endif // NS_SMILFLOATTYPE_H_ diff --git a/dom/smil/nsSMILNullType.h b/dom/smil/nsSMILNullType.h index d21610ff41..c668773ce5 100644 --- a/dom/smil/nsSMILNullType.h +++ b/dom/smil/nsSMILNullType.h @@ -44,7 +44,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr nsSMILNullType() {} + nsSMILNullType() {} }; #endif // NS_SMILNULLTYPE_H_ diff --git a/dom/svg/SVGIntegerPairSMILType.h b/dom/svg/SVGIntegerPairSMILType.h index 52af5d3dc6..ae62781fdb 100644 --- a/dom/svg/SVGIntegerPairSMILType.h +++ b/dom/svg/SVGIntegerPairSMILType.h @@ -45,7 +45,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGIntegerPairSMILType() {} + SVGIntegerPairSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGLengthListSMILType.h b/dom/svg/SVGLengthListSMILType.h index 10321b585b..2fbb844887 100644 --- a/dom/svg/SVGLengthListSMILType.h +++ b/dom/svg/SVGLengthListSMILType.h @@ -93,7 +93,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGLengthListSMILType() {} + SVGLengthListSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGMotionSMILType.h b/dom/svg/SVGMotionSMILType.h index 7529d796d7..768c90093f 100644 --- a/dom/svg/SVGMotionSMILType.h +++ b/dom/svg/SVGMotionSMILType.h @@ -78,7 +78,7 @@ public: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGMotionSMILType() {} + SVGMotionSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGNumberListSMILType.h b/dom/svg/SVGNumberListSMILType.h index 5e8db8afeb..efdabdb149 100644 --- a/dom/svg/SVGNumberListSMILType.h +++ b/dom/svg/SVGNumberListSMILType.h @@ -47,7 +47,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGNumberListSMILType() {} + SVGNumberListSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGNumberPairSMILType.h b/dom/svg/SVGNumberPairSMILType.h index 0f90163f58..a88a0f5acd 100644 --- a/dom/svg/SVGNumberPairSMILType.h +++ b/dom/svg/SVGNumberPairSMILType.h @@ -40,7 +40,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGNumberPairSMILType() {} + SVGNumberPairSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGOrientSMILType.h b/dom/svg/SVGOrientSMILType.h index 6b30cbee0f..904b376807 100644 --- a/dom/svg/SVGOrientSMILType.h +++ b/dom/svg/SVGOrientSMILType.h @@ -60,7 +60,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGOrientSMILType() {} + SVGOrientSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGPathSegListSMILType.h b/dom/svg/SVGPathSegListSMILType.h index 6856ef2759..5e4e89141e 100644 --- a/dom/svg/SVGPathSegListSMILType.h +++ b/dom/svg/SVGPathSegListSMILType.h @@ -51,7 +51,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGPathSegListSMILType() {} + SVGPathSegListSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGPointListSMILType.h b/dom/svg/SVGPointListSMILType.h index 6f58bd42e5..053959553a 100644 --- a/dom/svg/SVGPointListSMILType.h +++ b/dom/svg/SVGPointListSMILType.h @@ -47,7 +47,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGPointListSMILType() {} + SVGPointListSMILType() {} }; } // namespace mozilla diff --git a/dom/svg/SVGTransformListSMILType.h b/dom/svg/SVGTransformListSMILType.h index 526f9ba50c..6572815a15 100644 --- a/dom/svg/SVGTransformListSMILType.h +++ b/dom/svg/SVGTransformListSMILType.h @@ -123,7 +123,7 @@ public: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGTransformListSMILType() {} + SVGTransformListSMILType() {} }; } // end namespace mozilla diff --git a/dom/svg/SVGViewBoxSMILType.h b/dom/svg/SVGViewBoxSMILType.h index f05f3928d2..54b171263b 100644 --- a/dom/svg/SVGViewBoxSMILType.h +++ b/dom/svg/SVGViewBoxSMILType.h @@ -40,7 +40,7 @@ protected: private: // Private constructor: prevent instances beyond my singleton. - constexpr SVGViewBoxSMILType() {} + SVGViewBoxSMILType() {} }; } // namespace mozilla diff --git a/dom/xbl/nsXBLMaybeCompiled.h b/dom/xbl/nsXBLMaybeCompiled.h index d4b366b0ee..ba66ec5d51 100644 --- a/dom/xbl/nsXBLMaybeCompiled.h +++ b/dom/xbl/nsXBLMaybeCompiled.h @@ -124,7 +124,7 @@ struct BarrierMethods> template struct IsHeapConstructibleType> { // Yes, this is the exception to the rule. Sorry. - static constexpr bool value = true; + static const bool value = true; }; template From b530d56c4758242ab9d14e36274137650404a276 Mon Sep 17 00:00:00 2001 From: athenian200 Date: Wed, 16 Sep 2020 17:34:03 -0500 Subject: [PATCH 6/7] Issue #1647 - Part 1: Implement percentage for CSS opacity keywords This preliminary step allows percentages to be computed and display correctly, but unfortunately it fails a test after changing VARIANT_HN to VARIANT_HPN because that allows values to be serialized as percentages. However, not doing this means percentages are rejected as valid values for the user to input. The way the style system is setup makes it hard to change this for opacity without changing it for everything else, especially since some code-saving speed hacks in Bug 636029 and Bug 441367 that make a lot of assumptions about this stuff very rigid. --- layout/style/nsCSSPropList.h | 10 +++++----- layout/style/nsRuleNode.cpp | 15 +++++++++++++++ layout/style/test/property_database.js | 18 +++++++++--------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index f62aa38272..906ed7ee0e 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1703,7 +1703,7 @@ CSS_PROP_SVG( FillOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HN | VARIANT_OPENTYPE_SVG_KEYWORD, + VARIANT_HPN | VARIANT_OPENTYPE_SVG_KEYWORD, kContextOpacityKTable, offsetof(nsStyleSVG, mFillOpacity), eStyleAnimType_float) @@ -1841,7 +1841,7 @@ CSS_PROP_SVGRESET( FloodOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HN, + VARIANT_HPN, nullptr, offsetof(nsStyleSVGReset, mFloodOpacity), eStyleAnimType_float) @@ -3070,7 +3070,7 @@ CSS_PROP_EFFECTS( CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR | CSS_PROPERTY_CREATES_STACKING_CONTEXT, "", - VARIANT_HN, + VARIANT_HPN, nullptr, offsetof(nsStyleEffects, mOpacity), eStyleAnimType_float) @@ -3761,7 +3761,7 @@ CSS_PROP_SVGRESET( StopOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HN, + VARIANT_HPN, nullptr, offsetof(nsStyleSVGReset, mStopOpacity), eStyleAnimType_float) @@ -3836,7 +3836,7 @@ CSS_PROP_SVG( StrokeOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HN | VARIANT_OPENTYPE_SVG_KEYWORD, + VARIANT_HPN | VARIANT_OPENTYPE_SVG_KEYWORD, kContextOpacityKTable, offsetof(nsStyleSVG, mStrokeOpacity), eStyleAnimType_float) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 036d97f867..3863ec2926 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1579,6 +1579,21 @@ SetFactor(const nsCSSValue& aValue, float& aField, RuleNodeCacheConditions& aCon case eCSSUnit_Null: return; + case eCSSUnit_Percent: + aField = aValue.GetPercentValue(); + if (aFlags & SETFCT_POSITIVE) { + NS_ASSERTION(aField >= 0.0f, "negative value for positive-only property"); + if (aField < 0.0f) + aField = 0.0f; + } + if (aFlags & SETFCT_OPACITY) { + if (aField < 0.0f) + aField = 0.0f; + if (aField > 1.0f) + aField = 1.0f; + } + return; + case eCSSUnit_Number: aField = aValue.GetFloatValue(); if (aFlags & SETFCT_POSITIVE) { diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 2d6352148a..2fc50a539c 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -3540,7 +3540,7 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "1", "17", "397.376", "3e1", "3e+1", "3e0", "3e+0", "3e-0" ], - other_values: [ "0", "0.4", "0.0000", "-3", "3e-1" ], + other_values: [ "0", "0.4", "0.0000", "-3", "3e-1" "-100%", "50%"], invalid_values: [ "0px", "1px" ] }, "-moz-orient": { @@ -4272,8 +4272,8 @@ var gCSSProperties = { domProp: "fillOpacity", inherited: true, type: CSS_TYPE_LONGHAND, - initial_values: [ "1", "2.8", "1.000", "context-fill-opacity", "context-stroke-opacity" ], - other_values: [ "0", "0.3", "-7.3" ], + initial_values: [ "1", "2.8", "1.000", "300%", "context-fill-opacity", "context-stroke-opacity" ], + other_values: [ "0", "0.3", "-7.3", "-100%", "50%"], invalid_values: [] }, "fill-rule": { @@ -4305,8 +4305,8 @@ var gCSSProperties = { domProp: "floodOpacity", inherited: false, type: CSS_TYPE_LONGHAND, - initial_values: [ "1", "2.8", "1.000" ], - other_values: [ "0", "0.3", "-7.3" ], + initial_values: [ "1", "2.8", "1.000", "300%"], + other_values: [ "0", "0.3", "-7.3", "-100%", "50%"], invalid_values: [] }, "image-rendering": { @@ -4380,8 +4380,8 @@ var gCSSProperties = { domProp: "stopOpacity", inherited: false, type: CSS_TYPE_LONGHAND, - initial_values: [ "1", "2.8", "1.000" ], - other_values: [ "0", "0.3", "-7.3" ], + initial_values: [ "1", "2.8", "1.000", "300%" ], + other_values: [ "0", "0.3", "-7.3", "-100%", "50%" ], invalid_values: [] }, "stroke": { @@ -4436,8 +4436,8 @@ var gCSSProperties = { domProp: "strokeOpacity", inherited: true, type: CSS_TYPE_LONGHAND, - initial_values: [ "1", "2.8", "1.000", "context-fill-opacity", "context-stroke-opacity" ], - other_values: [ "0", "0.3", "-7.3" ], + initial_values: [ "1", "2.8", "1.000", "300%", "context-fill-opacity", "context-stroke-opacity" ], + other_values: [ "0", "0.3", "-7.3", "-100%", "50% ], invalid_values: [] }, "stroke-width": { From fb8d9b8c78a26e6b16fd4dd6762702b151a41a92 Mon Sep 17 00:00:00 2001 From: athenian200 Date: Thu, 17 Sep 2020 16:01:38 -0500 Subject: [PATCH 7/7] Issue #1647 - Part 2: Implement VARIANT_OPACITY to correctly serialize. Even though percentages are already treated as floats internally by the style system for computation purposes, you have to go out of your way to stop them from being read back out as percentages. What I do here amounts to storing the percentage token in the "wrong" container, the one normally used for floats. This allows a value that was read in as a percentage to be read back out as something else, which is normally prevented by the design of the style system. --- layout/inspector/inDOMUtils.cpp | 4 ++-- layout/style/nsCSSParser.cpp | 30 ++++++++++++++++++-------- layout/style/nsCSSPropList.h | 10 ++++----- layout/style/nsCSSProps.h | 1 + layout/style/nsRuleNode.cpp | 15 ------------- layout/style/test/property_database.js | 8 +++---- 6 files changed, 33 insertions(+), 35 deletions(-) diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index 58afc5f760..a28b35ef8f 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -842,7 +842,7 @@ PropertySupportsVariant(nsCSSPropertyID aPropertyID, uint32_t aVariant) case eCSSProperty_grid_row_end: case eCSSProperty_font_weight: case eCSSProperty_initial_letter: - supported = VARIANT_NUMBER; + supported = VARIANT_NUMBER | VARIANT_OPACITY; break; default: @@ -905,7 +905,7 @@ inDOMUtils::CssPropertySupportsType(const nsAString& aProperty, uint32_t aType, break; case TYPE_NUMBER: // Include integers under "number"? - variant = VARIANT_NUMBER | VARIANT_INTEGER; + variant = VARIANT_NUMBER | VARIANT_INTEGER | VARIANT_OPACITY; break; default: // Unknown type diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 556e35406a..18411be0b0 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -1300,7 +1300,7 @@ protected: } bool ParseNonNegativeNumber(nsCSSValue& aValue) { - return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER, nullptr); + return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER | VARIANT_OPACITY, nullptr); } // Helpers for some common ParseSingleTokenOneOrLargerVariant calls. @@ -1310,7 +1310,7 @@ protected: } bool ParseOneOrLargerNumber(nsCSSValue& aValue) { - return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_NUMBER, nullptr); + return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_NUMBER | VARIANT_OPACITY, nullptr); } // http://dev.w3.org/csswg/css-values/#custom-idents @@ -7791,6 +7791,7 @@ CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue, VARIANT_NUMBER | VARIANT_LENGTH | VARIANT_PERCENT | + VARIANT_OPACITY | VARIANT_INTEGER)) == 0, "need to update code below to handle additional variants"); @@ -7831,6 +7832,7 @@ CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue, // that we specifically handle. MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC | VARIANT_NUMBER | + VARIANT_OPACITY | VARIANT_INTEGER)) == 0, "need to update code below to handle additional variants"); @@ -7959,9 +7961,9 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, } } } - // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or - // VARIANT_ZERO_ANGLE. - if (((aVariantMask & VARIANT_NUMBER) != 0) && + // Check VARIANT_NUMBER, number tokens for VARIANT_OPACITY, and + // VARIANT_INTEGER before VARIANT_LENGTH or VARIANT_ZERO_ANGLE. + if (((aVariantMask & (VARIANT_NUMBER | VARIANT_OPACITY)) != 0) && (eCSSToken_Number == tk->mType)) { aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number); return CSSParseResult::Ok; @@ -7971,6 +7973,7 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer); return CSSParseResult::Ok; } + if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0 && eCSSToken_Dimension == tk->mType) || @@ -7996,6 +7999,15 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, aValue.SetPercentValue(tk->mNumber); return CSSParseResult::Ok; } + // We need to store eCSSToken_Percentage in eCSSUnit_Number in order to + // serialize opacity according to spec. All percentage tokens are stored + // as floats, so no type conversion is needed to make this possible. + // Percentage tokens have to be evaluated later than number tokens. + if (((aVariantMask & VARIANT_OPACITY) != 0) && + (eCSSToken_Percentage == tk->mType)) { + aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number); + return CSSParseResult::Ok; + } if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px if (((aVariantMask & VARIANT_LENGTH) != 0) && (eCSSToken_Number == tk->mType)) { @@ -8474,7 +8486,7 @@ CSSParserImpl::ParseImageRect(nsCSSValue& aImage) break; } - static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT; + static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT | VARIANT_OPACITY; if (!ParseSingleTokenNonNegativeVariant(top, VARIANT_SIDE, nullptr) || !ExpectSymbol(',', true) || !ParseSingleTokenNonNegativeVariant(right, VARIANT_SIDE, nullptr) || @@ -10883,7 +10895,7 @@ CSSParserImpl::ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient) if (mToken.mIdent.LowerCaseEqualsLiteral("color-stop")) { // Parse stop location, followed by comma. if (!ParseSingleTokenVariant(stop->mLocation, - VARIANT_NUMBER | VARIANT_PERCENT, + VARIANT_NUMBER | VARIANT_PERCENT | VARIANT_OPACITY, nullptr) || !ExpectSymbol(',', true)) { SkipUntil(')'); // Skip to end of color-stop(...) expression. @@ -16046,7 +16058,7 @@ static bool GetFunctionParseInformation(nsCSSKeyword aToken, {VARIANT_LBCALC, VARIANT_LBCALC, VARIANT_LBCALC}, {VARIANT_ANGLE_OR_ZERO}, {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO}, - {VARIANT_NUMBER}, + {VARIANT_NUMBER|VARIANT_OPACITY}, {VARIANT_LENGTH|VARIANT_NONNEGATIVE_DIMENSION}, {VARIANT_LB|VARIANT_NONNEGATIVE_DIMENSION}, {VARIANT_NUMBER, VARIANT_NUMBER}, @@ -17628,7 +17640,7 @@ CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_repeat) { nsCSSValue lengthValue; if (ParseNonNegativeVariant(lengthValue, - VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_CALC, + VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_OPACITY | VARIANT_CALC, nullptr) != CSSParseResult::Ok) { REPORT_UNEXPECTED(PEExpectedNonnegativeNP); SkipUntil(')'); diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 906ed7ee0e..8900192454 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1703,7 +1703,7 @@ CSS_PROP_SVG( FillOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HPN | VARIANT_OPENTYPE_SVG_KEYWORD, + VARIANT_INHERIT | VARIANT_OPACITY | VARIANT_OPENTYPE_SVG_KEYWORD, kContextOpacityKTable, offsetof(nsStyleSVG, mFillOpacity), eStyleAnimType_float) @@ -1841,7 +1841,7 @@ CSS_PROP_SVGRESET( FloodOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HPN, + VARIANT_INHERIT | VARIANT_OPACITY, nullptr, offsetof(nsStyleSVGReset, mFloodOpacity), eStyleAnimType_float) @@ -3070,7 +3070,7 @@ CSS_PROP_EFFECTS( CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR | CSS_PROPERTY_CREATES_STACKING_CONTEXT, "", - VARIANT_HPN, + VARIANT_INHERIT | VARIANT_OPACITY, nullptr, offsetof(nsStyleEffects, mOpacity), eStyleAnimType_float) @@ -3761,7 +3761,7 @@ CSS_PROP_SVGRESET( StopOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HPN, + VARIANT_INHERIT | VARIANT_OPACITY, nullptr, offsetof(nsStyleSVGReset, mStopOpacity), eStyleAnimType_float) @@ -3836,7 +3836,7 @@ CSS_PROP_SVG( StrokeOpacity, CSS_PROPERTY_PARSE_VALUE, "", - VARIANT_HPN | VARIANT_OPENTYPE_SVG_KEYWORD, + VARIANT_INHERIT | VARIANT_OPACITY | VARIANT_OPENTYPE_SVG_KEYWORD, kContextOpacityKTable, offsetof(nsStyleSVG, mStrokeOpacity), eStyleAnimType_float) diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index aabbac07a7..34d457c087 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -43,6 +43,7 @@ #define VARIANT_IDENTIFIER 0x002000 // D #define VARIANT_IDENTIFIER_NO_INHERIT 0x004000 // like above, but excluding // 'inherit' and 'initial' +#define VARIANT_OPACITY 0x008000 // Take floats and percents as input, output float. #define VARIANT_AUTO 0x010000 // A #define VARIANT_INHERIT 0x020000 // H eCSSUnit_Initial, eCSSUnit_Inherit, eCSSUnit_Unset #define VARIANT_NONE 0x040000 // O diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 3863ec2926..036d97f867 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1579,21 +1579,6 @@ SetFactor(const nsCSSValue& aValue, float& aField, RuleNodeCacheConditions& aCon case eCSSUnit_Null: return; - case eCSSUnit_Percent: - aField = aValue.GetPercentValue(); - if (aFlags & SETFCT_POSITIVE) { - NS_ASSERTION(aField >= 0.0f, "negative value for positive-only property"); - if (aField < 0.0f) - aField = 0.0f; - } - if (aFlags & SETFCT_OPACITY) { - if (aField < 0.0f) - aField = 0.0f; - if (aField > 1.0f) - aField = 1.0f; - } - return; - case eCSSUnit_Number: aField = aValue.GetFloatValue(); if (aFlags & SETFCT_POSITIVE) { diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 2fc50a539c..bc43836303 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -3540,7 +3540,7 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "1", "17", "397.376", "3e1", "3e+1", "3e0", "3e+0", "3e-0" ], - other_values: [ "0", "0.4", "0.0000", "-3", "3e-1" "-100%", "50%"], + other_values: [ "0", "0.4", "0.0000", "-3", "3e-1" "-100%", "50%" ], invalid_values: [ "0px", "1px" ] }, "-moz-orient": { @@ -4273,7 +4273,7 @@ var gCSSProperties = { inherited: true, type: CSS_TYPE_LONGHAND, initial_values: [ "1", "2.8", "1.000", "300%", "context-fill-opacity", "context-stroke-opacity" ], - other_values: [ "0", "0.3", "-7.3", "-100%", "50%"], + other_values: [ "0", "0.3", "-7.3", "-100%", "50%" ], invalid_values: [] }, "fill-rule": { @@ -4305,8 +4305,8 @@ var gCSSProperties = { domProp: "floodOpacity", inherited: false, type: CSS_TYPE_LONGHAND, - initial_values: [ "1", "2.8", "1.000", "300%"], - other_values: [ "0", "0.3", "-7.3", "-100%", "50%"], + initial_values: [ "1", "2.8", "1.000", "300%" ], + other_values: [ "0", "0.3", "-7.3", "-100%", "50%" ], invalid_values: [] }, "image-rendering": {