зеркало из https://github.com/mozilla/gecko-dev.git
342 строки
13 KiB
C++
342 строки
13 KiB
C++
/* -*- 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 "FrameMetrics.h"
|
|
|
|
#include <ostream>
|
|
|
|
#include "gfxUtils.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsStyleStruct.h"
|
|
#include "mozilla/WritingModes.h"
|
|
#include "mozilla/gfx/Types.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
const ScrollableLayerGuid::ViewID ScrollableLayerGuid::NULL_SCROLL_ID = 0;
|
|
|
|
std::ostream& operator<<(std::ostream& aStream, const FrameMetrics& aMetrics) {
|
|
aStream << "{ [cb=" << aMetrics.GetCompositionBounds()
|
|
<< "] [sr=" << aMetrics.GetScrollableRect()
|
|
<< "] [s=" << aMetrics.GetVisualScrollOffset();
|
|
if (aMetrics.GetVisualScrollUpdateType() != FrameMetrics::eNone) {
|
|
aStream << "] [vd=" << aMetrics.GetVisualDestination();
|
|
}
|
|
aStream << "] [dp=" << aMetrics.GetDisplayPort()
|
|
<< "] [rcs=" << aMetrics.GetBoundingCompositionSize()
|
|
<< "] [v=" << aMetrics.GetLayoutViewport()
|
|
<< nsPrintfCString("] [z=(ld=%.3f r=%.3f",
|
|
aMetrics.GetDevPixelsPerCSSPixel().scale,
|
|
aMetrics.GetPresShellResolution())
|
|
.get()
|
|
<< " cr=" << aMetrics.GetCumulativeResolution()
|
|
<< " z=" << aMetrics.GetZoom()
|
|
<< " t=" << aMetrics.GetTransformToAncestorScale() << " )] [u=("
|
|
<< (int)aMetrics.GetVisualScrollUpdateType() << " "
|
|
<< aMetrics.GetScrollGeneration()
|
|
<< ")] scrollId=" << aMetrics.GetScrollId();
|
|
if (aMetrics.IsRootContent()) {
|
|
aStream << " [rcd]";
|
|
}
|
|
aStream << " }";
|
|
return aStream;
|
|
}
|
|
|
|
void FrameMetrics::RecalculateLayoutViewportOffset() {
|
|
// For subframes, the visual and layout viewports coincide, so just
|
|
// keep the layout viewport offset in sync with the visual one.
|
|
if (!mIsRootContent) {
|
|
mLayoutViewport.MoveTo(GetVisualScrollOffset());
|
|
return;
|
|
}
|
|
// For the root, the two viewports can diverge, but the layout
|
|
// viewport needs to keep enclosing the visual viewport.
|
|
KeepLayoutViewportEnclosingVisualViewport(GetVisualViewport(),
|
|
mScrollableRect, mLayoutViewport);
|
|
}
|
|
|
|
/* static */
|
|
void FrameMetrics::KeepLayoutViewportEnclosingVisualViewport(
|
|
const CSSRect& aVisualViewport, const CSSRect& aScrollableRect,
|
|
CSSRect& aLayoutViewport) {
|
|
// If the visual viewport is contained within the layout viewport, we don't
|
|
// need to make any adjustments, so we can exit early.
|
|
//
|
|
// Additionally, if the composition bounds changes (due to an orientation
|
|
// change, window resize, etc.), it may take a few frames for aLayoutViewport
|
|
// to update and during that time, the visual viewport may be larger than the
|
|
// layout viewport. In such situations, we take an early exit if the visual
|
|
// viewport contains the layout viewport.
|
|
if (aLayoutViewport.Contains(aVisualViewport) ||
|
|
aVisualViewport.Contains(aLayoutViewport)) {
|
|
return;
|
|
}
|
|
|
|
// If visual viewport size is greater than the layout viewport, move the
|
|
// layout viewport such that it remains inside the visual viewport. Otherwise,
|
|
// move the layout viewport such that the visual viewport is contained
|
|
// inside the layout viewport.
|
|
if ((aLayoutViewport.Width() < aVisualViewport.Width() &&
|
|
!FuzzyEqualsMultiplicative(aLayoutViewport.Width(),
|
|
aVisualViewport.Width())) ||
|
|
(aLayoutViewport.Height() < aVisualViewport.Height() &&
|
|
!FuzzyEqualsMultiplicative(aLayoutViewport.Height(),
|
|
aVisualViewport.Height()))) {
|
|
if (aLayoutViewport.X() < aVisualViewport.X()) {
|
|
// layout viewport moves right
|
|
aLayoutViewport.MoveToX(aVisualViewport.X());
|
|
} else if (aVisualViewport.XMost() < aLayoutViewport.XMost()) {
|
|
// layout viewport moves left
|
|
aLayoutViewport.MoveByX(aVisualViewport.XMost() -
|
|
aLayoutViewport.XMost());
|
|
}
|
|
if (aLayoutViewport.Y() < aVisualViewport.Y()) {
|
|
// layout viewport moves down
|
|
aLayoutViewport.MoveToY(aVisualViewport.Y());
|
|
} else if (aVisualViewport.YMost() < aLayoutViewport.YMost()) {
|
|
// layout viewport moves up
|
|
aLayoutViewport.MoveByY(aVisualViewport.YMost() -
|
|
aLayoutViewport.YMost());
|
|
}
|
|
} else {
|
|
if (aVisualViewport.X() < aLayoutViewport.X()) {
|
|
aLayoutViewport.MoveToX(aVisualViewport.X());
|
|
} else if (aLayoutViewport.XMost() < aVisualViewport.XMost()) {
|
|
aLayoutViewport.MoveByX(aVisualViewport.XMost() -
|
|
aLayoutViewport.XMost());
|
|
}
|
|
if (aVisualViewport.Y() < aLayoutViewport.Y()) {
|
|
aLayoutViewport.MoveToY(aVisualViewport.Y());
|
|
} else if (aLayoutViewport.YMost() < aVisualViewport.YMost()) {
|
|
aLayoutViewport.MoveByY(aVisualViewport.YMost() -
|
|
aLayoutViewport.YMost());
|
|
}
|
|
}
|
|
|
|
// Regardless of any adjustment above, the layout viewport is not allowed
|
|
// to go outside the scrollable rect.
|
|
aLayoutViewport = aLayoutViewport.MoveInsideAndClamp(aScrollableRect);
|
|
}
|
|
|
|
/* static */
|
|
CSSRect FrameMetrics::CalculateScrollRange(
|
|
const CSSRect& aScrollableRect, const ParentLayerRect& aCompositionBounds,
|
|
const CSSToParentLayerScale& aZoom) {
|
|
CSSSize scrollPortSize =
|
|
CalculateCompositedSizeInCssPixels(aCompositionBounds, aZoom);
|
|
CSSRect scrollRange = aScrollableRect;
|
|
scrollRange.SetWidth(
|
|
std::max(scrollRange.Width() - scrollPortSize.width, 0.0f));
|
|
scrollRange.SetHeight(
|
|
std::max(scrollRange.Height() - scrollPortSize.height, 0.0f));
|
|
return scrollRange;
|
|
}
|
|
|
|
/* static */
|
|
CSSSize FrameMetrics::CalculateCompositedSizeInCssPixels(
|
|
const ParentLayerRect& aCompositionBounds,
|
|
const CSSToParentLayerScale& aZoom) {
|
|
if (aZoom == CSSToParentLayerScale(0)) {
|
|
return CSSSize(); // avoid division by zero
|
|
}
|
|
return aCompositionBounds.Size() / aZoom;
|
|
}
|
|
|
|
bool FrameMetrics::ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate) {
|
|
// In applying a main-thread scroll update, try to preserve the relative
|
|
// offset between the visual and layout viewports.
|
|
CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset();
|
|
MOZ_ASSERT(IsRootContent() || relativeOffset == CSSPoint());
|
|
// We need to set the two offsets together, otherwise a subsequent
|
|
// RecalculateLayoutViewportOffset() could see divergent layout and
|
|
// visual offsets.
|
|
bool offsetChanged = SetLayoutScrollOffset(aUpdate.GetDestination());
|
|
offsetChanged |=
|
|
ClampAndSetVisualScrollOffset(aUpdate.GetDestination() + relativeOffset);
|
|
return offsetChanged;
|
|
}
|
|
|
|
CSSPoint FrameMetrics::ApplyRelativeScrollUpdateFrom(
|
|
const ScrollPositionUpdate& aUpdate) {
|
|
MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::Relative);
|
|
CSSPoint origin = GetVisualScrollOffset();
|
|
CSSPoint delta = (aUpdate.GetDestination() - aUpdate.GetSource());
|
|
ClampAndSetVisualScrollOffset(origin + delta);
|
|
return GetVisualScrollOffset() - origin;
|
|
}
|
|
|
|
CSSPoint FrameMetrics::ApplyPureRelativeScrollUpdateFrom(
|
|
const ScrollPositionUpdate& aUpdate) {
|
|
MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::PureRelative);
|
|
CSSPoint origin = GetVisualScrollOffset();
|
|
ClampAndSetVisualScrollOffset(origin + aUpdate.GetDelta());
|
|
return GetVisualScrollOffset() - origin;
|
|
}
|
|
|
|
void FrameMetrics::UpdatePendingScrollInfo(const ScrollPositionUpdate& aInfo) {
|
|
// We only get this "pending scroll info" for paint-skip transactions,
|
|
// but PureRelative position updates always trigger a full paint, so
|
|
// we should never enter this code with a PureRelative update type. For
|
|
// the other types, the destination field on the ScrollPositionUpdate will
|
|
// tell us the final layout scroll position on the main thread.
|
|
MOZ_ASSERT(aInfo.GetType() != ScrollUpdateType::PureRelative);
|
|
|
|
// In applying a main-thread scroll update, try to preserve the relative
|
|
// offset between the visual and layout viewports.
|
|
CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset();
|
|
MOZ_ASSERT(IsRootContent() || relativeOffset == CSSPoint());
|
|
|
|
SetLayoutScrollOffset(aInfo.GetDestination());
|
|
ClampAndSetVisualScrollOffset(aInfo.GetDestination() + relativeOffset);
|
|
mScrollGeneration = aInfo.GetGeneration();
|
|
}
|
|
|
|
ScrollSnapInfo::ScrollSnapInfo()
|
|
: mScrollSnapStrictnessX(StyleScrollSnapStrictness::None),
|
|
mScrollSnapStrictnessY(StyleScrollSnapStrictness::None) {}
|
|
|
|
bool ScrollSnapInfo::HasScrollSnapping() const {
|
|
return mScrollSnapStrictnessY != StyleScrollSnapStrictness::None ||
|
|
mScrollSnapStrictnessX != StyleScrollSnapStrictness::None;
|
|
}
|
|
|
|
bool ScrollSnapInfo::HasSnapPositions() const {
|
|
if (!HasScrollSnapping()) {
|
|
return false;
|
|
}
|
|
|
|
for (const auto& target : mSnapTargets) {
|
|
if ((target.mSnapPositionX &&
|
|
mScrollSnapStrictnessX != StyleScrollSnapStrictness::None) ||
|
|
(target.mSnapPositionY &&
|
|
mScrollSnapStrictnessY != StyleScrollSnapStrictness::None)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ScrollSnapInfo::InitializeScrollSnapStrictness(
|
|
WritingMode aWritingMode, const nsStyleDisplay* aDisplay) {
|
|
if (aDisplay->mScrollSnapType.strictness == StyleScrollSnapStrictness::None) {
|
|
return;
|
|
}
|
|
|
|
mScrollSnapStrictnessX = StyleScrollSnapStrictness::None;
|
|
mScrollSnapStrictnessY = StyleScrollSnapStrictness::None;
|
|
|
|
switch (aDisplay->mScrollSnapType.axis) {
|
|
case StyleScrollSnapAxis::X:
|
|
mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
|
|
break;
|
|
case StyleScrollSnapAxis::Y:
|
|
mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
|
|
break;
|
|
case StyleScrollSnapAxis::Block:
|
|
if (aWritingMode.IsVertical()) {
|
|
mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
|
|
} else {
|
|
mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
|
|
}
|
|
break;
|
|
case StyleScrollSnapAxis::Inline:
|
|
if (aWritingMode.IsVertical()) {
|
|
mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
|
|
} else {
|
|
mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
|
|
}
|
|
break;
|
|
case StyleScrollSnapAxis::Both:
|
|
mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
|
|
mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& aStream,
|
|
const OverscrollBehavior& aBehavior) {
|
|
switch (aBehavior) {
|
|
case OverscrollBehavior::Auto: {
|
|
aStream << "auto";
|
|
break;
|
|
}
|
|
case OverscrollBehavior::Contain: {
|
|
aStream << "contain";
|
|
break;
|
|
}
|
|
case OverscrollBehavior::None: {
|
|
aStream << "none";
|
|
break;
|
|
}
|
|
}
|
|
return aStream;
|
|
}
|
|
|
|
OverscrollBehaviorInfo::OverscrollBehaviorInfo()
|
|
: mBehaviorX(OverscrollBehavior::Auto),
|
|
mBehaviorY(OverscrollBehavior::Auto) {}
|
|
|
|
static OverscrollBehavior ToOverscrollBehavior(
|
|
StyleOverscrollBehavior aBehavior) {
|
|
switch (aBehavior) {
|
|
case StyleOverscrollBehavior::Auto:
|
|
return OverscrollBehavior::Auto;
|
|
case StyleOverscrollBehavior::Contain:
|
|
return OverscrollBehavior::Contain;
|
|
case StyleOverscrollBehavior::None:
|
|
return OverscrollBehavior::None;
|
|
}
|
|
MOZ_ASSERT_UNREACHABLE("Invalid overscroll behavior");
|
|
return OverscrollBehavior::Auto;
|
|
}
|
|
|
|
OverscrollBehaviorInfo OverscrollBehaviorInfo::FromStyleConstants(
|
|
StyleOverscrollBehavior aBehaviorX, StyleOverscrollBehavior aBehaviorY) {
|
|
OverscrollBehaviorInfo result;
|
|
result.mBehaviorX = ToOverscrollBehavior(aBehaviorX);
|
|
result.mBehaviorY = ToOverscrollBehavior(aBehaviorY);
|
|
return result;
|
|
}
|
|
|
|
bool OverscrollBehaviorInfo::operator==(
|
|
const OverscrollBehaviorInfo& aOther) const {
|
|
return mBehaviorX == aOther.mBehaviorX && mBehaviorY == aOther.mBehaviorY;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& aStream,
|
|
const OverscrollBehaviorInfo& aInfo) {
|
|
if (aInfo.mBehaviorX == aInfo.mBehaviorY) {
|
|
aStream << aInfo.mBehaviorX;
|
|
} else {
|
|
aStream << "{ x=" << aInfo.mBehaviorX << ", y=" << aInfo.mBehaviorY << " }";
|
|
}
|
|
return aStream;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& aStream,
|
|
const ScrollMetadata& aMetadata) {
|
|
aStream << "{ [metrics=" << aMetadata.GetMetrics()
|
|
<< "] [color=" << aMetadata.GetBackgroundColor();
|
|
if (aMetadata.GetScrollParentId() != ScrollableLayerGuid::NULL_SCROLL_ID) {
|
|
aStream << "] [scrollParent=" << aMetadata.GetScrollParentId();
|
|
}
|
|
aStream << "] [overscroll=" << aMetadata.GetOverscrollBehavior() << "] ["
|
|
<< aMetadata.GetScrollUpdates().Length() << " scrollupdates"
|
|
<< "] }";
|
|
return aStream;
|
|
}
|
|
|
|
void ScrollMetadata::SetBackgroundColor(
|
|
const gfx::sRGBColor& aBackgroundColor) {
|
|
mBackgroundColor = gfx::ToDeviceColor(aBackgroundColor);
|
|
}
|
|
|
|
StaticAutoPtr<const ScrollMetadata> ScrollMetadata::sNullMetadata;
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|