/* -*- 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/. */
/* static */ bool nsLayoutUtils::HasAnimationOfPropertySet( const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet,
EffectSet* aEffectSet) {
MOZ_ASSERT(
!aEffectSet || EffectSet::GetForFrame(aFrame, aPropertySet) == aEffectSet, "The EffectSet, if supplied, should match what we would otherwise fetch");
if (!aEffectSet) { return nsLayoutUtils::HasAnimationOfPropertySet(aFrame, aPropertySet);
}
if (!aEffectSet->MayHaveTransformAnimation() &&
aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties())) { returnfalse;
}
if (!aEffectSet->MayHaveOpacityAnimation() &&
aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())) { returnfalse;
}
// We fetch the effects for the style frame here since this method is called // by RestyleManager::AddLayerChangesForAnimation which takes care to apply // the relevant hints to the primary frame as needed.
EffectSet* effects = EffectSet::GetForStyleFrame(aStyleFrame); if (!effects) { return properties;
}
AnimationPerformanceWarning::Type warning; if (!EffectCompositor::AllowCompositorAnimationsOnFrame(aStyleFrame,
warning)) { return properties;
}
// If properties only have motion-path properties, we have to make sure they // have effects. i.e. offset-path is not none or we have offset-path // animations. if (properties.IsSubsetOf(nsCSSPropertyIDSet::MotionPathProperties()) &&
!properties.HasProperty(eCSSProperty_offset_path) &&
aStyleFrame->StyleDisplay()->mOffsetPath.IsNone()) {
properties.Empty();
}
return properties;
}
staticfloat GetSuitableScale(float aMaxScale, float aMinScale,
nscoord aVisibleDimension,
nscoord aDisplayDimension) { float displayVisibleRatio = float(aDisplayDimension) / float(aVisibleDimension); // We want to rasterize based on the largest scale used during the // transform animation, unless that would make us rasterize something // larger than the screen. But we never want to go smaller than the // minimum scale over the animation. if (FuzzyEqualsMultiplicative(displayVisibleRatio, aMaxScale, .01f)) { // Using aMaxScale may make us rasterize something a fraction larger than // the screen. However, if aMaxScale happens to be the final scale of a // transform animation it is better to use aMaxScale so that for the // fraction of a second before we delayerize the composited texture it has // a better chance of being pixel aligned and composited without resampling // (avoiding visually clunky delayerization). return aMaxScale;
} return std::clamp(displayVisibleRatio, aMinScale, aMaxScale);
}
// The first value in this pair is the min scale, and the second one is the max // scale. using MinAndMaxScale = std::pair<MatrixScales, MatrixScales>;
// The final transform matrix is calculated by merging the final results of each // transform-like properties, so do the scale factors. In other words, the // potential min/max scales could be gotten by multiplying the max/min scales of // each properties. // // For example, there is an animation: // from { "transform: scale(1, 1)", "scale: 3, 3" }; // to { "transform: scale(2, 2)", "scale: 1, 1" }; // // the min scale is (1, 1) * (1, 1) = (1, 1), and // The max scale is (2, 2) * (3, 3) = (6, 6). // This means we multiply the min/max scale factor of transform property and the // min/max scale factor of scale property to get the final max/min scale factor. static Array<MinAndMaxScale, 2> GetMinAndMaxScaleForAnimationProperty( const nsIFrame* aFrame, const nsTArray<RefPtr<dom::Animation>>& aAnimations) { // We use a fixed array to store the min/max scales for each property. // The first element in the array is for eCSSProperty_transform, and the // second one is for eCSSProperty_scale. const MinAndMaxScale defaultValue =
std::make_pair(MatrixScales(std::numeric_limits<float>::max(),
std::numeric_limits<float>::max()),
MatrixScales(std::numeric_limits<float>::min(),
std::numeric_limits<float>::min()));
Array<MinAndMaxScale, 2> minAndMaxScales(defaultValue, defaultValue);
for (dom::Animation* anim : aAnimations) { // This method is only expected to be passed animations that are running on // the compositor and we only pass playing animations to the compositor, // which are, by definition, "relevant" animations (animations that are // not yet finished or which are filling forwards).
MOZ_ASSERT(anim->IsRelevant());
const dom::KeyframeEffect* effect =
anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr;
MOZ_ASSERT(effect, "A playing animation should have a keyframe effect"); for (const AnimationProperty& prop : effect->Properties()) { if (prop.mProperty.mID != eCSSProperty_transform &&
prop.mProperty.mID != eCSSProperty_scale) { continue;
}
// We need to factor in the scale of the base style if the base style // will be used on the compositor. const AnimationValue& baseStyle = effect->BaseStyle(prop.mProperty); if (!baseStyle.IsNull()) {
UpdateMinMaxScale(aFrame, baseStyle, scales);
}
for (const AnimationPropertySegment& segment : prop.mSegments) { // In case of add or accumulate composite, StyleAnimationValue does // not have a valid value. if (segment.HasReplaceableFromValue()) {
UpdateMinMaxScale(aFrame, segment.mFromValue, scales);
}
if (segment.HasReplaceableToValue()) {
UpdateMinMaxScale(aFrame, segment.mToValue, scales);
}
}
}
}
// This might cause an issue if users use std::numeric_limits<float>::min() // (or max()) as the scale value. However, in this case, we may render an // extreme small (or large) element, so this may not be a problem. If so, // please fix this.
MatrixScales maxScale(std::numeric_limits<float>::min(),
std::numeric_limits<float>::min());
MatrixScales minScale(std::numeric_limits<float>::max(),
std::numeric_limits<float>::max());
// Iterate the slots to get the final scale value. for (constauto& pair : minAndMaxScales) { const MatrixScales& currMinScale = pair.first; const MatrixScales& currMaxScale = pair.second;
if (isUnset(currMaxScale, currMinScale)) { // We don't have this animation property, so skip. continue;
}
if (isUnset(maxScale, minScale)) { // Initialize maxScale and minScale.
maxScale = currMaxScale;
minScale = currMinScale;
} else { // The scale factors of each transform-like property should be multiplied // by others because we merge their sampled values as a final matrix by // matrix multiplication, so here we multiply the scale factors by the // previous one to get the possible max and min scale factors.
maxScale = maxScale * currMaxScale;
minScale = minScale * currMinScale;
}
}
if (isUnset(maxScale, minScale)) { // We didn't encounter any transform-like property. return MatrixScales();
}
// Use the pres shell root frame to get the display root frame. This skips // the early exit in |nsLayoutUtils::GetDisplayRootFrame()| for popup frames. const nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame(); if (!rootFrame) { return nullptr;
}
bool nsLayoutUtils::UsesAsyncScrolling(nsIFrame* aFrame) { #ifdef MOZ_WIDGET_ANDROID // We always have async scrolling for android returntrue; #endif
return AsyncPanZoomEnabled(aFrame);
}
bool nsLayoutUtils::AsyncPanZoomEnabled(const nsIFrame* aFrame) { // We use this as a shortcut, since if the compositor will never use APZ, // no widget will either. if (!gfxPlatform::AsyncPanZoomEnabled()) { returnfalse;
}
bool nsLayoutUtils::AllowZoomingForDocument( const mozilla::dom::Document* aDocument) { if (aDocument->GetPresShell() &&
!aDocument->GetPresShell()->AsyncPanZoomEnabled()) { returnfalse;
} // True if we allow zooming for all documents on this platform, or if we are // in RDM.
BrowsingContext* bc = aDocument->GetBrowsingContext(); return StaticPrefs::apz_allow_zooming() || (bc && bc->InRDMPane());
}
staticbool HasVisibleAnonymousContents(Document* aDoc) { for (RefPtr<AnonymousContent>& ac : aDoc->GetAnonymousContents()) { // We check to see if the anonymous content node has a frame. If it doesn't, // that means that's not visible to the user because e.g. it's display:none. // For now we assume that if it has a frame, it is visible. We might be able // to refine this further by adding complexity if it turns out this // condition results in a lot of false positives. if (ac->Host()->GetPrimaryFrame()) { returntrue;
}
} returnfalse;
}
bool nsLayoutUtils::ShouldDisableApzForElement(nsIContent* aContent) { if (!aContent) { returnfalse;
}
if (aContent->GetProperty(nsGkAtoms::apzDisabled)) { returntrue;
}
Document* doc = aContent->GetComposedDoc(); if (PresShell* rootPresShell =
APZCCallbackHelper::GetRootContentDocumentPresShellForContent(
aContent)) { if (Document* rootDoc = rootPresShell->GetDocument()) {
nsIFrame* rootScrollContainerFrame =
rootPresShell->GetRootScrollContainerFrame();
nsIContent* rootContent = rootScrollContainerFrame
? rootScrollContainerFrame->GetContent()
: rootDoc->GetDocumentElement(); // For the AccessibleCaret and other anonymous contents: disable APZ on // any scrollable subframes that are not the root scrollframe of a // document, if the document has any visible anonymous contents. // // If we find this is triggering in too many scenarios then we might // want to tighten this check further. The main use cases for which we // want to disable APZ as of this writing are listed in bug 1316318. if (aContent != rootContent && HasVisibleAnonymousContents(rootDoc)) { returntrue;
}
}
}
if (!doc) { returnfalse;
}
if (PresShell* presShell = doc->GetPresShell()) { if (RefPtr<AccessibleCaretEventHub> eventHub =
presShell->GetAccessibleCaretEventHub()) { // Disable APZ for all elements if AccessibleCaret tells us to do so. if (eventHub->ShouldDisableApz()) { returntrue;
}
}
}
if (aChildFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
nsIFrame* pif = aChildFrame->GetPrevInFlow(); if (pif->GetParent() == aChildFrame->GetParent()) {
id = FrameChildListID::ExcessOverflowContainers;
} else {
id = FrameChildListID::OverflowContainers;
}
} else {
LayoutFrameType childType = aChildFrame->Type(); if (LayoutFrameType::TableColGroup == childType) {
id = FrameChildListID::ColGroup;
} elseif (aChildFrame->IsTableCaption()) {
id = FrameChildListID::Caption;
} else {
id = FrameChildListID::Principal;
}
}
#ifdef DEBUG // Verify that the frame is actually in that child list or in the // corresponding overflow list.
nsContainerFrame* parent = aChildFrame->GetParent(); bool found = parent->GetChildList(id).ContainsFrame(aChildFrame); if (!found) {
found = parent->GetChildList(FrameChildListID::Overflow)
.ContainsFrame(aChildFrame);
MOZ_ASSERT(found, "not in child list");
} #endif
nsIFrame* frame = aContent->GetPrimaryFrame(); if (!frame) { return;
}
if (!frame->StyleContent()->NonAltContentItems().IsEmpty()) { for (nsIFrame* child : frame->PrincipalChildList()) {
nsIFrame::RenderedText text = child->GetRenderedText();
aText += text.mString;
} return;
}
if (!frame->StyleList()->mListStyleImage.IsNone()) { // ::marker is an image, so use default bullet character. staticconst char16_t kDiscMarkerString[] = {0x2022, ' ', 0};
aText.AssignLiteral(kDiscMarkerString); return;
}
// Return true if aFrame1 is after aFrame2 staticbool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2) {
nsIFrame* f = aFrame2; do {
f = f->GetNextSibling(); if (f == aFrame1) { returntrue;
}
} while (f); returnfalse;
}
// static
int32_t nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
nsIFrame* aFrame2,
nsIFrame* aCommonAncestor) {
MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
MOZ_ASSERT(aFrame2, "aFrame2 must not be null");
// static
int32_t nsLayoutUtils::DoCompareTreePosition(
nsIFrame* aFrame1, nsIFrame* aFrame2, nsTArray<nsIFrame*>& aFrame2Ancestors,
nsIFrame* aCommonAncestor) {
MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
MOZ_ASSERT(aFrame2, "aFrame2 must not be null");
nsPresContext* presContext = aFrame1->PresContext(); if (presContext != aFrame2->PresContext()) {
NS_ERROR("no common ancestor at all, different documents"); return 0;
}
AutoTArray<nsIFrame*, 20> frame1Ancestors; if (aCommonAncestor &&
!FillAncestors(aFrame1, aCommonAncestor, &frame1Ancestors)) { // We reached the root of the frame tree ... if aCommonAncestor was set, // it is wrong return DoCompareTreePosition(aFrame1, aFrame2, nullptr);
}
if (last1 < 0) { if (last2 < 0) {
NS_ASSERTION(aFrame1 == aFrame2, "internal error?"); return 0;
} // aFrame1 is an ancestor of aFrame2 return -1;
}
if (last2 < 0) { // aFrame2 is an ancestor of aFrame1 return 1;
}
nsIFrame* ancestor1 = frame1Ancestors[last1];
nsIFrame* ancestor2 = aFrame2Ancestors[last2]; // Now we should be able to walk sibling chains to find which one is first if (IsFrameAfter(ancestor2, ancestor1)) { return -1;
} if (IsFrameAfter(ancestor1, ancestor2)) { return 1;
}
NS_WARNING("Frames were in different child lists???"); return 0;
}
ScrollableLayerGuid::ViewID nsLayoutUtils::ScrollIdForRootScrollFrame(
nsPresContext* aPresContext) {
ViewID id = ScrollableLayerGuid::NULL_SCROLL_ID; if (nsIFrame* rootScrollFrame =
aPresContext->PresShell()->GetRootScrollContainerFrame()) { if (nsIContent* content = rootScrollFrame->GetContent()) {
id = FindOrCreateIDFor(content);
}
} return id;
}
// static
ScrollContainerFrame* nsLayoutUtils::GetNearestScrollableFrameForDirection(
nsIFrame* aFrame, ScrollDirections aDirections) {
NS_ASSERTION(
aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame"); // FIXME Bug 1714720 : This nearest scroll target is not going to work over // process boundaries, in such cases we need to hand over in APZ side. for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f); if (scrollContainerFrame) {
ScrollDirections directions =
scrollContainerFrame
->GetAvailableScrollingDirectionsForUserInputEvents(); if (aDirections.contains(ScrollDirection::eVertical)) { if (directions.contains(ScrollDirection::eVertical)) { return scrollContainerFrame;
}
} if (aDirections.contains(ScrollDirection::eHorizontal)) { if (directions.contains(ScrollDirection::eHorizontal)) { return scrollContainerFrame;
}
}
}
} return nullptr;
}
for (nsIFrame* f = aFrame; f; f = GetNextFrame(f)) { if (aClipFrameCheck && aClipFrameCheck(f)) { return f;
}
if ((aFlags & nsLayoutUtils::SCROLLABLE_STOP_AT_PAGE) && f->IsPageFrame()) { break;
}
// TODO: We should also stop at popup frames other than // SCROLLABLE_ONLY_ASYNC_SCROLLABLE cases. if ((aFlags & nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE) &&
f->IsMenuPopupFrame()) { break;
}
if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f)) { if (aFlags & nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE) { if (scrollContainerFrame->WantAsyncScroll()) { return f;
}
} else {
ScrollStyles ss = scrollContainerFrame->GetScrollStyles(); if ((aFlags & nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN) ||
ss.mVertical != StyleOverflow::Hidden ||
ss.mHorizontal != StyleOverflow::Hidden) { return f;
}
} if (aFlags & nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT) {
PresShell* presShell = f->PresShell(); if (presShell->GetRootScrollContainerFrame() == f &&
presShell->GetDocument() &&
presShell->GetDocument()->IsRootDisplayDocument()) { return f;
}
}
} if ((aFlags & nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT) &&
f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
nsLayoutUtils::IsReallyFixedPos(f)) { return f->PresShell()->GetRootScrollContainerFrame();
}
} return nullptr;
}
// static
ScrollContainerFrame* nsLayoutUtils::GetNearestScrollContainerFrame(
nsIFrame* aFrame, uint32_t aFlags) {
nsIFrame* found = GetNearestScrollableOrOverflowClipFrame(aFrame, aFlags); if (!found) { return nullptr;
}
return do_QueryFrame(found);
}
// static
nsIFrame* nsLayoutUtils::GetNearestOverflowClipFrame(nsIFrame* aFrame) { return GetNearestScrollableOrOverflowClipFrame(
aFrame, SCROLLABLE_SAME_DOC | SCROLLABLE_INCLUDE_HIDDEN,
[](const nsIFrame* currentFrame) -> bool { // In cases of SVG Inner/Outer frames it basically clips descendants // unless overflow: visible is explicitly specified.
LayoutFrameType type = currentFrame->Type(); return ((type == LayoutFrameType::SVGOuterSVG ||
type == LayoutFrameType::SVGInnerSVG) &&
(currentFrame->StyleDisplay()->mOverflowX !=
StyleOverflow::Visible &&
currentFrame->StyleDisplay()->mOverflowY !=
StyleOverflow::Visible));
});
}
// static bool nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
ComputedStyle* aComputedStyle,
PseudoStyleType aPseudoElement,
nsPresContext* aPresContext) {
MOZ_ASSERT(aPresContext, "Must have a prescontext");
nsView* view = frame->GetView(); if (view) {
nsIWidget* frameWidget = view->GetWidget(); if (frameWidget && frameWidget == aWidget) { // Special case this cause it happens a lot. // This also fixes bug 664707, events in the extra-special case of select // dropdown popups that are transformed.
nsPresContext* presContext = frame->PresContext();
nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x),
presContext->DevPixelsToAppUnits(aPoint.y)); return pt - view->ViewToWidgetOffset();
}
}
/* If we walk up the frame tree and discover that any of the frames are * transformed, we need to do extra work to convert from the global * space to the local space.
*/ const nsIFrame* rootFrame = frame; bool transformFound = false; for (const nsIFrame* f = frame; f;
f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) { if (f->IsTransformed() || ViewportUtils::IsZoomedContentRoot(f)) {
transformFound = true;
}
if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
}
// Convert from root document app units to app units of the document aFrame // is in.
int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
int32_t localAPD = frame->PresContext()->AppUnitsPerDevPixel();
widgetToView = widgetToView.ScaleToOtherAppUnits(rootAPD, localAPD);
/* If we encountered a transform, we can't do simple arithmetic to figure * out how to convert back to aFrame's coordinates and must use the CTM.
*/ if (transformFound || frame->IsInSVGTextSubtree()) { return nsLayoutUtils::TransformRootPointToFrame(ViewportType::Visual,
aFrame, widgetToView);
}
/* Otherwise, all coordinate systems are translations of one another, * so we can just subtract out the difference.
*/ return widgetToView - frame->GetOffsetToCrossDoc(rootFrame);
}
RefPtr<nsPresContext> presContext = aPresShell->GetPresContext(); if (!presContext) { return;
}
nsIFrame* targetFrame = presContext->EventStateManager()->GetEventTarget(); if (!targetFrame) { return;
}
WidgetEvent* openingEvent = nullptr; // For popupshowing events, redirect via the original mouse event // that triggered the popup to open. if (aEvent->mMessage == eXULPopupShowing) { if (auto* pm = nsXULPopupManager::GetInstance()) { if (Event* openingPopupEvent = pm->GetOpeningPopupEvent()) {
openingEvent = openingPopupEvent->WidgetEventPtr();
}
}
}
nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
openingEvent ? openingEvent : aEvent, RelativeTo{targetFrame});
if (aContainer) { // TODO: This result may be useful to change to Selection. However, this // may return improper node (e.g., native anonymous node) for the // Selection. Perhaps, this should take Selection optionally and // if it's specified, needs to check if it's proper for the // Selection.
nsCOMPtr<nsIContent> container =
targetFrame->GetContentOffsetsFromPoint(point).content; if (container && (!container->ChromeOnlyAccess() ||
nsContentUtils::CanAccessNativeAnon())) {
container.forget(aContainer);
}
} if (aOffset) {
*aOffset = targetFrame->GetContentOffsetsFromPoint(point).offset;
}
}
// Here we try to make sure that the resulting nsRect will continue to cover // as much of the area that was covered by the original gfx Rect as possible.
// We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this // range: float end = aStart + aSize;
aStart = std::clamp(aStart, float(nscoord_MIN), float(nscoord_MAX));
end = std::clamp(end, float(nscoord_MIN), float(nscoord_MAX));
aSize = end - aStart;
// We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height() // can't return a value greater than nscoord_MAX. If aSize is greater than // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect // centered: if (MOZ_UNLIKELY(std::isnan(aSize))) { // Can happen if aStart is -inf and aSize is +inf for example.
aStart = 0.0f;
aSize = float(nscoord_MAX);
} elseif (aSize > float(nscoord_MAX)) { float excess = aSize - float(nscoord_MAX);
excess /= 2;
aStart += excess;
aSize = float(nscoord_MAX);
}
}
/** * Given a gfxFloat, constrains its value to be between nscoord_MIN and * nscoord_MAX. * * @param aVal The value to constrain (in/out)
*/ staticvoid ConstrainToCoordValues(gfxFloat& aVal) { if (aVal <= nscoord_MIN) {
aVal = nscoord_MIN;
} elseif (aVal >= nscoord_MAX) {
aVal = nscoord_MAX;
}
}
// Clamp the end points to within nscoord range
::ConstrainToCoordValues(aStart);
::ConstrainToCoordValues(max);
aSize = max - aStart; // If the width if still greater than the max nscoord, then bring both // endpoints in by the same amount until it fits. if (MOZ_UNLIKELY(std::isnan(aSize))) { // Can happen if aStart is -inf and aSize is +inf for example.
aStart = 0.0f;
aSize = nscoord_MAX;
} elseif (aSize > nscoord_MAX) {
gfxFloat excess = aSize - nscoord_MAX;
excess /= 2;
nsRegion nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect, const nscoord aRadii[8], const nsRect& aContainedRect) { // rectFullHeight and rectFullWidth together will approximately contain // the total area of the frame minus the rounded corners.
nsRect rectFullHeight = aRoundedRect;
nscoord xDiff = std::max(aRadii[eCornerTopLeftX], aRadii[eCornerBottomLeftX]);
rectFullHeight.x += xDiff;
rectFullHeight.width -=
std::max(aRadii[eCornerTopRightX], aRadii[eCornerBottomRightX]) + xDiff;
nsRect r1;
r1.IntersectRect(rectFullHeight, aContainedRect);
nsIntRegion nsLayoutUtils::RoundedRectIntersectIntRect( const nsIntRect& aRoundedRect, const RectCornerRadii& aCornerRadii, const nsIntRect& aContainedRect) { // rectFullHeight and rectFullWidth together will approximately contain // the total area of the frame minus the rounded corners.
nsIntRect rectFullHeight = aRoundedRect;
uint32_t xDiff =
std::max(aCornerRadii.TopLeft().width, aCornerRadii.BottomLeft().width);
rectFullHeight.x += xDiff;
rectFullHeight.width -= std::max(aCornerRadii.TopRight().width,
aCornerRadii.BottomRight().width) +
xDiff;
nsIntRect r1;
r1.IntersectRect(rectFullHeight, aContainedRect);
// Helper for RoundedRectIntersectsRect. staticbool CheckCorner(nscoord aXOffset, nscoord aYOffset, nscoord aXRadius,
nscoord aYRadius) {
MOZ_ASSERT(aXOffset > 0 && aYOffset > 0, "must not pass nonpositives to CheckCorner");
MOZ_ASSERT(aXRadius >= 0 && aYRadius >= 0, "must not pass negatives to CheckCorner");
// Avoid floating point math unless we're either (1) within the // quarter-ellipse area at the rounded corner or (2) outside the // rounding. if (aXOffset >= aXRadius || aYOffset >= aYRadius) { returntrue;
}
// Convert coordinates to a unit circle with (0,0) as the center of // curvature, and see if we're inside the circle or outside. float scaledX = float(aXRadius - aXOffset) / float(aXRadius); float scaledY = float(aYRadius - aYOffset) / float(aYRadius); return scaledX * scaledX + scaledY * scaledY < 1.0f;
}
// distances from this edge of aRoundedRect to opposite edge of aTestRect, // which we know are positive due to the Intersects check above.
nsMargin insets;
insets.top = aTestRect.YMost() - aRoundedRect.y;
insets.right = aRoundedRect.XMost() - aTestRect.x;
insets.bottom = aRoundedRect.YMost() - aTestRect.y;
insets.left = aTestRect.XMost() - aRoundedRect.x;
// Check whether the bottom-right corner of aTestRect is inside the // top left corner of aBounds when rounded by aRadii, etc. If any // corner is not, then fail; otherwise succeed. return CheckCorner(insets.left, insets.top, aRadii[eCornerTopLeftX],
aRadii[eCornerTopLeftY]) &&
CheckCorner(insets.right, insets.top, aRadii[eCornerTopRightX],
aRadii[eCornerTopRightY]) &&
CheckCorner(insets.right, insets.bottom, aRadii[eCornerBottomRightX],
aRadii[eCornerBottomRightY]) &&
CheckCorner(insets.left, insets.bottom, aRadii[eCornerBottomLeftX],
aRadii[eCornerBottomLeftY]);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.20Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.