# HG changeset patch # User Robert O'Callahan # Date 1379672463 -43200 # Node ID d938cfe41ca84c390414b4f77b52a89098f43750 # Parent 21f293fc8d34737fe2816c286364e779fd8674fb Bug 917755. Part 1: Add nsLayoutUtils::TransformCSSPoints and nsLayoutUtils::GetFirstNonAnonymousFrame. r=matspal diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1888,16 +1888,76 @@ nsLayoutUtils::GetTransformToAncestor(ns if (!parent->Preserves3DChildren()) { ctm.ProjectTo2D(); } ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent); } return ctm; } +static nsIFrame* +FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2) +{ + nsAutoTArray ancestors1; + nsAutoTArray ancestors2; + nsIFrame* commonAncestor = nullptr; + if (aFrame1->PresContext() == aFrame2->PresContext()) { + commonAncestor = aFrame1->PresContext()->PresShell()->GetRootFrame(); + } + for (nsIFrame* f = aFrame1; f != commonAncestor; + f = nsLayoutUtils::GetCrossDocParentFrame(f)) { + ancestors1.AppendElement(f); + } + for (nsIFrame* f = aFrame2; f != commonAncestor; + f = nsLayoutUtils::GetCrossDocParentFrame(f)) { + ancestors2.AppendElement(f); + } + uint32_t minLengths = std::min(ancestors1.Length(), ancestors2.Length()); + for (uint32_t i = 1; i <= minLengths; ++i) { + if (ancestors1[ancestors1.Length() - i] == ancestors2[ancestors2.Length() - i]) { + commonAncestor = ancestors1[ancestors1.Length() - i]; + } else { + break; + } + } + return commonAncestor; +} + +nsLayoutUtils::TransformResult +nsLayoutUtils::TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame, + uint32_t aPointCount, CSSPoint* aPoints) +{ + nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame); + if (!nearestCommonAncestor) { + return NO_COMMON_ANCESTOR; + } + gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor); + if (downToDest.IsSingular()) { + return NONINVERTIBLE_TRANSFORM; + } + downToDest.Invert(); + gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor); + CSSToLayoutDeviceScale devPixelsPerCSSPixelFromFrame( + double(nsPresContext::AppUnitsPerCSSPixel())/ + aFromFrame->PresContext()->AppUnitsPerDevPixel()); + CSSToLayoutDeviceScale devPixelsPerCSSPixelToFrame( + double(nsPresContext::AppUnitsPerCSSPixel())/ + aToFrame->PresContext()->AppUnitsPerDevPixel()); + for (uint32_t i = 0; i < aPointCount; ++i) { + LayoutDevicePoint devPixels = aPoints[i] * devPixelsPerCSSPixelFromFrame; + gfxPoint toDevPixels = downToDest.ProjectPoint( + upToAncestor.ProjectPoint(gfxPoint(devPixels.x, devPixels.y))); + // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct + // answer instead of some inaccuracy multiplying a number by its reciprocal. + aPoints[i] = LayoutDevicePoint(toDevPixels.x, toDevPixels.y) / + devPixelsPerCSSPixelToFrame; + } + return TRANSFORM_SUCCEEDED; +} + bool nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame, gfx3DMatrix* aTransform) { // FIXME/bug 796690: we can sometimes compute a transform in these // cases, it just increases complexity considerably. Punt for now. if (aFrame->Preserves3DChildren() || aFrame->HasTransformGetter()) { return false; @@ -2594,16 +2654,53 @@ void nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback) { while (aFrame) { AddBoxesForFrame(aFrame, aCallback); aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame); } } +nsIFrame* +nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame) +{ + while (aFrame) { + nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo(); + + if (pseudoType == nsCSSAnonBoxes::tableOuter) { + nsIFrame* f = GetFirstNonAnonymousFrame(aFrame->GetFirstPrincipalChild()); + if (f) { + return f; + } + nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList); + if (kid) { + f = GetFirstNonAnonymousFrame(kid); + if (f) { + return f; + } + } + } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || + pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || + pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock || + pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) { + for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { + nsIFrame* f = GetFirstNonAnonymousFrame(kid); + if (f) { + return f; + } + } + } else { + return aFrame; + } + + aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame); + } + return nullptr; +} + struct BoxToRect : public nsLayoutUtils::BoxCallback { nsIFrame* mRelativeTo; nsLayoutUtils::RectCallback* mCallback; uint32_t mFlags; BoxToRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback, uint32_t aFlags) : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {} diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1,16 +1,39 @@ /* -*- Mode: C++; 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/. */ #ifndef nsLayoutUtils_h__ #define nsLayoutUtils_h__ +#include "mozilla/MemoryReporting.h" +#include "nsChangeHint.h" +#include "nsAutoPtr.h" +#include "nsFrameList.h" +#include "nsThreadUtils.h" +#include "nsIPrincipal.h" +#include "GraphicsFilter.h" +#include "nsCSSPseudoElements.h" +#include "FrameMetrics.h" +#include "gfx3DMatrix.h" +#include "nsIWidget.h" +#include "nsCSSProperty.h" +#include "nsStyleCoord.h" +#include "nsStyleConsts.h" +#include "nsGkAtoms.h" +#include "nsRuleNode.h" +#include "imgIContainer.h" +#include "mozilla/gfx/2D.h" +#include "Units.h" + +#include +#include + class nsIFormControlFrame; class nsPresContext; class nsIContent; class nsIAtom; class nsIScrollableFrame; class nsIDOMEvent; class nsRegion; class nsDisplayListBuilder; @@ -25,42 +48,21 @@ class gfxDrawable; class nsView; class nsIFrame; class nsStyleCoord; class nsStyleCorners; class gfxContext; class nsPIDOMWindow; class imgIRequest; class nsIDocument; +class gfxPoint; struct nsStyleFont; struct nsStyleImageOrientation; struct nsOverflowAreas; -#include "mozilla/MemoryReporting.h" -#include "nsChangeHint.h" -#include "nsAutoPtr.h" -#include "nsFrameList.h" -#include "nsThreadUtils.h" -#include "nsIPrincipal.h" -#include "GraphicsFilter.h" -#include "nsCSSPseudoElements.h" -#include "FrameMetrics.h" -#include "gfx3DMatrix.h" -#include "nsIWidget.h" -#include "nsCSSProperty.h" -#include "nsStyleCoord.h" -#include "nsStyleConsts.h" -#include "nsGkAtoms.h" -#include "nsRuleNode.h" -#include "imgIContainer.h" -#include "mozilla/gfx/2D.h" - -#include -#include - namespace mozilla { class SVGImageContext; struct IntrinsicSize; struct ContainerLayerParameters; namespace dom { class DOMRectList; class Element; class HTMLImageElement; @@ -90,16 +92,17 @@ class nsLayoutUtils typedef mozilla::layers::Layer Layer; typedef mozilla::ContainerLayerParameters ContainerLayerParameters; typedef mozilla::gfx::SourceSurface SourceSurface; typedef mozilla::gfx::DrawTarget DrawTarget; public: typedef mozilla::layers::FrameMetrics FrameMetrics; typedef FrameMetrics::ViewID ViewID; + typedef mozilla::CSSPoint CSSPoint; /** * Finds previously assigned ViewID for the given content element, if any. * Returns whether a ViewID was previously assigned. */ static bool FindIDFor(const nsIContent* aContent, ViewID* aOutViewId); /** @@ -667,16 +670,34 @@ public: /** * Gets the transform for aFrame relative to aAncestor. Pass null for aAncestor * to go up to the root frame. */ static gfx3DMatrix GetTransformToAncestor(nsIFrame *aFrame, const nsIFrame *aAncestor); /** + * Transforms a list of points, given as gfxPoints representing CSS pixels, + * from aFromFrame to aToFrame, taking into account all relevant transformations + * on the frames up to (but excluding) their nearest common ancestor. + * If we encounter a transform that we need to invert but which is + * non-invertible, we return NONINVERTIBLE_TRANSFORM. If the frames have + * no common ancestor, we return NO_COMMON_ANCESTOR. + * If this returns TRANSFORM_SUCCEEDED, the points in aPoints are transformed + * in-place, otherwise they are untouched. + */ + enum TransformResult { + TRANSFORM_SUCCEEDED, + NO_COMMON_ANCESTOR, + NONINVERTIBLE_TRANSFORM + }; + static TransformResult TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame, + uint32_t aPointCount, CSSPoint* aPoints); + + /** * Return true if a "layer transform" could be computed for aFrame, * and optionally return the computed transform. The returned * transform is what would be set on the layer currently if a layers * transaction were opened at the time this helper is called. */ static bool GetLayerTransformForFrame(nsIFrame* aFrame, gfx3DMatrix* aTransform); @@ -866,16 +887,22 @@ public: * Collect all CSS boxes associated with aFrame and its * continuations, "drilling down" through outer table frames and * some anonymous blocks since they're not real CSS boxes. * If aFrame is null, no boxes are returned. * SVG frames return a single box, themselves. */ static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback); + /** + * Find the first frame descendant of aFrame (including aFrame) which is + * not an anonymous frame that getBoxQuads/getClientRects should ignore. + */ + static nsIFrame* GetFirstNonAnonymousFrame(nsIFrame* aFrame); + class RectCallback { public: virtual void AddRect(const nsRect& aRect) = 0; }; struct RectAccumulator : public RectCallback { nsRect mResultRect; nsRect mFirstRect;