layout/base/GeometryUtils.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "GeometryUtils.h"
     8 #include "mozilla/dom/DOMPointBinding.h"
     9 #include "mozilla/dom/GeometryUtilsBinding.h"
    10 #include "mozilla/dom/Element.h"
    11 #include "mozilla/dom/Text.h"
    12 #include "mozilla/dom/DOMPoint.h"
    13 #include "mozilla/dom/DOMQuad.h"
    14 #include "mozilla/dom/DOMRect.h"
    15 #include "nsIFrame.h"
    16 #include "nsGenericDOMDataNode.h"
    17 #include "nsCSSFrameConstructor.h"
    18 #include "nsLayoutUtils.h"
    19 #include "nsSVGUtils.h"
    21 using namespace mozilla;
    22 using namespace mozilla::dom;
    24 namespace mozilla {
    26 enum GeometryNodeType {
    27   GEOMETRY_NODE_ELEMENT,
    28   GEOMETRY_NODE_TEXT,
    29   GEOMETRY_NODE_DOCUMENT
    30 };
    32 static nsIFrame*
    33 GetFrameForNode(nsINode* aNode, GeometryNodeType aType)
    34 {
    35   nsIDocument* doc = aNode->OwnerDoc();
    36   doc->FlushPendingNotifications(Flush_Layout);
    37   switch (aType) {
    38   case GEOMETRY_NODE_ELEMENT:
    39     return aNode->AsContent()->GetPrimaryFrame();
    40   case GEOMETRY_NODE_TEXT: {
    41     nsIPresShell* presShell = doc->GetShell();
    42     if (presShell) {
    43       return presShell->FrameConstructor()->EnsureFrameForTextNode(
    44           static_cast<nsGenericDOMDataNode*>(aNode));
    45     }
    46     return nullptr;
    47   }
    48   case GEOMETRY_NODE_DOCUMENT: {
    49     nsIPresShell* presShell = doc->GetShell();
    50     return presShell ? presShell->GetRootFrame() : nullptr;
    51   }
    52   default:
    53     MOZ_ASSERT(false, "Unknown GeometryNodeType");
    54     return nullptr;
    55   }
    56 }
    58 static nsIFrame*
    59 GetFrameForGeometryNode(const Optional<OwningGeometryNode>& aGeometryNode,
    60                         nsINode* aDefaultNode)
    61 {
    62   if (!aGeometryNode.WasPassed()) {
    63     return GetFrameForNode(aDefaultNode->OwnerDoc(), GEOMETRY_NODE_DOCUMENT);
    64   }
    66   const OwningGeometryNode& value = aGeometryNode.Value();
    67   if (value.IsElement()) {
    68     return GetFrameForNode(value.GetAsElement(), GEOMETRY_NODE_ELEMENT);
    69   }
    70   if (value.IsDocument()) {
    71     return GetFrameForNode(value.GetAsDocument(), GEOMETRY_NODE_DOCUMENT);
    72   }
    73   return GetFrameForNode(value.GetAsText(), GEOMETRY_NODE_TEXT);
    74 }
    76 static nsIFrame*
    77 GetFrameForGeometryNode(const GeometryNode& aGeometryNode)
    78 {
    79   if (aGeometryNode.IsElement()) {
    80     return GetFrameForNode(&aGeometryNode.GetAsElement(), GEOMETRY_NODE_ELEMENT);
    81   }
    82   if (aGeometryNode.IsDocument()) {
    83     return GetFrameForNode(&aGeometryNode.GetAsDocument(), GEOMETRY_NODE_DOCUMENT);
    84   }
    85   return GetFrameForNode(&aGeometryNode.GetAsText(), GEOMETRY_NODE_TEXT);
    86 }
    88 static nsIFrame*
    89 GetFrameForNode(nsINode* aNode)
    90 {
    91   if (aNode->IsElement()) {
    92     return GetFrameForNode(aNode, GEOMETRY_NODE_ELEMENT);
    93   }
    94   if (aNode == aNode->OwnerDoc()) {
    95     return GetFrameForNode(aNode, GEOMETRY_NODE_DOCUMENT);
    96   }
    97   NS_ASSERTION(aNode->IsNodeOfType(nsINode::eTEXT), "Unknown node type");
    98   return GetFrameForNode(aNode, GEOMETRY_NODE_TEXT);
    99 }
   101 static nsIFrame*
   102 GetFirstNonAnonymousFrameForGeometryNode(const Optional<OwningGeometryNode>& aNode,
   103                                          nsINode* aDefaultNode)
   104 {
   105   nsIFrame* f = GetFrameForGeometryNode(aNode, aDefaultNode);
   106   if (f) {
   107     f = nsLayoutUtils::GetFirstNonAnonymousFrame(f);
   108   }
   109   return f;
   110 }
   112 static nsIFrame*
   113 GetFirstNonAnonymousFrameForGeometryNode(const GeometryNode& aNode)
   114 {
   115   nsIFrame* f = GetFrameForGeometryNode(aNode);
   116   if (f) {
   117     f = nsLayoutUtils::GetFirstNonAnonymousFrame(f);
   118   }
   119   return f;
   120 }
   122 static nsIFrame*
   123 GetFirstNonAnonymousFrameForNode(nsINode* aNode)
   124 {
   125   nsIFrame* f = GetFrameForNode(aNode);
   126   if (f) {
   127     f = nsLayoutUtils::GetFirstNonAnonymousFrame(f);
   128   }
   129   return f;
   130 }
   132 /**
   133  * This can modify aFrame to point to a different frame. This is needed to
   134  * handle SVG, where SVG elements can only compute a rect that's valid with
   135  * respect to the "outer SVG" frame.
   136  */
   137 static nsRect
   138 GetBoxRectForFrame(nsIFrame** aFrame, CSSBoxType aType)
   139 {
   140   nsRect r;
   141   nsIFrame* f = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(*aFrame, &r);
   142   if (f) {
   143     // For SVG, the BoxType is ignored.
   144     *aFrame = f;
   145     return r;
   146   }
   148   f = *aFrame;
   149   switch (aType) {
   150   case CSSBoxType::Content: r = f->GetContentRectRelativeToSelf(); break;
   151   case CSSBoxType::Padding: r = f->GetPaddingRectRelativeToSelf(); break;
   152   case CSSBoxType::Border: r = nsRect(nsPoint(0, 0), f->GetSize()); break;
   153   case CSSBoxType::Margin: {
   154     r = nsRect(nsPoint(0, 0), f->GetSize());
   155     r.Inflate(f->GetUsedMargin());
   156     break;
   157   }
   158   default: MOZ_ASSERT(false, "unknown box type"); return r;
   159   }
   161   return r;
   162 }
   164 class AccumulateQuadCallback : public nsLayoutUtils::BoxCallback {
   165 public:
   166   AccumulateQuadCallback(nsISupports* aParentObject,
   167                          nsTArray<nsRefPtr<DOMQuad> >& aResult,
   168                          nsIFrame* aRelativeToFrame,
   169                          const nsPoint& aRelativeToBoxTopLeft,
   170                          CSSBoxType aBoxType)
   171     : mParentObject(aParentObject)
   172     , mResult(aResult)
   173     , mRelativeToFrame(aRelativeToFrame)
   174     , mRelativeToBoxTopLeft(aRelativeToBoxTopLeft)
   175     , mBoxType(aBoxType)
   176   {
   177   }
   179   virtual void AddBox(nsIFrame* aFrame) MOZ_OVERRIDE
   180   {
   181     nsIFrame* f = aFrame;
   182     nsRect box = GetBoxRectForFrame(&f, mBoxType);
   183     nsPoint appUnits[4] =
   184       { box.TopLeft(), box.TopRight(), box.BottomRight(), box.BottomLeft() };
   185     CSSPoint points[4];
   186     for (uint32_t i = 0; i < 4; ++i) {
   187       points[i] = CSSPoint(nsPresContext::AppUnitsToFloatCSSPixels(appUnits[i].x),
   188                            nsPresContext::AppUnitsToFloatCSSPixels(appUnits[i].y));
   189     }
   190     nsLayoutUtils::TransformResult rv =
   191       nsLayoutUtils::TransformPoints(f, mRelativeToFrame, 4, points);
   192     if (rv == nsLayoutUtils::TRANSFORM_SUCCEEDED) {
   193       CSSPoint delta(nsPresContext::AppUnitsToFloatCSSPixels(mRelativeToBoxTopLeft.x),
   194                      nsPresContext::AppUnitsToFloatCSSPixels(mRelativeToBoxTopLeft.y));
   195       for (uint32_t i = 0; i < 4; ++i) {
   196         points[i] -= delta;
   197       }
   198     } else {
   199       PodArrayZero(points);
   200     }
   201     mResult.AppendElement(new DOMQuad(mParentObject, points));
   202   }
   204   nsISupports* mParentObject;
   205   nsTArray<nsRefPtr<DOMQuad> >& mResult;
   206   nsIFrame* mRelativeToFrame;
   207   nsPoint mRelativeToBoxTopLeft;
   208   CSSBoxType mBoxType;
   209 };
   211 static nsPresContext*
   212 FindTopLevelPresContext(nsPresContext* aPC)
   213 {
   214   bool isChrome = aPC->IsChrome();
   215   nsPresContext* pc = aPC;
   216   for (;;) {
   217     nsPresContext* parent = pc->GetParentPresContext();
   218     if (!parent || parent->IsChrome() != isChrome) {
   219       return pc;
   220     }
   221     pc = parent;
   222   }
   223 }
   225 static bool
   226 CheckFramesInSameTopLevelBrowsingContext(nsIFrame* aFrame1, nsIFrame* aFrame2)
   227 {
   228   nsPresContext* pc1 = aFrame1->PresContext();
   229   nsPresContext* pc2 = aFrame2->PresContext();
   230   if (pc1 == pc2) {
   231     return true;
   232   }
   233   if (nsContentUtils::IsCallerChrome()) {
   234     return true;
   235   }
   236   if (FindTopLevelPresContext(pc1) == FindTopLevelPresContext(pc2)) {
   237     return true;
   238   }
   239   return false;
   240 }
   242 void GetBoxQuads(nsINode* aNode,
   243                  const dom::BoxQuadOptions& aOptions,
   244                  nsTArray<nsRefPtr<DOMQuad> >& aResult,
   245                  ErrorResult& aRv)
   246 {
   247   nsIFrame* frame = GetFrameForNode(aNode);
   248   if (!frame) {
   249     // No boxes to return
   250     return;
   251   }
   252   nsIDocument* ownerDoc = aNode->OwnerDoc();
   253   nsIFrame* relativeToFrame =
   254     GetFirstNonAnonymousFrameForGeometryNode(aOptions.mRelativeTo, ownerDoc);
   255   if (!relativeToFrame) {
   256     aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   257     return;
   258   }
   259   if (!CheckFramesInSameTopLevelBrowsingContext(frame, relativeToFrame)) {
   260     aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   261     return;
   262   }
   263   // GetBoxRectForFrame can modify relativeToFrame so call it first.
   264   nsPoint relativeToTopLeft =
   265       GetBoxRectForFrame(&relativeToFrame, CSSBoxType::Border).TopLeft();
   266   AccumulateQuadCallback callback(ownerDoc, aResult, relativeToFrame,
   267                                   relativeToTopLeft, aOptions.mBox);
   268   nsLayoutUtils::GetAllInFlowBoxes(frame, &callback);
   269 }
   271 static void
   272 TransformPoints(nsINode* aTo, const GeometryNode& aFrom,
   273                 uint32_t aPointCount, CSSPoint* aPoints,
   274                 const ConvertCoordinateOptions& aOptions, ErrorResult& aRv)
   275 {
   276   nsIFrame* fromFrame = GetFirstNonAnonymousFrameForGeometryNode(aFrom);
   277   nsIFrame* toFrame = GetFirstNonAnonymousFrameForNode(aTo);
   278   if (!fromFrame || !toFrame) {
   279     aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   280     return;
   281   }
   282   if (!CheckFramesInSameTopLevelBrowsingContext(fromFrame, toFrame)) {
   283     aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   284     return;
   285   }
   287   nsPoint fromOffset = GetBoxRectForFrame(&fromFrame, aOptions.mFromBox).TopLeft();
   288   nsPoint toOffset = GetBoxRectForFrame(&toFrame, aOptions.mToBox).TopLeft();
   289   CSSPoint fromOffsetGfx(nsPresContext::AppUnitsToFloatCSSPixels(fromOffset.x),
   290                          nsPresContext::AppUnitsToFloatCSSPixels(fromOffset.y));
   291   for (uint32_t i = 0; i < aPointCount; ++i) {
   292     aPoints[i] += fromOffsetGfx;
   293   }
   294   nsLayoutUtils::TransformResult rv =
   295     nsLayoutUtils::TransformPoints(fromFrame, toFrame, aPointCount, aPoints);
   296   if (rv == nsLayoutUtils::TRANSFORM_SUCCEEDED) {
   297     CSSPoint toOffsetGfx(nsPresContext::AppUnitsToFloatCSSPixels(toOffset.x),
   298                          nsPresContext::AppUnitsToFloatCSSPixels(toOffset.y));
   299     for (uint32_t i = 0; i < aPointCount; ++i) {
   300       aPoints[i] -= toOffsetGfx;
   301     }
   302   } else {
   303     PodZero(aPoints, aPointCount);
   304   }
   305 }
   307 already_AddRefed<DOMQuad>
   308 ConvertQuadFromNode(nsINode* aTo, dom::DOMQuad& aQuad,
   309                     const GeometryNode& aFrom,
   310                     const dom::ConvertCoordinateOptions& aOptions,
   311                     ErrorResult& aRv)
   312 {
   313   CSSPoint points[4];
   314   for (uint32_t i = 0; i < 4; ++i) {
   315     DOMPoint* p = aQuad.Point(i);
   316     if (p->W() != 1.0 || p->Z() != 0.0) {
   317       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   318       return nullptr;
   319     }
   320     points[i] = CSSPoint(p->X(), p->Y());
   321   }
   322   TransformPoints(aTo, aFrom, 4, points, aOptions, aRv);
   323   if (aRv.Failed()) {
   324     return nullptr;
   325   }
   326   nsRefPtr<DOMQuad> result = new DOMQuad(aTo->GetParentObject().mObject, points);
   327   return result.forget();
   328 }
   330 already_AddRefed<DOMQuad>
   331 ConvertRectFromNode(nsINode* aTo, dom::DOMRectReadOnly& aRect,
   332                     const GeometryNode& aFrom,
   333                     const dom::ConvertCoordinateOptions& aOptions,
   334                     ErrorResult& aRv)
   335 {
   336   CSSPoint points[4];
   337   double x = aRect.X(), y = aRect.Y(), w = aRect.Width(), h = aRect.Height();
   338   points[0] = CSSPoint(x, y);
   339   points[1] = CSSPoint(x + w, y);
   340   points[2] = CSSPoint(x + w, y + h);
   341   points[3] = CSSPoint(x, y + h);
   342   TransformPoints(aTo, aFrom, 4, points, aOptions, aRv);
   343   if (aRv.Failed()) {
   344     return nullptr;
   345   }
   346   nsRefPtr<DOMQuad> result = new DOMQuad(aTo->GetParentObject().mObject, points);
   347   return result.forget();
   348 }
   350 already_AddRefed<DOMPoint>
   351 ConvertPointFromNode(nsINode* aTo, const dom::DOMPointInit& aPoint,
   352                      const GeometryNode& aFrom,
   353                      const dom::ConvertCoordinateOptions& aOptions,
   354                      ErrorResult& aRv)
   355 {
   356   if (aPoint.mW != 1.0 || aPoint.mZ != 0.0) {
   357     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   358     return nullptr;
   359   }
   360   CSSPoint point(aPoint.mX, aPoint.mY);
   361   TransformPoints(aTo, aFrom, 1, &point, aOptions, aRv);
   362   if (aRv.Failed()) {
   363     return nullptr;
   364   }
   365   nsRefPtr<DOMPoint> result = new DOMPoint(aTo->GetParentObject().mObject, point.x, point.y);
   366   return result.forget();
   367 }
   369 }

mercurial