mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-26 00:57:30 +09:00
[whitespace] Fix CRLF line endings.
When originally implementing ResizeObserver, line endings ended up being CRLF instead of LF. This commit fixes those line endings (no code changes)
This commit is contained in:
parent
07288b49a3
commit
2703c4fc63
3 changed files with 691 additions and 691 deletions
|
|
@ -1,376 +1,376 @@
|
|||
/* -*- 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 "nsIContentInlines.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsSVGUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Note: The shadow root is not part of the calculation because the caller,
|
||||
* ResizeObserver, doesn't observe the shadow root, and only needs relative
|
||||
* depths among all the observed targets. In other words, we calculate the
|
||||
* depth of the flattened tree.
|
||||
*
|
||||
* Note: these is a spec issue about how to handle shadow DOM case. We
|
||||
* may need to update this function later.
|
||||
*
|
||||
*
|
||||
* https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h
|
||||
*/
|
||||
static uint32_t
|
||||
GetNodeDepth(nsINode* aNode) {
|
||||
uint32_t depth = 1;
|
||||
|
||||
MOZ_ASSERT(aNode, "Node shouldn't be null");
|
||||
|
||||
// Use GetFlattenedTreeParentNode to bypass the shadow root and cross the
|
||||
// shadow boundary to calculate the node depth without the shadow root.
|
||||
while ((aNode = aNode->GetFlattenedTreeParentNode())) {
|
||||
++depth;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns |aTarget|'s size in the form of an nsSize.
|
||||
* If the target is an SVG, width and height are determined from the bounding box.
|
||||
*/
|
||||
static nsSize
|
||||
GetTargetSize(Element* aTarget, ResizeObserverBoxOptions aBox) {
|
||||
nsSize size;
|
||||
nsIFrame* frame = aTarget->GetPrimaryFrame();
|
||||
|
||||
if (!frame) {
|
||||
return size;
|
||||
}
|
||||
|
||||
if (aTarget->IsSVGElement()) {
|
||||
// Per the spec, an SVG size is always its bounding box size, no matter what
|
||||
// box option you choose, because SVG elements do not use the standard CSS box
|
||||
// model.
|
||||
gfxRect bbox = nsSVGUtils::GetBBox(frame);
|
||||
size.width = NSFloatPixelsToAppUnits(bbox.width, AppUnitsPerCSSPixel());
|
||||
size.height = NSFloatPixelsToAppUnits(bbox.height, AppUnitsPerCSSPixel());
|
||||
} else {
|
||||
// Per the spec, non-replaced inline Elements will always have an empty
|
||||
// content rect. We therefore always use the same empty size for
|
||||
// non-replaced inline elements here, and their IsActive() will
|
||||
// always return false. (So its observation won't be fired.)
|
||||
if (!frame->IsFrameOfType(nsIFrame::eReplaced) &&
|
||||
frame->IsFrameOfType(nsIFrame::eLineParticipant)) {
|
||||
return size;
|
||||
}
|
||||
|
||||
switch (aBox) {
|
||||
case ResizeObserverBoxOptions::Border_box:
|
||||
// GetSize() includes the content area, borders, and padding.
|
||||
size = frame->GetSize();
|
||||
break;
|
||||
case ResizeObserverBoxOptions::Content_box:
|
||||
default:
|
||||
size = frame->GetContentRectRelativeToSelf().Size();
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
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>
|
||||
ResizeObserver::Constructor(const GlobalObject& aGlobal,
|
||||
ResizeObserverCallback& aCb,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> document = window->GetExtantDoc();
|
||||
|
||||
if (!document) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ResizeObserver> observer = new ResizeObserver(window.forget(), aCb);
|
||||
document->AddResizeObserver(observer);
|
||||
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserver::Observe(Element* aTarget,
|
||||
const ResizeObserverOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aTarget) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ResizeObservation> observation;
|
||||
|
||||
if (!mObservationMap.Get(aTarget, getter_AddRefs(observation))) {
|
||||
nsIFrame* frame = aTarget->GetPrimaryFrame();
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
observation = new ResizeObservation(aTarget->OwnerDoc(), aTarget, aOptions.mBox, wm);
|
||||
|
||||
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.
|
||||
aTarget->OwnerDoc()->ScheduleResizeObserversNotification();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserver::Unobserve(Element* aTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aTarget) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ResizeObservation> 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 = 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<OwningNonNull<ResizeObserverEntry>> entries;
|
||||
|
||||
for (auto observation : mActiveTargets) {
|
||||
Element* target = observation->Target();
|
||||
RefPtr<ResizeObserverEntry> entry = new ResizeObserverEntry(this, target);
|
||||
|
||||
nsSize borderBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Border_box);
|
||||
entry->SetBorderBoxSize(borderBoxSize);
|
||||
|
||||
nsSize contentBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Content_box);
|
||||
entry->SetContentRectAndSize(contentBoxSize);
|
||||
|
||||
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.
|
||||
switch (observation->BoxOptions()) {
|
||||
case ResizeObserverBoxOptions::Border_box:
|
||||
observation->UpdateLastReportedSize(borderBoxSize);
|
||||
break;
|
||||
case ResizeObserverBoxOptions::Content_box:
|
||||
default:
|
||||
observation->UpdateLastReportedSize(contentBoxSize);
|
||||
}
|
||||
|
||||
uint32_t targetDepth = 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,
|
||||
mOwner,
|
||||
mTarget,
|
||||
mContentRect,
|
||||
mBorderBoxSize,
|
||||
mContentBoxSize)
|
||||
|
||||
already_AddRefed<ResizeObserverEntry>
|
||||
ResizeObserverEntry::Constructor(const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<ResizeObserverEntry> observerEntry =
|
||||
new ResizeObserverEntry(aGlobal.GetAsSupports(), aTarget);
|
||||
return observerEntry.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserverEntry::SetBorderBoxSize(const nsSize& aSize) {
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
mBorderBoxSize = new ResizeObserverSize(this, aSize, wm);
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserverEntry::SetContentRectAndSize(const nsSize& aSize) {
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
|
||||
// Update mContentRect.
|
||||
nsMargin padding = frame ? frame->GetUsedPadding(): nsMargin();
|
||||
// Per the spec, we need to use the top-left padding offset as the origin of
|
||||
// our contentRect.
|
||||
nsRect rect(nsPoint(padding.left, padding.top), aSize);
|
||||
RefPtr<DOMRect> contentRect = new DOMRect(mTarget);
|
||||
contentRect->SetLayoutRect(rect);
|
||||
mContentRect = contentRect.forget();
|
||||
|
||||
// Update mContentBoxSize.
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
mContentBoxSize = new ResizeObserverSize(this, aSize, wm);
|
||||
}
|
||||
|
||||
ResizeObserverEntry::~ResizeObserverEntry()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize, mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverSize)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
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)
|
||||
|
||||
bool
|
||||
ResizeObservation::IsActive() const
|
||||
{
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
LogicalSize size(wm, GetTargetSize(mTarget, mObservedBox));
|
||||
return mLastReportedSize.ISize(mLastReportedWM) != size.ISize(wm) ||
|
||||
mLastReportedSize.BSize(mLastReportedWM) != size.BSize(wm);
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObservation::UpdateLastReportedSize(const nsSize& aSize) {
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
mLastReportedWM = frame ? frame->GetWritingMode() : WritingMode();
|
||||
mLastReportedSize = LogicalSize(mLastReportedWM, aSize);
|
||||
}
|
||||
|
||||
ResizeObservation::~ResizeObservation()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
/* -*- 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 "nsIContentInlines.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsSVGUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Note: The shadow root is not part of the calculation because the caller,
|
||||
* ResizeObserver, doesn't observe the shadow root, and only needs relative
|
||||
* depths among all the observed targets. In other words, we calculate the
|
||||
* depth of the flattened tree.
|
||||
*
|
||||
* Note: these is a spec issue about how to handle shadow DOM case. We
|
||||
* may need to update this function later.
|
||||
*
|
||||
*
|
||||
* https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h
|
||||
*/
|
||||
static uint32_t
|
||||
GetNodeDepth(nsINode* aNode) {
|
||||
uint32_t depth = 1;
|
||||
|
||||
MOZ_ASSERT(aNode, "Node shouldn't be null");
|
||||
|
||||
// Use GetFlattenedTreeParentNode to bypass the shadow root and cross the
|
||||
// shadow boundary to calculate the node depth without the shadow root.
|
||||
while ((aNode = aNode->GetFlattenedTreeParentNode())) {
|
||||
++depth;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns |aTarget|'s size in the form of an nsSize.
|
||||
* If the target is an SVG, width and height are determined from the bounding box.
|
||||
*/
|
||||
static nsSize
|
||||
GetTargetSize(Element* aTarget, ResizeObserverBoxOptions aBox) {
|
||||
nsSize size;
|
||||
nsIFrame* frame = aTarget->GetPrimaryFrame();
|
||||
|
||||
if (!frame) {
|
||||
return size;
|
||||
}
|
||||
|
||||
if (aTarget->IsSVGElement()) {
|
||||
// Per the spec, an SVG size is always its bounding box size, no matter what
|
||||
// box option you choose, because SVG elements do not use the standard CSS box
|
||||
// model.
|
||||
gfxRect bbox = nsSVGUtils::GetBBox(frame);
|
||||
size.width = NSFloatPixelsToAppUnits(bbox.width, AppUnitsPerCSSPixel());
|
||||
size.height = NSFloatPixelsToAppUnits(bbox.height, AppUnitsPerCSSPixel());
|
||||
} else {
|
||||
// Per the spec, non-replaced inline Elements will always have an empty
|
||||
// content rect. We therefore always use the same empty size for
|
||||
// non-replaced inline elements here, and their IsActive() will
|
||||
// always return false. (So its observation won't be fired.)
|
||||
if (!frame->IsFrameOfType(nsIFrame::eReplaced) &&
|
||||
frame->IsFrameOfType(nsIFrame::eLineParticipant)) {
|
||||
return size;
|
||||
}
|
||||
|
||||
switch (aBox) {
|
||||
case ResizeObserverBoxOptions::Border_box:
|
||||
// GetSize() includes the content area, borders, and padding.
|
||||
size = frame->GetSize();
|
||||
break;
|
||||
case ResizeObserverBoxOptions::Content_box:
|
||||
default:
|
||||
size = frame->GetContentRectRelativeToSelf().Size();
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
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>
|
||||
ResizeObserver::Constructor(const GlobalObject& aGlobal,
|
||||
ResizeObserverCallback& aCb,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindowInner> window =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> document = window->GetExtantDoc();
|
||||
|
||||
if (!document) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ResizeObserver> observer = new ResizeObserver(window.forget(), aCb);
|
||||
document->AddResizeObserver(observer);
|
||||
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserver::Observe(Element* aTarget,
|
||||
const ResizeObserverOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aTarget) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ResizeObservation> observation;
|
||||
|
||||
if (!mObservationMap.Get(aTarget, getter_AddRefs(observation))) {
|
||||
nsIFrame* frame = aTarget->GetPrimaryFrame();
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
observation = new ResizeObservation(aTarget->OwnerDoc(), aTarget, aOptions.mBox, wm);
|
||||
|
||||
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.
|
||||
aTarget->OwnerDoc()->ScheduleResizeObserversNotification();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserver::Unobserve(Element* aTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aTarget) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ResizeObservation> 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 = 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<OwningNonNull<ResizeObserverEntry>> entries;
|
||||
|
||||
for (auto observation : mActiveTargets) {
|
||||
Element* target = observation->Target();
|
||||
RefPtr<ResizeObserverEntry> entry = new ResizeObserverEntry(this, target);
|
||||
|
||||
nsSize borderBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Border_box);
|
||||
entry->SetBorderBoxSize(borderBoxSize);
|
||||
|
||||
nsSize contentBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Content_box);
|
||||
entry->SetContentRectAndSize(contentBoxSize);
|
||||
|
||||
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.
|
||||
switch (observation->BoxOptions()) {
|
||||
case ResizeObserverBoxOptions::Border_box:
|
||||
observation->UpdateLastReportedSize(borderBoxSize);
|
||||
break;
|
||||
case ResizeObserverBoxOptions::Content_box:
|
||||
default:
|
||||
observation->UpdateLastReportedSize(contentBoxSize);
|
||||
}
|
||||
|
||||
uint32_t targetDepth = 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,
|
||||
mOwner,
|
||||
mTarget,
|
||||
mContentRect,
|
||||
mBorderBoxSize,
|
||||
mContentBoxSize)
|
||||
|
||||
already_AddRefed<ResizeObserverEntry>
|
||||
ResizeObserverEntry::Constructor(const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<ResizeObserverEntry> observerEntry =
|
||||
new ResizeObserverEntry(aGlobal.GetAsSupports(), aTarget);
|
||||
return observerEntry.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserverEntry::SetBorderBoxSize(const nsSize& aSize) {
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
mBorderBoxSize = new ResizeObserverSize(this, aSize, wm);
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObserverEntry::SetContentRectAndSize(const nsSize& aSize) {
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
|
||||
// Update mContentRect.
|
||||
nsMargin padding = frame ? frame->GetUsedPadding(): nsMargin();
|
||||
// Per the spec, we need to use the top-left padding offset as the origin of
|
||||
// our contentRect.
|
||||
nsRect rect(nsPoint(padding.left, padding.top), aSize);
|
||||
RefPtr<DOMRect> contentRect = new DOMRect(mTarget);
|
||||
contentRect->SetLayoutRect(rect);
|
||||
mContentRect = contentRect.forget();
|
||||
|
||||
// Update mContentBoxSize.
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
mContentBoxSize = new ResizeObserverSize(this, aSize, wm);
|
||||
}
|
||||
|
||||
ResizeObserverEntry::~ResizeObserverEntry()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize, mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverSize)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
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)
|
||||
|
||||
bool
|
||||
ResizeObservation::IsActive() const
|
||||
{
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
|
||||
LogicalSize size(wm, GetTargetSize(mTarget, mObservedBox));
|
||||
return mLastReportedSize.ISize(mLastReportedWM) != size.ISize(wm) ||
|
||||
mLastReportedSize.BSize(mLastReportedWM) != size.BSize(wm);
|
||||
}
|
||||
|
||||
void
|
||||
ResizeObservation::UpdateLastReportedSize(const nsSize& aSize) {
|
||||
nsIFrame* frame = mTarget->GetPrimaryFrame();
|
||||
mLastReportedWM = frame ? frame->GetWritingMode() : WritingMode();
|
||||
mLastReportedSize = LogicalSize(mLastReportedWM, aSize);
|
||||
}
|
||||
|
||||
ResizeObservation::~ResizeObservation()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
|||
|
|
@ -1,312 +1,312 @@
|
|||
/* -*- 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/AppUnits.h"
|
||||
#include "mozilla/WritingModes.h"
|
||||
#include "mozilla/dom/ResizeObserverBinding.h"
|
||||
#include "nsCoord.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<nsPIDOMWindowInner>&& aOwner,
|
||||
ResizeObserverCallback& aCb)
|
||||
: mOwner(aOwner)
|
||||
, mCallback(&aCb)
|
||||
{
|
||||
MOZ_ASSERT(mOwner, "Need a non-null owner window");
|
||||
}
|
||||
|
||||
static already_AddRefed<ResizeObserver>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
ResizeObserverCallback& aCb,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return ResizeObserverBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
void Observe(Element* aTarget, const ResizeObserverOptions& aOptions, 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 active observations' mLastReportedSize fields will be updated, and
|
||||
* mActiveTargets will be cleared. It also returns the shallowest depth of
|
||||
* elements from active observations or numeric_limits<uint32_t>::max() if
|
||||
* there are not any active observations.
|
||||
*/
|
||||
uint32_t BroadcastActiveObservations();
|
||||
|
||||
protected:
|
||||
~ResizeObserver()
|
||||
{
|
||||
mObservationList.clear();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mOwner;
|
||||
RefPtr<ResizeObserverCallback> mCallback;
|
||||
nsTArray<RefPtr<ResizeObservation>> mActiveTargets;
|
||||
// The spec uses a list to store the skipped targets. However, it seems what
|
||||
// we want is to check if there are any skipped targets (i.e. existence).
|
||||
// Therefore, we use a boolean value to represent the existence of skipped
|
||||
// targets.
|
||||
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<nsPtrHashKey<Element>, ResizeObservation> mObservationMap;
|
||||
LinkedList<ResizeObservation> 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<ResizeObserverEntry>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns target's logical border-box size and content-box size as
|
||||
* ResizeObserverSize.
|
||||
*/
|
||||
ResizeObserverSize* BorderBoxSize() const {
|
||||
return mBorderBoxSize;
|
||||
}
|
||||
ResizeObserverSize* ContentBoxSize() const {
|
||||
return mContentBoxSize;
|
||||
}
|
||||
|
||||
// Set borderBoxSize.
|
||||
void SetBorderBoxSize(const nsSize& aSize);
|
||||
// Set contentRect and contentBoxSize.
|
||||
void SetContentRectAndSize(const nsSize& aSize);
|
||||
|
||||
protected:
|
||||
~ResizeObserverEntry();
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsCOMPtr<Element> mTarget;
|
||||
|
||||
RefPtr<DOMRectReadOnly> mContentRect;
|
||||
RefPtr<ResizeObserverSize> mBorderBoxSize;
|
||||
RefPtr<ResizeObserverSize> mContentBoxSize;
|
||||
};
|
||||
|
||||
class ResizeObserverSize final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverSize)
|
||||
|
||||
// Note: the unit of |aSize| is app unit, and we convert it into css pixel in
|
||||
// the public JS APIs.
|
||||
ResizeObserverSize(nsISupports* aOwner, const nsSize& aSize,
|
||||
const WritingMode aWM)
|
||||
: mOwner(aOwner), mSize(aWM, aSize), mWM(aWM) {
|
||||
MOZ_ASSERT(mOwner, "Need a non-null owner");
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override {
|
||||
return ResizeObserverSizeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
double InlineSize() const {
|
||||
return NSAppUnitsToDoublePixels(mSize.ISize(mWM), AppUnitsPerCSSPixel());
|
||||
}
|
||||
|
||||
double BlockSize() const {
|
||||
return NSAppUnitsToDoublePixels(mSize.BSize(mWM), AppUnitsPerCSSPixel());
|
||||
}
|
||||
|
||||
protected:
|
||||
~ResizeObserverSize() = default;
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
const LogicalSize mSize;
|
||||
const WritingMode mWM;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<ResizeObservation>
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObservation)
|
||||
|
||||
ResizeObservation(nsISupports* aOwner,
|
||||
Element* aTarget,
|
||||
ResizeObserverBoxOptions aBox,
|
||||
const WritingMode aWM)
|
||||
: mOwner(aOwner)
|
||||
, mTarget(aTarget)
|
||||
, mObservedBox(aBox)
|
||||
, mLastReportedSize(aWM)
|
||||
, mLastReportedWM(aWM)
|
||||
{
|
||||
MOZ_ASSERT(mOwner, "Need a non-null owner");
|
||||
MOZ_ASSERT(mTarget, "Need a non-null target element");
|
||||
}
|
||||
|
||||
static already_AddRefed<ResizeObservation>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return ResizeObservationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
Element* Target() const
|
||||
{
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
ResizeObserverBoxOptions BoxOptions() const
|
||||
{
|
||||
return mObservedBox;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the observed target element size differs from the saved
|
||||
* mLastReportedSize.
|
||||
*/
|
||||
bool IsActive() const;
|
||||
|
||||
/*
|
||||
* Update current mLastReportedSize with size from aSize.
|
||||
*/
|
||||
void UpdateLastReportedSize(const nsSize& aSize);
|
||||
|
||||
protected:
|
||||
~ResizeObservation();
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsCOMPtr<Element> mTarget;
|
||||
|
||||
const ResizeObserverBoxOptions mObservedBox;
|
||||
|
||||
// The latest recorded size of observed target.
|
||||
// Per the spec, observation.lastReportedSize should be entry.borderBoxSize
|
||||
// or entry.contentBoxSize (i.e. logical size), instead of entry.contentRect
|
||||
// (i.e. physical rect), so we store this as LogicalSize.
|
||||
LogicalSize mLastReportedSize;
|
||||
WritingMode mLastReportedWM;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ResizeObserver_h
|
||||
|
||||
/* -*- 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/AppUnits.h"
|
||||
#include "mozilla/WritingModes.h"
|
||||
#include "mozilla/dom/ResizeObserverBinding.h"
|
||||
#include "nsCoord.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<nsPIDOMWindowInner>&& aOwner,
|
||||
ResizeObserverCallback& aCb)
|
||||
: mOwner(aOwner)
|
||||
, mCallback(&aCb)
|
||||
{
|
||||
MOZ_ASSERT(mOwner, "Need a non-null owner window");
|
||||
}
|
||||
|
||||
static already_AddRefed<ResizeObserver>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
ResizeObserverCallback& aCb,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return ResizeObserverBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
void Observe(Element* aTarget, const ResizeObserverOptions& aOptions, 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 active observations' mLastReportedSize fields will be updated, and
|
||||
* mActiveTargets will be cleared. It also returns the shallowest depth of
|
||||
* elements from active observations or numeric_limits<uint32_t>::max() if
|
||||
* there are not any active observations.
|
||||
*/
|
||||
uint32_t BroadcastActiveObservations();
|
||||
|
||||
protected:
|
||||
~ResizeObserver()
|
||||
{
|
||||
mObservationList.clear();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mOwner;
|
||||
RefPtr<ResizeObserverCallback> mCallback;
|
||||
nsTArray<RefPtr<ResizeObservation>> mActiveTargets;
|
||||
// The spec uses a list to store the skipped targets. However, it seems what
|
||||
// we want is to check if there are any skipped targets (i.e. existence).
|
||||
// Therefore, we use a boolean value to represent the existence of skipped
|
||||
// targets.
|
||||
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<nsPtrHashKey<Element>, ResizeObservation> mObservationMap;
|
||||
LinkedList<ResizeObservation> 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<ResizeObserverEntry>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns target's logical border-box size and content-box size as
|
||||
* ResizeObserverSize.
|
||||
*/
|
||||
ResizeObserverSize* BorderBoxSize() const {
|
||||
return mBorderBoxSize;
|
||||
}
|
||||
ResizeObserverSize* ContentBoxSize() const {
|
||||
return mContentBoxSize;
|
||||
}
|
||||
|
||||
// Set borderBoxSize.
|
||||
void SetBorderBoxSize(const nsSize& aSize);
|
||||
// Set contentRect and contentBoxSize.
|
||||
void SetContentRectAndSize(const nsSize& aSize);
|
||||
|
||||
protected:
|
||||
~ResizeObserverEntry();
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsCOMPtr<Element> mTarget;
|
||||
|
||||
RefPtr<DOMRectReadOnly> mContentRect;
|
||||
RefPtr<ResizeObserverSize> mBorderBoxSize;
|
||||
RefPtr<ResizeObserverSize> mContentBoxSize;
|
||||
};
|
||||
|
||||
class ResizeObserverSize final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverSize)
|
||||
|
||||
// Note: the unit of |aSize| is app unit, and we convert it into css pixel in
|
||||
// the public JS APIs.
|
||||
ResizeObserverSize(nsISupports* aOwner, const nsSize& aSize,
|
||||
const WritingMode aWM)
|
||||
: mOwner(aOwner), mSize(aWM, aSize), mWM(aWM) {
|
||||
MOZ_ASSERT(mOwner, "Need a non-null owner");
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const { return mOwner; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override {
|
||||
return ResizeObserverSizeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
double InlineSize() const {
|
||||
return NSAppUnitsToDoublePixels(mSize.ISize(mWM), AppUnitsPerCSSPixel());
|
||||
}
|
||||
|
||||
double BlockSize() const {
|
||||
return NSAppUnitsToDoublePixels(mSize.BSize(mWM), AppUnitsPerCSSPixel());
|
||||
}
|
||||
|
||||
protected:
|
||||
~ResizeObserverSize() = default;
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
const LogicalSize mSize;
|
||||
const WritingMode mWM;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<ResizeObservation>
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObservation)
|
||||
|
||||
ResizeObservation(nsISupports* aOwner,
|
||||
Element* aTarget,
|
||||
ResizeObserverBoxOptions aBox,
|
||||
const WritingMode aWM)
|
||||
: mOwner(aOwner)
|
||||
, mTarget(aTarget)
|
||||
, mObservedBox(aBox)
|
||||
, mLastReportedSize(aWM)
|
||||
, mLastReportedWM(aWM)
|
||||
{
|
||||
MOZ_ASSERT(mOwner, "Need a non-null owner");
|
||||
MOZ_ASSERT(mTarget, "Need a non-null target element");
|
||||
}
|
||||
|
||||
static already_AddRefed<ResizeObservation>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return ResizeObservationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
Element* Target() const
|
||||
{
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
ResizeObserverBoxOptions BoxOptions() const
|
||||
{
|
||||
return mObservedBox;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the observed target element size differs from the saved
|
||||
* mLastReportedSize.
|
||||
*/
|
||||
bool IsActive() const;
|
||||
|
||||
/*
|
||||
* Update current mLastReportedSize with size from aSize.
|
||||
*/
|
||||
void UpdateLastReportedSize(const nsSize& aSize);
|
||||
|
||||
protected:
|
||||
~ResizeObservation();
|
||||
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsCOMPtr<Element> mTarget;
|
||||
|
||||
const ResizeObserverBoxOptions mObservedBox;
|
||||
|
||||
// The latest recorded size of observed target.
|
||||
// Per the spec, observation.lastReportedSize should be entry.borderBoxSize
|
||||
// or entry.contentBoxSize (i.e. logical size), instead of entry.contentRect
|
||||
// (i.e. physical rect), so we store this as LogicalSize.
|
||||
LogicalSize mLastReportedSize;
|
||||
WritingMode mLastReportedWM;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ResizeObserver_h
|
||||
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ public:
|
|||
|
||||
void Disconnect();
|
||||
|
||||
bool IsRegistered() const { return mRegistered; }
|
||||
|
||||
void DetachFromOwner() { mOwner = nullptr; }
|
||||
bool IsRegistered() const { return mRegistered; }
|
||||
|
||||
void DetachFromOwner() { mOwner = nullptr; }
|
||||
|
||||
protected:
|
||||
virtual ~ResizeObserverNotificationHelper();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue