зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1243846 - Implement Intersection Observer API. r=mrbkap, r=mstange
--HG-- extra : rebase_source : fed18f84d17be6f8d1a6196d3a0b6c66f005fa8b extra : histedit_source : 2d2579bba67ba6bb1857ecbf92a1ab4a21e0d81e
This commit is contained in:
Родитель
cea0812b0c
Коммит
e7229ba450
|
@ -0,0 +1,448 @@
|
|||
/* -*- 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 "DOMIntersectionObserver.h"
|
||||
#include "nsCSSParser.h"
|
||||
#include "nsCSSPropertyID.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserverEntry)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMIntersectionObserverEntry)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMIntersectionObserverEntry)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMIntersectionObserverEntry, mOwner,
|
||||
mRootBounds, mBoundingClientRect,
|
||||
mIntersectionRect, mTarget)
|
||||
|
||||
double
|
||||
DOMIntersectionObserverEntry::IntersectionRatio()
|
||||
{
|
||||
double targetArea = mBoundingClientRect->Width() * mBoundingClientRect->Height();
|
||||
double intersectionArea = mIntersectionRect->Width() * mIntersectionRect->Height();
|
||||
double intersectionRatio = targetArea > 0.0 ? intersectionArea / targetArea : 0.0;
|
||||
return intersectionRatio;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMIntersectionObserver)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(DOMIntersectionObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMIntersectionObserver)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMIntersectionObserver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMIntersectionObserver)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMIntersectionObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
already_AddRefed<DOMIntersectionObserver>
|
||||
DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
return Constructor(aGlobal, aCb, IntersectionObserverInit(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMIntersectionObserver>
|
||||
DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb,
|
||||
const mozilla::dom::IntersectionObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<DOMIntersectionObserver> observer =
|
||||
new DOMIntersectionObserver(window.forget(), aCb);
|
||||
|
||||
observer->mRoot = aOptions.mRoot;
|
||||
|
||||
if (!observer->SetRootMargin(aOptions.mRootMargin)) {
|
||||
aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
|
||||
NS_LITERAL_CSTRING("rootMargin must be specified in pixels or percent."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aOptions.mThreshold.IsDoubleSequence()) {
|
||||
const mozilla::dom::Sequence<double>& thresholds = aOptions.mThreshold.GetAsDoubleSequence();
|
||||
observer->mThresholds.SetCapacity(thresholds.Length());
|
||||
for (const auto& thresh : thresholds) {
|
||||
if (thresh < 0.0 || thresh > 1.0) {
|
||||
aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
observer->mThresholds.AppendElement(thresh);
|
||||
}
|
||||
observer->mThresholds.Sort();
|
||||
} else {
|
||||
double thresh = aOptions.mThreshold.GetAsDouble();
|
||||
if (thresh < 0.0 || thresh > 1.0) {
|
||||
aRv.ThrowTypeError<dom::MSG_THRESHOLD_RANGE_ERROR>();
|
||||
return nullptr;
|
||||
}
|
||||
observer->mThresholds.AppendElement(thresh);
|
||||
}
|
||||
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
DOMIntersectionObserver::SetRootMargin(const nsAString& aString)
|
||||
{
|
||||
// By not passing a CSS Loader object we make sure we don't parse in quirks
|
||||
// mode so that pixel/percent and unit-less values will be differentiated.
|
||||
nsCSSParser parser(nullptr);
|
||||
nsCSSValue value;
|
||||
if (!parser.ParseMarginString(aString, nullptr, 0, value, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mRootMargin = value.GetRectValue();
|
||||
|
||||
for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
|
||||
nsCSSValue value = mRootMargin.*nsCSSRect::sides[i];
|
||||
if (!(value.IsPixelLengthUnit() || value.IsPercentLengthUnit())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::GetRootMargin(mozilla::dom::DOMString& aRetVal)
|
||||
{
|
||||
mRootMargin.AppendToString(eCSSProperty_DOM, aRetVal, nsCSSValue::eNormalized);
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::GetThresholds(nsTArray<double>& aRetVal)
|
||||
{
|
||||
aRetVal = mThresholds;
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::Observe(Element& aTarget)
|
||||
{
|
||||
if (mObservationTargets.Contains(&aTarget)) {
|
||||
return;
|
||||
}
|
||||
aTarget.RegisterIntersectionObserver(this);
|
||||
mObservationTargets.PutEntry(&aTarget);
|
||||
Connect();
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::Unobserve(Element& aTarget)
|
||||
{
|
||||
if (!mObservationTargets.Contains(&aTarget)) {
|
||||
return;
|
||||
}
|
||||
if (mObservationTargets.Count() == 1) {
|
||||
Disconnect();
|
||||
return;
|
||||
}
|
||||
aTarget.UnregisterIntersectionObserver(this);
|
||||
mObservationTargets.RemoveEntry(&aTarget);
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::Connect()
|
||||
{
|
||||
if (mConnected) {
|
||||
return;
|
||||
}
|
||||
nsIDocument* document = mOwner->GetExtantDoc();
|
||||
document->AddIntersectionObserver(this);
|
||||
mConnected = true;
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::Disconnect()
|
||||
{
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
for (auto iter = mObservationTargets.Iter(); !iter.Done(); iter.Next()) {
|
||||
Element* target = iter.Get()->GetKey();
|
||||
target->UnregisterIntersectionObserver(this);
|
||||
}
|
||||
mObservationTargets.Clear();
|
||||
nsIDocument* document = mOwner->GetExtantDoc();
|
||||
document->RemoveIntersectionObserver(this);
|
||||
mConnected = false;
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal)
|
||||
{
|
||||
aRetVal.SwapElements(mQueuedEntries);
|
||||
mQueuedEntries.Clear();
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimilarOrigin(nsINode* aNode1, nsINode* aNode2)
|
||||
{
|
||||
nsIPrincipal* principal1 = aNode1->NodePrincipal();
|
||||
nsIPrincipal* principal2 = aNode2->NodePrincipal();
|
||||
nsAutoCString baseDomain1;
|
||||
nsAutoCString baseDomain2;
|
||||
|
||||
nsresult rv = principal1->GetBaseDomain(baseDomain1);
|
||||
if (NS_FAILED(rv)) {
|
||||
return principal1 == principal2;
|
||||
}
|
||||
|
||||
rv = principal2->GetBaseDomain(baseDomain2);
|
||||
if (NS_FAILED(rv)) {
|
||||
return principal1 == principal2;
|
||||
}
|
||||
|
||||
return baseDomain1 == baseDomain2;
|
||||
}
|
||||
|
||||
static Maybe<nsRect>
|
||||
EdgeInclusiveIntersection(const nsRect& aRect, const nsRect& aOtherRect)
|
||||
{
|
||||
nscoord left = std::max(aRect.x, aOtherRect.x);
|
||||
nscoord top = std::max(aRect.y, aOtherRect.y);
|
||||
nscoord right = std::min(aRect.XMost(), aOtherRect.XMost());
|
||||
nscoord bottom = std::min(aRect.YMost(), aOtherRect.YMost());
|
||||
if (left > right || top > bottom) {
|
||||
return Nothing();
|
||||
}
|
||||
return Some(nsRect(left, top, right - left, bottom - top));
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time)
|
||||
{
|
||||
Element* root;
|
||||
nsIFrame* rootFrame;
|
||||
nsRect rootRect;
|
||||
|
||||
if (mRoot) {
|
||||
root = mRoot;
|
||||
rootFrame = root->GetPrimaryFrame();
|
||||
if (rootFrame) {
|
||||
if (rootFrame->GetType() == nsGkAtoms::scrollFrame) {
|
||||
nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
|
||||
rootRect = nsLayoutUtils::TransformFrameRectToAncestor(
|
||||
rootFrame,
|
||||
rootFrame->GetContentRectRelativeToSelf(),
|
||||
scrollFrame->GetScrolledFrame());
|
||||
} else {
|
||||
rootRect = nsLayoutUtils::GetAllInFlowRectsUnion(rootFrame,
|
||||
nsLayoutUtils::GetContainingBlockForClientRect(rootFrame),
|
||||
nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsIPresShell> presShell = aDocument->GetShell();
|
||||
if (presShell) {
|
||||
rootFrame = presShell->GetRootScrollFrame();
|
||||
nsPresContext* presContext = rootFrame->PresContext();
|
||||
while (!presContext->IsRootContentDocument()) {
|
||||
presContext = rootFrame->PresContext()->GetParentPresContext();
|
||||
rootFrame = presContext->PresShell()->GetRootScrollFrame();
|
||||
}
|
||||
root = rootFrame->GetContent()->AsElement();
|
||||
nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
|
||||
rootRect = scrollFrame->GetScrollPortRect();
|
||||
}
|
||||
}
|
||||
|
||||
nsMargin rootMargin;
|
||||
NS_FOR_CSS_SIDES(side) {
|
||||
nscoord basis = side == NS_SIDE_TOP || side == NS_SIDE_BOTTOM ?
|
||||
rootRect.height : rootRect.width;
|
||||
nsCSSValue value = mRootMargin.*nsCSSRect::sides[side];
|
||||
nsStyleCoord coord;
|
||||
if (value.IsPixelLengthUnit()) {
|
||||
coord.SetCoordValue(value.GetPixelLength());
|
||||
} else if (value.IsPercentLengthUnit()) {
|
||||
coord.SetPercentValue(value.GetPercentValue());
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("invalid length unit");
|
||||
}
|
||||
rootMargin.Side(side) = nsLayoutUtils::ComputeCBDependentValue(basis, coord);
|
||||
}
|
||||
|
||||
for (auto iter = mObservationTargets.Iter(); !iter.Done(); iter.Next()) {
|
||||
Element* target = iter.Get()->GetKey();
|
||||
nsIFrame* targetFrame = target->GetPrimaryFrame();
|
||||
nsRect targetRect;
|
||||
Maybe<nsRect> intersectionRect;
|
||||
|
||||
if (rootFrame && targetFrame &&
|
||||
(!mRoot || nsLayoutUtils::IsAncestorFrameCrossDoc(rootFrame, targetFrame))) {
|
||||
targetRect = nsLayoutUtils::GetAllInFlowRectsUnion(
|
||||
targetFrame,
|
||||
nsLayoutUtils::GetContainingBlockForClientRect(targetFrame),
|
||||
nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS
|
||||
);
|
||||
intersectionRect = Some(targetFrame->GetVisualOverflowRect());
|
||||
|
||||
nsIFrame* containerFrame = nsLayoutUtils::GetCrossDocParentFrame(targetFrame);
|
||||
while (containerFrame && containerFrame != rootFrame) {
|
||||
if (containerFrame->GetType() == nsGkAtoms::scrollFrame) {
|
||||
nsIScrollableFrame* scrollFrame = do_QueryFrame(containerFrame);
|
||||
nsRect subFrameRect = scrollFrame->GetScrollPortRect();
|
||||
nsRect intersectionRectRelativeToContainer =
|
||||
nsLayoutUtils::TransformFrameRectToAncestor(targetFrame,
|
||||
intersectionRect.value(),
|
||||
containerFrame);
|
||||
intersectionRect = EdgeInclusiveIntersection(intersectionRectRelativeToContainer,
|
||||
subFrameRect);
|
||||
if (!intersectionRect) {
|
||||
break;
|
||||
}
|
||||
targetFrame = containerFrame;
|
||||
}
|
||||
|
||||
// TODO: Apply clip-path.
|
||||
|
||||
containerFrame = nsLayoutUtils::GetCrossDocParentFrame(containerFrame);
|
||||
}
|
||||
}
|
||||
|
||||
nsRect rootIntersectionRect = rootRect;
|
||||
bool isInSimilarOriginBrowsingContext = CheckSimilarOrigin(root, target);
|
||||
|
||||
if (isInSimilarOriginBrowsingContext) {
|
||||
rootIntersectionRect.Inflate(rootMargin);
|
||||
}
|
||||
|
||||
if (intersectionRect.isSome()) {
|
||||
nsRect intersectionRectRelativeToRoot =
|
||||
nsLayoutUtils::TransformFrameRectToAncestor(
|
||||
targetFrame,
|
||||
intersectionRect.value(),
|
||||
nsLayoutUtils::GetContainingBlockForClientRect(rootFrame)
|
||||
);
|
||||
intersectionRect = EdgeInclusiveIntersection(
|
||||
intersectionRectRelativeToRoot,
|
||||
rootIntersectionRect
|
||||
);
|
||||
if (intersectionRect.isSome()) {
|
||||
intersectionRect = Some(nsLayoutUtils::TransformFrameRectToAncestor(
|
||||
nsLayoutUtils::GetContainingBlockForClientRect(rootFrame),
|
||||
intersectionRect.value(),
|
||||
targetFrame->PresContext()->PresShell()->GetRootScrollFrame()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
double targetArea = targetRect.width * targetRect.height;
|
||||
double intersectionArea = !intersectionRect ?
|
||||
0 : intersectionRect->width * intersectionRect->height;
|
||||
double intersectionRatio = targetArea > 0.0 ? intersectionArea / targetArea : 0.0;
|
||||
|
||||
size_t threshold = -1;
|
||||
if (intersectionRatio > 0.0) {
|
||||
if (intersectionRatio == 1.0) {
|
||||
threshold = mThresholds.Length();
|
||||
} else {
|
||||
for (size_t k = 0; k < mThresholds.Length(); ++k) {
|
||||
if (mThresholds[k] <= intersectionRatio) {
|
||||
threshold = k + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (intersectionRect.isSome()) {
|
||||
threshold = 0;
|
||||
}
|
||||
|
||||
if (target->UpdateIntersectionObservation(this, threshold)) {
|
||||
QueueIntersectionObserverEntry(
|
||||
target, time,
|
||||
isInSimilarOriginBrowsingContext ? Some(rootIntersectionRect) : Nothing(),
|
||||
targetRect, intersectionRect
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::QueueIntersectionObserverEntry(Element* aTarget,
|
||||
DOMHighResTimeStamp time,
|
||||
const Maybe<nsRect>& aRootRect,
|
||||
const nsRect& aTargetRect,
|
||||
const Maybe<nsRect>& aIntersectionRect)
|
||||
{
|
||||
RefPtr<DOMRect> rootBounds;
|
||||
if (aRootRect.isSome()) {
|
||||
rootBounds = new DOMRect(this);
|
||||
rootBounds->SetLayoutRect(aRootRect.value());
|
||||
}
|
||||
RefPtr<DOMRect> boundingClientRect = new DOMRect(this);
|
||||
boundingClientRect->SetLayoutRect(aTargetRect);
|
||||
RefPtr<DOMRect> intersectionRect = new DOMRect(this);
|
||||
if (aIntersectionRect.isSome()) {
|
||||
intersectionRect->SetLayoutRect(aIntersectionRect.value());
|
||||
}
|
||||
RefPtr<DOMIntersectionObserverEntry> entry = new DOMIntersectionObserverEntry(
|
||||
this,
|
||||
time,
|
||||
rootBounds.forget(),
|
||||
boundingClientRect.forget(),
|
||||
intersectionRect.forget(),
|
||||
aTarget);
|
||||
mQueuedEntries.AppendElement(entry.forget());
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::Notify()
|
||||
{
|
||||
if (!mQueuedEntries.Length()) {
|
||||
return;
|
||||
}
|
||||
mozilla::dom::Sequence<mozilla::OwningNonNull<DOMIntersectionObserverEntry>> entries;
|
||||
if (entries.SetCapacity(mQueuedEntries.Length(), mozilla::fallible)) {
|
||||
for (uint32_t i = 0; i < mQueuedEntries.Length(); ++i) {
|
||||
RefPtr<DOMIntersectionObserverEntry> next = mQueuedEntries[i];
|
||||
*entries.AppendElement(mozilla::fallible) = next;
|
||||
}
|
||||
}
|
||||
mQueuedEntries.Clear();
|
||||
mCallback->Call(this, entries, *this);
|
||||
}
|
||||
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,172 @@
|
|||
/* -*- 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 DOMIntersectionObserver_h
|
||||
#define DOMIntersectionObserver_h
|
||||
|
||||
#include "mozilla/dom/IntersectionObserverBinding.h"
|
||||
#include "nsCSSValue.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
using mozilla::dom::DOMRect;
|
||||
using mozilla::dom::Element;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMIntersectionObserver;
|
||||
|
||||
class DOMIntersectionObserverEntry final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
~DOMIntersectionObserverEntry() {}
|
||||
|
||||
public:
|
||||
DOMIntersectionObserverEntry(nsISupports* aOwner,
|
||||
DOMHighResTimeStamp aTime,
|
||||
RefPtr<DOMRect> aRootBounds,
|
||||
RefPtr<DOMRect> aBoundingClientRect,
|
||||
RefPtr<DOMRect> aIntersectionRect,
|
||||
Element* aTarget)
|
||||
: mOwner(aOwner),
|
||||
mTime(aTime),
|
||||
mRootBounds(aRootBounds),
|
||||
mBoundingClientRect(aBoundingClientRect),
|
||||
mIntersectionRect(aIntersectionRect),
|
||||
mTarget(aTarget)
|
||||
{
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMIntersectionObserverEntry)
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return mozilla::dom::IntersectionObserverEntryBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp Time()
|
||||
{
|
||||
return mTime;
|
||||
}
|
||||
|
||||
DOMRect* GetRootBounds()
|
||||
{
|
||||
return mRootBounds;
|
||||
}
|
||||
|
||||
DOMRect* BoundingClientRect()
|
||||
{
|
||||
return mBoundingClientRect;
|
||||
}
|
||||
|
||||
DOMRect* IntersectionRect()
|
||||
{
|
||||
return mIntersectionRect;
|
||||
}
|
||||
|
||||
double IntersectionRatio();
|
||||
|
||||
Element* Target()
|
||||
{
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
DOMHighResTimeStamp mTime;
|
||||
RefPtr<DOMRect> mRootBounds;
|
||||
RefPtr<DOMRect> mBoundingClientRect;
|
||||
RefPtr<DOMRect> mIntersectionRect;
|
||||
RefPtr<Element> mTarget;
|
||||
};
|
||||
|
||||
#define NS_DOM_INTERSECTION_OBSERVER_IID \
|
||||
{ 0x8570a575, 0xe303, 0x4d18, \
|
||||
{ 0xb6, 0xb1, 0x4d, 0x2b, 0x49, 0xd8, 0xef, 0x94 } }
|
||||
|
||||
class DOMIntersectionObserver final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
virtual ~DOMIntersectionObserver() { }
|
||||
|
||||
public:
|
||||
DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
|
||||
mozilla::dom::IntersectionCallback& aCb)
|
||||
: mOwner(aOwner), mCallback(&aCb), mConnected(false)
|
||||
{
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMIntersectionObserver)
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_INTERSECTION_OBSERVER_IID)
|
||||
|
||||
static already_AddRefed<DOMIntersectionObserver>
|
||||
Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb,
|
||||
mozilla::ErrorResult& aRv);
|
||||
static already_AddRefed<DOMIntersectionObserver>
|
||||
Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
mozilla::dom::IntersectionCallback& aCb,
|
||||
const mozilla::dom::IntersectionObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return mozilla::dom::IntersectionObserverBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
Element* GetRoot() const {
|
||||
return mRoot;
|
||||
}
|
||||
|
||||
void GetRootMargin(mozilla::dom::DOMString& aRetVal);
|
||||
void GetThresholds(nsTArray<double>& aRetVal);
|
||||
void Observe(Element& aTarget);
|
||||
void Unobserve(Element& aTarget);
|
||||
|
||||
void Disconnect();
|
||||
void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
|
||||
|
||||
mozilla::dom::IntersectionCallback* IntersectionCallback() { return mCallback; }
|
||||
|
||||
bool SetRootMargin(const nsAString& aString);
|
||||
|
||||
void Update(nsIDocument* aDocument, DOMHighResTimeStamp time);
|
||||
void Notify();
|
||||
|
||||
protected:
|
||||
void Connect();
|
||||
void QueueIntersectionObserverEntry(Element* aTarget,
|
||||
DOMHighResTimeStamp time,
|
||||
const Maybe<nsRect>& aRootRect,
|
||||
const nsRect& aTargetRect,
|
||||
const Maybe<nsRect>& aIntersectionRect);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mOwner;
|
||||
RefPtr<mozilla::dom::IntersectionCallback> mCallback;
|
||||
RefPtr<Element> mRoot;
|
||||
nsCSSRect mRootMargin;
|
||||
nsTArray<double> mThresholds;
|
||||
nsTHashtable<nsPtrHashKey<Element>> mObservationTargets;
|
||||
nsTArray<RefPtr<DOMIntersectionObserverEntry>> mQueuedEntries;
|
||||
bool mConnected;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(DOMIntersectionObserver, NS_DOM_INTERSECTION_OBSERVER_IID)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -149,6 +149,7 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsDOMStringMap.h"
|
||||
#include "DOMIntersectionObserver.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -3886,3 +3887,44 @@ Element::ClearDataset()
|
|||
slots->mDataset = nullptr;
|
||||
}
|
||||
|
||||
nsTArray<Element::nsDOMSlots::IntersectionObserverRegistration>*
|
||||
Element::RegisteredIntersectionObservers()
|
||||
{
|
||||
nsDOMSlots* slots = DOMSlots();
|
||||
return &slots->mRegisteredIntersectionObservers;
|
||||
}
|
||||
|
||||
void
|
||||
Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
|
||||
{
|
||||
RegisteredIntersectionObservers()->AppendElement(
|
||||
nsDOMSlots::IntersectionObserverRegistration { aObserver, -1 });
|
||||
}
|
||||
|
||||
void
|
||||
Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver)
|
||||
{
|
||||
nsTArray<nsDOMSlots::IntersectionObserverRegistration>* observers =
|
||||
RegisteredIntersectionObservers();
|
||||
for (uint32_t i = 0; i < observers->Length(); ++i) {
|
||||
nsDOMSlots::IntersectionObserverRegistration reg = observers->ElementAt(i);
|
||||
if (reg.observer == aObserver) {
|
||||
observers->RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold)
|
||||
{
|
||||
nsTArray<nsDOMSlots::IntersectionObserverRegistration>* observers =
|
||||
RegisteredIntersectionObservers();
|
||||
for (auto& reg : *observers) {
|
||||
if (reg.observer == aObserver && reg.previousThreshold != aThreshold) {
|
||||
reg.previousThreshold = aThreshold;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "Units.h"
|
||||
#include "DOMIntersectionObserver.h"
|
||||
|
||||
class nsIFrame;
|
||||
class nsIDOMMozNamedAttrMap;
|
||||
|
@ -61,6 +62,7 @@ namespace dom {
|
|||
struct AnimationFilter;
|
||||
struct ScrollIntoViewOptions;
|
||||
struct ScrollToOptions;
|
||||
class DOMIntersectionObserver;
|
||||
class ElementOrCSSPseudoElement;
|
||||
class UnrestrictedDoubleOrKeyframeAnimationOptions;
|
||||
} // namespace dom
|
||||
|
@ -1149,6 +1151,10 @@ public:
|
|||
// to it.
|
||||
void ClearDataset();
|
||||
|
||||
void RegisterIntersectionObserver(DOMIntersectionObserver* aObserver);
|
||||
void UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver);
|
||||
bool UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t threshold);
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Named-bools for use with SetAttrAndNotify to make call sites easier to
|
||||
|
@ -1362,6 +1368,8 @@ protected:
|
|||
nsDOMTokenList* GetTokenList(nsIAtom* aAtom,
|
||||
const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr);
|
||||
|
||||
nsTArray<nsDOMSlots::IntersectionObserverRegistration>* RegisteredIntersectionObservers();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get this element's client area rect in app units.
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace css {
|
|||
class Declaration;
|
||||
} // namespace css
|
||||
namespace dom {
|
||||
class DOMIntersectionObserver;
|
||||
class Element;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -344,6 +345,16 @@ public:
|
|||
* Web components custom element data.
|
||||
*/
|
||||
RefPtr<CustomElementData> mCustomElementData;
|
||||
|
||||
/**
|
||||
* Registered Intersection Observers on the element.
|
||||
*/
|
||||
struct IntersectionObserverRegistration {
|
||||
DOMIntersectionObserver* observer;
|
||||
int32_t previousThreshold;
|
||||
};
|
||||
|
||||
nsTArray<IntersectionObserverRegistration> mRegisteredIntersectionObservers;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
|
|
@ -165,6 +165,7 @@ EXPORTS.mozilla.dom += [
|
|||
'DOMError.h',
|
||||
'DOMException.h',
|
||||
'DOMImplementation.h',
|
||||
'DOMIntersectionObserver.h',
|
||||
'DOMMatrix.h',
|
||||
'DOMParser.h',
|
||||
'DOMPoint.h',
|
||||
|
@ -358,6 +359,8 @@ if CONFIG['MOZ_WEBRTC']:
|
|||
|
||||
# these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
|
||||
SOURCES += [
|
||||
# Several conflicts with other bindings.
|
||||
'DOMIntersectionObserver.cpp',
|
||||
# Because of OS X headers.
|
||||
'nsContentUtils.cpp',
|
||||
# this file doesn't like windows.h
|
||||
|
|
|
@ -1451,6 +1451,8 @@ nsDocument::~nsDocument()
|
|||
// Clear mObservers to keep it in sync with the mutationobserver list
|
||||
mObservers.Clear();
|
||||
|
||||
mIntersectionObservers.Clear();
|
||||
|
||||
if (mStyleSheetSetList) {
|
||||
mStyleSheetSetList->Disconnect();
|
||||
}
|
||||
|
@ -1720,6 +1722,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntersectionObservers)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSubImportLinks)
|
||||
|
||||
for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
|
||||
|
@ -1806,6 +1810,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
|
||||
|
||||
tmp->ClearAllBoxObjects();
|
||||
|
||||
if (tmp->mListenerManager) {
|
||||
|
@ -12306,6 +12312,51 @@ nsDocument::ReportUseCounters()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::AddIntersectionObserver(DOMIntersectionObserver* aObserver)
|
||||
{
|
||||
NS_ASSERTION(mIntersectionObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
|
||||
"Intersection observer already in the list");
|
||||
mIntersectionObservers.AppendElement(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::RemoveIntersectionObserver(DOMIntersectionObserver* aObserver)
|
||||
{
|
||||
mIntersectionObservers.RemoveElement(aObserver);
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::UpdateIntersectionObservations()
|
||||
{
|
||||
DOMHighResTimeStamp time = 0;
|
||||
if (nsPIDOMWindowInner* window = GetInnerWindow()) {
|
||||
Performance* perf = window->GetPerformance();
|
||||
if (perf) {
|
||||
time = perf->Now();
|
||||
}
|
||||
}
|
||||
for (const auto& observer : mIntersectionObservers) {
|
||||
observer->Update(this, time);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::ScheduleIntersectionObserverNotification()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> notification = NewRunnableMethod(this,
|
||||
&nsDocument::NotifyIntersectionObservers);
|
||||
NS_DispatchToCurrentThread(notification);
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::NotifyIntersectionObservers()
|
||||
{
|
||||
for (const auto& observer : mIntersectionObservers) {
|
||||
observer->Notify();
|
||||
}
|
||||
}
|
||||
|
||||
XPathEvaluator*
|
||||
nsIDocument::XPathEvaluator()
|
||||
{
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#include "ImportManager.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "CustomElementRegistry.h"
|
||||
#include "mozilla/dom/Performance.h"
|
||||
|
||||
#define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
|
||||
#define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
|
||||
|
@ -97,6 +98,8 @@ class BoxObject;
|
|||
class ImageTracker;
|
||||
struct LifecycleCallbacks;
|
||||
class CallbackFunction;
|
||||
class DOMIntersectionObserver;
|
||||
class Performance;
|
||||
|
||||
struct FullscreenRequest : public LinkedListElement<FullscreenRequest>
|
||||
{
|
||||
|
@ -773,6 +776,15 @@ public:
|
|||
|
||||
void ReportUseCounters();
|
||||
|
||||
virtual void AddIntersectionObserver(
|
||||
mozilla::dom::DOMIntersectionObserver* aObserver) override;
|
||||
virtual void RemoveIntersectionObserver(
|
||||
mozilla::dom::DOMIntersectionObserver* aObserver) override;
|
||||
virtual void UpdateIntersectionObservations() override;
|
||||
virtual void ScheduleIntersectionObserverNotification() override;
|
||||
virtual void NotifyIntersectionObservers() override;
|
||||
|
||||
|
||||
private:
|
||||
void AddOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet);
|
||||
nsRadioGroupStruct* GetRadioGroupInternal(const nsAString& aName) const;
|
||||
|
@ -1325,6 +1337,9 @@ protected:
|
|||
// Array of observers
|
||||
nsTObserverArray<nsIDocumentObserver*> mObservers;
|
||||
|
||||
// Array of intersection observers
|
||||
nsTArray<RefPtr<mozilla::dom::DOMIntersectionObserver>> mIntersectionObservers;
|
||||
|
||||
// Tracker for animations that are waiting to start.
|
||||
// nullptr until GetOrCreatePendingAnimationTracker is called.
|
||||
RefPtr<mozilla::PendingAnimationTracker> mPendingAnimationTracker;
|
||||
|
|
|
@ -127,6 +127,7 @@ class DocumentFragment;
|
|||
class DocumentTimeline;
|
||||
class DocumentType;
|
||||
class DOMImplementation;
|
||||
class DOMIntersectionObserver;
|
||||
class DOMStringList;
|
||||
class Element;
|
||||
struct ElementCreationOptions;
|
||||
|
@ -2844,6 +2845,15 @@ public:
|
|||
return mHasScrollLinkedEffect;
|
||||
}
|
||||
|
||||
virtual void AddIntersectionObserver(
|
||||
mozilla::dom::DOMIntersectionObserver* aObserver) = 0;
|
||||
virtual void RemoveIntersectionObserver(
|
||||
mozilla::dom::DOMIntersectionObserver* aObserver) = 0;
|
||||
|
||||
virtual void UpdateIntersectionObservations() = 0;
|
||||
virtual void ScheduleIntersectionObserverNotification() = 0;
|
||||
virtual void NotifyIntersectionObservers() = 0;
|
||||
|
||||
protected:
|
||||
bool GetUseCounter(mozilla::UseCounter aUseCounter)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
#target5 {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #f00;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="target5"></div>
|
||||
<script>
|
||||
var io = new IntersectionObserver(function (records) {
|
||||
parent.postMessage(records[0].rootBounds == null, 'http://mochi.test:8888');
|
||||
}, {});
|
||||
io.observe(document.getElementById("target5"));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -236,6 +236,7 @@ support-files =
|
|||
!/dom/xhr/tests/file_XHRSendData.sjs
|
||||
script_bug1238440.js
|
||||
file_blobURL_expiring.html
|
||||
intersectionobserver_subframe.html
|
||||
|
||||
[test_anchor_area_referrer.html]
|
||||
[test_anchor_area_referrer_changing.html]
|
||||
|
@ -697,6 +698,7 @@ skip-if = e10s || os != 'linux' || buildapp != 'browser' # Already tests multipr
|
|||
[test_img_referrer.html]
|
||||
[test_innersize_scrollport.html]
|
||||
[test_integer_attr_with_leading_zero.html]
|
||||
[test_intersectionobservers.html]
|
||||
[test_ipc_messagemanager_blob.html]
|
||||
[test_link_prefetch.html]
|
||||
skip-if = !e10s # Track Bug 1281415
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -581,6 +581,15 @@ DOMInterfaces = {
|
|||
'notflattened': True
|
||||
},
|
||||
|
||||
'IntersectionObserver': {
|
||||
'nativeType': 'mozilla::dom::DOMIntersectionObserver',
|
||||
},
|
||||
|
||||
'IntersectionObserverEntry': {
|
||||
'nativeType': 'mozilla::dom::DOMIntersectionObserverEntry',
|
||||
'headerFile': 'DOMIntersectionObserver.h',
|
||||
},
|
||||
|
||||
'KeyEvent': {
|
||||
'concrete': False
|
||||
},
|
||||
|
|
|
@ -103,3 +103,4 @@ MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <
|
|||
MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")
|
||||
MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the supported range for time values.")
|
||||
MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
|
||||
MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].")
|
||||
|
|
|
@ -29,4 +29,11 @@ interface DOMRectReadOnly {
|
|||
readonly attribute unrestricted double right;
|
||||
readonly attribute unrestricted double bottom;
|
||||
readonly attribute unrestricted double left;
|
||||
};
|
||||
};
|
||||
|
||||
dictionary DOMRectInit {
|
||||
unrestricted double x = 0;
|
||||
unrestricted double y = 0;
|
||||
unrestricted double width = 0;
|
||||
unrestricted double height = 0;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- 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/IntersectionObserver/
|
||||
*/
|
||||
|
||||
[ProbablyShortLivingObject]
|
||||
interface IntersectionObserverEntry {
|
||||
[Constant]
|
||||
readonly attribute DOMHighResTimeStamp time;
|
||||
[Constant]
|
||||
readonly attribute DOMRectReadOnly? rootBounds;
|
||||
[Constant]
|
||||
readonly attribute DOMRectReadOnly boundingClientRect;
|
||||
[Constant]
|
||||
readonly attribute DOMRectReadOnly intersectionRect;
|
||||
[Constant]
|
||||
readonly attribute double intersectionRatio;
|
||||
[Constant]
|
||||
readonly attribute Element target;
|
||||
};
|
||||
|
||||
[Constructor(IntersectionCallback intersectionCallback,
|
||||
optional IntersectionObserverInit options)]
|
||||
interface IntersectionObserver {
|
||||
[Constant]
|
||||
readonly attribute Element? root;
|
||||
[Constant]
|
||||
readonly attribute DOMString rootMargin;
|
||||
[Constant,Cached]
|
||||
readonly attribute sequence<double> thresholds;
|
||||
void observe(Element target);
|
||||
void unobserve(Element target);
|
||||
void disconnect();
|
||||
sequence<IntersectionObserverEntry> takeRecords();
|
||||
|
||||
[ChromeOnly]
|
||||
readonly attribute IntersectionCallback intersectionCallback;
|
||||
};
|
||||
|
||||
callback IntersectionCallback =
|
||||
void (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);
|
||||
|
||||
dictionary IntersectionObserverEntryInit {
|
||||
required DOMHighResTimeStamp time;
|
||||
required DOMRectInit rootBounds;
|
||||
required DOMRectInit boundingClientRect;
|
||||
required DOMRectInit intersectionRect;
|
||||
required Element target;
|
||||
};
|
||||
|
||||
dictionary IntersectionObserverInit {
|
||||
Element? root = null;
|
||||
DOMString rootMargin = "0px";
|
||||
(double or sequence<double>) threshold = 0;
|
||||
};
|
|
@ -281,6 +281,7 @@ WEBIDL_FILES = [
|
|||
'InputPort.webidl',
|
||||
'InputPortManager.webidl',
|
||||
'InspectorUtils.webidl',
|
||||
'IntersectionObserver.webidl',
|
||||
'IterableIterator.webidl',
|
||||
'KeyAlgorithm.webidl',
|
||||
'KeyboardEvent.webidl',
|
||||
|
|
|
@ -80,6 +80,7 @@ static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
|
|||
|
||||
#define DEFAULT_THROTTLED_FRAME_RATE 1
|
||||
#define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
|
||||
#define DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS 100
|
||||
// after 10 minutes, stop firing off inactive timers
|
||||
#define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
|
||||
|
||||
|
@ -976,6 +977,17 @@ nsRefreshDriver::GetMinRecomputeVisibilityInterval()
|
|||
return TimeDuration::FromMilliseconds(interval);
|
||||
}
|
||||
|
||||
/* static */ mozilla::TimeDuration
|
||||
nsRefreshDriver::GetMinNotifyIntersectionObserversInterval()
|
||||
{
|
||||
int32_t interval =
|
||||
Preferences::GetInt("layout.visibility.min-notify-intersection-observers-interval-ms", -1);
|
||||
if (interval <= 0) {
|
||||
interval = DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS;
|
||||
}
|
||||
return TimeDuration::FromMilliseconds(interval);
|
||||
}
|
||||
|
||||
double
|
||||
nsRefreshDriver::GetRefreshTimerInterval() const
|
||||
{
|
||||
|
@ -1018,6 +1030,8 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
|
|||
mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds(
|
||||
GetThrottledTimerInterval())),
|
||||
mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
|
||||
mMinNotifyIntersectionObserversInterval(
|
||||
GetMinNotifyIntersectionObserversInterval()),
|
||||
mThrottled(false),
|
||||
mNeedToRecomputeVisibility(false),
|
||||
mTestControllingRefreshes(false),
|
||||
|
@ -1038,6 +1052,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
|
|||
mMostRecentTick = mMostRecentRefresh;
|
||||
mNextThrottledFrameRequestTick = mMostRecentTick;
|
||||
mNextRecomputeVisibilityTick = mMostRecentTick;
|
||||
mNextNotifyIntersectionObserversTick = mMostRecentTick;
|
||||
|
||||
++sRefreshDriverCount;
|
||||
}
|
||||
|
@ -1828,6 +1843,22 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
|||
presShell->ScheduleApproximateFrameVisibilityUpdateNow();
|
||||
}
|
||||
|
||||
bool notifyIntersectionObservers = false;
|
||||
if (aNowTime >= mNextNotifyIntersectionObserversTick) {
|
||||
mNextNotifyIntersectionObserversTick =
|
||||
aNowTime + mMinNotifyIntersectionObserversInterval;
|
||||
notifyIntersectionObservers = true;
|
||||
}
|
||||
nsCOMArray<nsIDocument> documents;
|
||||
CollectDocuments(mPresContext->Document(), &documents);
|
||||
for (int32_t i = 0; i < documents.Count(); ++i) {
|
||||
nsIDocument* doc = documents[i];
|
||||
doc->UpdateIntersectionObservations();
|
||||
if (notifyIntersectionObservers) {
|
||||
doc->ScheduleIntersectionObserverNotification();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform notification to imgIRequests subscribed to listen
|
||||
* for refresh events.
|
||||
|
|
|
@ -368,6 +368,7 @@ private:
|
|||
static double GetThrottledTimerInterval();
|
||||
|
||||
static mozilla::TimeDuration GetMinRecomputeVisibilityInterval();
|
||||
static mozilla::TimeDuration GetMinNotifyIntersectionObserversInterval();
|
||||
|
||||
bool HaveFrameRequestCallbacks() const {
|
||||
return mFrameRequestCallbackDocs.Length() != 0;
|
||||
|
@ -403,6 +404,8 @@ private:
|
|||
// flush since the last time we did it.
|
||||
const mozilla::TimeDuration mMinRecomputeVisibilityInterval;
|
||||
|
||||
const mozilla::TimeDuration mMinNotifyIntersectionObserversInterval;
|
||||
|
||||
bool mThrottled;
|
||||
bool mNeedToRecomputeVisibility;
|
||||
bool mTestControllingRefreshes;
|
||||
|
@ -433,6 +436,7 @@ private:
|
|||
mozilla::TimeStamp mTickStart;
|
||||
mozilla::TimeStamp mNextThrottledFrameRequestTick;
|
||||
mozilla::TimeStamp mNextRecomputeVisibilityTick;
|
||||
mozilla::TimeStamp mNextNotifyIntersectionObserversTick;
|
||||
|
||||
// separate arrays for each flush type we support
|
||||
ObserverArray mObservers[3];
|
||||
|
|
|
@ -232,6 +232,12 @@ public:
|
|||
nsCSSValue& aValue,
|
||||
bool aSuppressErrors /* false */);
|
||||
|
||||
bool ParseMarginString(const nsSubstring& aBuffer,
|
||||
nsIURI* aURL, // for error reporting
|
||||
uint32_t aLineNumber, // for error reporting
|
||||
nsCSSValue& aValue,
|
||||
bool aSuppressErrors /* false */);
|
||||
|
||||
nsresult ParseSelectorString(const nsSubstring& aSelectorString,
|
||||
nsIURI* aURL, // for error reporting
|
||||
uint32_t aLineNumber, // for error reporting
|
||||
|
@ -1158,7 +1164,8 @@ protected:
|
|||
void AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue);
|
||||
bool ParseBoxProperties(const nsCSSPropertyID aPropIDs[]);
|
||||
bool ParseGroupedBoxProperty(int32_t aVariantMask,
|
||||
nsCSSValue& aValue);
|
||||
nsCSSValue& aValue,
|
||||
uint32_t aRestrictions);
|
||||
bool ParseBoxCornerRadius(const nsCSSPropertyID aPropID);
|
||||
bool ParseBoxCornerRadiiInternals(nsCSSValue array[]);
|
||||
bool ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[]);
|
||||
|
@ -2272,6 +2279,32 @@ CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
|
|||
return colorParsed;
|
||||
}
|
||||
|
||||
bool
|
||||
CSSParserImpl::ParseMarginString(const nsSubstring& aBuffer,
|
||||
nsIURI* aURI, // for error reporting
|
||||
uint32_t aLineNumber, // for error reporting
|
||||
nsCSSValue& aValue,
|
||||
bool aSuppressErrors /* false */)
|
||||
{
|
||||
nsCSSScanner scanner(aBuffer, aLineNumber);
|
||||
css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
|
||||
InitScanner(scanner, reporter, aURI, aURI, nullptr);
|
||||
|
||||
nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
|
||||
|
||||
// Parse a margin, and check that there's nothing else after it.
|
||||
bool marginParsed = ParseGroupedBoxProperty(VARIANT_LP, aValue, 0) && !GetToken(true);
|
||||
|
||||
if (aSuppressErrors) {
|
||||
CLEAR_ERROR();
|
||||
} else {
|
||||
OUTPUT_ERROR();
|
||||
}
|
||||
|
||||
ReleaseScanner();
|
||||
return marginParsed;
|
||||
}
|
||||
|
||||
bool
|
||||
CSSParserImpl::ParseFontFamilyListString(const nsSubstring& aBuffer,
|
||||
nsIURI* aURI, // for error reporting
|
||||
|
@ -11184,10 +11217,11 @@ CSSParserImpl::ParseBoxProperties(const nsCSSPropertyID aPropIDs[])
|
|||
}
|
||||
|
||||
// Similar to ParseBoxProperties, except there is only one property
|
||||
// with the result as its value, not four. Requires values be nonnegative.
|
||||
// with the result as its value, not four.
|
||||
bool
|
||||
CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
|
||||
/** outparam */ nsCSSValue& aValue)
|
||||
/** outparam */ nsCSSValue& aValue,
|
||||
uint32_t aRestrictions)
|
||||
{
|
||||
nsCSSRect& result = aValue.SetRectValue();
|
||||
|
||||
|
@ -11196,7 +11230,7 @@ CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
|
|||
CSSParseResult parseResult =
|
||||
ParseVariantWithRestrictions(result.*(nsCSSRect::sides[index]),
|
||||
aVariantMask, nullptr,
|
||||
CSS_PROPERTY_VALUE_NONNEGATIVE);
|
||||
aRestrictions);
|
||||
if (parseResult == CSSParseResult::NotFound) {
|
||||
break;
|
||||
}
|
||||
|
@ -13210,7 +13244,8 @@ CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
|
|||
|
||||
// Parse the box dimensions.
|
||||
nsCSSValue imageSliceBoxValue;
|
||||
if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) {
|
||||
if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue,
|
||||
CSS_PROPERTY_VALUE_NONNEGATIVE)) {
|
||||
if (!hasFill && aConsumedTokens) {
|
||||
*aConsumedTokens = false;
|
||||
}
|
||||
|
@ -13254,7 +13289,7 @@ CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
|
|||
}
|
||||
|
||||
// Parse the box dimensions.
|
||||
if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
|
||||
if (!ParseGroupedBoxProperty(VARIANT_ALPN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -13277,7 +13312,7 @@ CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
|
|||
}
|
||||
|
||||
// Parse the box dimensions.
|
||||
if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
|
||||
if (!ParseGroupedBoxProperty(VARIANT_LN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -17994,6 +18029,17 @@ nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
|
|||
ParseColorString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSSParser::ParseMarginString(const nsSubstring& aBuffer,
|
||||
nsIURI* aURI,
|
||||
uint32_t aLineNumber,
|
||||
nsCSSValue& aValue,
|
||||
bool aSuppressErrors /* false */)
|
||||
{
|
||||
return static_cast<CSSParserImpl*>(mImpl)->
|
||||
ParseMarginString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString,
|
||||
nsIURI* aURI,
|
||||
|
|
|
@ -205,6 +205,18 @@ public:
|
|||
nsCSSValue& aValue,
|
||||
bool aSuppressErrors = false);
|
||||
|
||||
/**
|
||||
* Parse aBuffer into a nsCSSValue |aValue|. Will return false
|
||||
* if aBuffer is not a valid CSS margin specification.
|
||||
* One can use nsRuleNode::GetRectValue to compute an nsCSSRect from
|
||||
* the returned nsCSSValue.
|
||||
*/
|
||||
bool ParseMarginString(const nsSubstring& aBuffer,
|
||||
nsIURI* aURL,
|
||||
uint32_t aLineNumber,
|
||||
nsCSSValue& aValue,
|
||||
bool aSuppressErrors = false);
|
||||
|
||||
/**
|
||||
* Parse aBuffer into a selector list. On success, caller must
|
||||
* delete *aSelectorList when done with it.
|
||||
|
|
|
@ -63,7 +63,10 @@ enum nsCSSPropertyID {
|
|||
eCSSPropertyExtra_x_auto_value,
|
||||
|
||||
// Extra value to represent custom properties (--*).
|
||||
eCSSPropertyExtra_variable
|
||||
eCSSPropertyExtra_variable,
|
||||
|
||||
// Extra value for use in the DOM API's
|
||||
eCSSProperty_DOM
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
|
|
|
@ -1218,7 +1218,8 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
|
|||
// eCSSProperty_UNKNOWN gets used for some recursive calls below.
|
||||
MOZ_ASSERT((0 <= aProperty &&
|
||||
aProperty <= eCSSProperty_COUNT_no_shorthands) ||
|
||||
aProperty == eCSSProperty_UNKNOWN,
|
||||
aProperty == eCSSProperty_UNKNOWN ||
|
||||
aProperty == eCSSProperty_DOM,
|
||||
"property ID out of range");
|
||||
|
||||
nsCSSUnit unit = GetUnit();
|
||||
|
@ -2429,7 +2430,8 @@ nsCSSRect::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
|
|||
|
||||
if (eCSSProperty_border_image_slice == aProperty ||
|
||||
eCSSProperty_border_image_width == aProperty ||
|
||||
eCSSProperty_border_image_outset == aProperty) {
|
||||
eCSSProperty_border_image_outset == aProperty ||
|
||||
eCSSProperty_DOM == aProperty) {
|
||||
NS_NAMED_LITERAL_STRING(space, " ");
|
||||
|
||||
mTop.AppendToString(aProperty, aResult, aSerialization);
|
||||
|
|
|
@ -665,6 +665,10 @@ public:
|
|||
{ return eCSSUnit_Point <= aUnit && aUnit <= eCSSUnit_Pixel; }
|
||||
bool IsPixelLengthUnit() const
|
||||
{ return IsPixelLengthUnit(mUnit); }
|
||||
static bool IsPercentLengthUnit(nsCSSUnit aUnit)
|
||||
{ return aUnit == eCSSUnit_Percent; }
|
||||
bool IsPercentLengthUnit()
|
||||
{ return IsPercentLengthUnit(mUnit); }
|
||||
static bool IsFloatUnit(nsCSSUnit aUnit)
|
||||
{ return eCSSUnit_Number <= aUnit; }
|
||||
bool IsAngularUnit() const
|
||||
|
|
Загрузка…
Ссылка в новой задаче