layout/base/GeometryUtils.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/base/GeometryUtils.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,369 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "GeometryUtils.h"
    1.10 +
    1.11 +#include "mozilla/dom/DOMPointBinding.h"
    1.12 +#include "mozilla/dom/GeometryUtilsBinding.h"
    1.13 +#include "mozilla/dom/Element.h"
    1.14 +#include "mozilla/dom/Text.h"
    1.15 +#include "mozilla/dom/DOMPoint.h"
    1.16 +#include "mozilla/dom/DOMQuad.h"
    1.17 +#include "mozilla/dom/DOMRect.h"
    1.18 +#include "nsIFrame.h"
    1.19 +#include "nsGenericDOMDataNode.h"
    1.20 +#include "nsCSSFrameConstructor.h"
    1.21 +#include "nsLayoutUtils.h"
    1.22 +#include "nsSVGUtils.h"
    1.23 +
    1.24 +using namespace mozilla;
    1.25 +using namespace mozilla::dom;
    1.26 +
    1.27 +namespace mozilla {
    1.28 +
    1.29 +enum GeometryNodeType {
    1.30 +  GEOMETRY_NODE_ELEMENT,
    1.31 +  GEOMETRY_NODE_TEXT,
    1.32 +  GEOMETRY_NODE_DOCUMENT
    1.33 +};
    1.34 +
    1.35 +static nsIFrame*
    1.36 +GetFrameForNode(nsINode* aNode, GeometryNodeType aType)
    1.37 +{
    1.38 +  nsIDocument* doc = aNode->OwnerDoc();
    1.39 +  doc->FlushPendingNotifications(Flush_Layout);
    1.40 +  switch (aType) {
    1.41 +  case GEOMETRY_NODE_ELEMENT:
    1.42 +    return aNode->AsContent()->GetPrimaryFrame();
    1.43 +  case GEOMETRY_NODE_TEXT: {
    1.44 +    nsIPresShell* presShell = doc->GetShell();
    1.45 +    if (presShell) {
    1.46 +      return presShell->FrameConstructor()->EnsureFrameForTextNode(
    1.47 +          static_cast<nsGenericDOMDataNode*>(aNode));
    1.48 +    }
    1.49 +    return nullptr;
    1.50 +  }
    1.51 +  case GEOMETRY_NODE_DOCUMENT: {
    1.52 +    nsIPresShell* presShell = doc->GetShell();
    1.53 +    return presShell ? presShell->GetRootFrame() : nullptr;
    1.54 +  }
    1.55 +  default:
    1.56 +    MOZ_ASSERT(false, "Unknown GeometryNodeType");
    1.57 +    return nullptr;
    1.58 +  }
    1.59 +}
    1.60 +
    1.61 +static nsIFrame*
    1.62 +GetFrameForGeometryNode(const Optional<OwningGeometryNode>& aGeometryNode,
    1.63 +                        nsINode* aDefaultNode)
    1.64 +{
    1.65 +  if (!aGeometryNode.WasPassed()) {
    1.66 +    return GetFrameForNode(aDefaultNode->OwnerDoc(), GEOMETRY_NODE_DOCUMENT);
    1.67 +  }
    1.68 +
    1.69 +  const OwningGeometryNode& value = aGeometryNode.Value();
    1.70 +  if (value.IsElement()) {
    1.71 +    return GetFrameForNode(value.GetAsElement(), GEOMETRY_NODE_ELEMENT);
    1.72 +  }
    1.73 +  if (value.IsDocument()) {
    1.74 +    return GetFrameForNode(value.GetAsDocument(), GEOMETRY_NODE_DOCUMENT);
    1.75 +  }
    1.76 +  return GetFrameForNode(value.GetAsText(), GEOMETRY_NODE_TEXT);
    1.77 +}
    1.78 +
    1.79 +static nsIFrame*
    1.80 +GetFrameForGeometryNode(const GeometryNode& aGeometryNode)
    1.81 +{
    1.82 +  if (aGeometryNode.IsElement()) {
    1.83 +    return GetFrameForNode(&aGeometryNode.GetAsElement(), GEOMETRY_NODE_ELEMENT);
    1.84 +  }
    1.85 +  if (aGeometryNode.IsDocument()) {
    1.86 +    return GetFrameForNode(&aGeometryNode.GetAsDocument(), GEOMETRY_NODE_DOCUMENT);
    1.87 +  }
    1.88 +  return GetFrameForNode(&aGeometryNode.GetAsText(), GEOMETRY_NODE_TEXT);
    1.89 +}
    1.90 +
    1.91 +static nsIFrame*
    1.92 +GetFrameForNode(nsINode* aNode)
    1.93 +{
    1.94 +  if (aNode->IsElement()) {
    1.95 +    return GetFrameForNode(aNode, GEOMETRY_NODE_ELEMENT);
    1.96 +  }
    1.97 +  if (aNode == aNode->OwnerDoc()) {
    1.98 +    return GetFrameForNode(aNode, GEOMETRY_NODE_DOCUMENT);
    1.99 +  }
   1.100 +  NS_ASSERTION(aNode->IsNodeOfType(nsINode::eTEXT), "Unknown node type");
   1.101 +  return GetFrameForNode(aNode, GEOMETRY_NODE_TEXT);
   1.102 +}
   1.103 +
   1.104 +static nsIFrame*
   1.105 +GetFirstNonAnonymousFrameForGeometryNode(const Optional<OwningGeometryNode>& aNode,
   1.106 +                                         nsINode* aDefaultNode)
   1.107 +{
   1.108 +  nsIFrame* f = GetFrameForGeometryNode(aNode, aDefaultNode);
   1.109 +  if (f) {
   1.110 +    f = nsLayoutUtils::GetFirstNonAnonymousFrame(f);
   1.111 +  }
   1.112 +  return f;
   1.113 +}
   1.114 +
   1.115 +static nsIFrame*
   1.116 +GetFirstNonAnonymousFrameForGeometryNode(const GeometryNode& aNode)
   1.117 +{
   1.118 +  nsIFrame* f = GetFrameForGeometryNode(aNode);
   1.119 +  if (f) {
   1.120 +    f = nsLayoutUtils::GetFirstNonAnonymousFrame(f);
   1.121 +  }
   1.122 +  return f;
   1.123 +}
   1.124 +
   1.125 +static nsIFrame*
   1.126 +GetFirstNonAnonymousFrameForNode(nsINode* aNode)
   1.127 +{
   1.128 +  nsIFrame* f = GetFrameForNode(aNode);
   1.129 +  if (f) {
   1.130 +    f = nsLayoutUtils::GetFirstNonAnonymousFrame(f);
   1.131 +  }
   1.132 +  return f;
   1.133 +}
   1.134 +
   1.135 +/**
   1.136 + * This can modify aFrame to point to a different frame. This is needed to
   1.137 + * handle SVG, where SVG elements can only compute a rect that's valid with
   1.138 + * respect to the "outer SVG" frame.
   1.139 + */
   1.140 +static nsRect
   1.141 +GetBoxRectForFrame(nsIFrame** aFrame, CSSBoxType aType)
   1.142 +{
   1.143 +  nsRect r;
   1.144 +  nsIFrame* f = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(*aFrame, &r);
   1.145 +  if (f) {
   1.146 +    // For SVG, the BoxType is ignored.
   1.147 +    *aFrame = f;
   1.148 +    return r;
   1.149 +  }
   1.150 +
   1.151 +  f = *aFrame;
   1.152 +  switch (aType) {
   1.153 +  case CSSBoxType::Content: r = f->GetContentRectRelativeToSelf(); break;
   1.154 +  case CSSBoxType::Padding: r = f->GetPaddingRectRelativeToSelf(); break;
   1.155 +  case CSSBoxType::Border: r = nsRect(nsPoint(0, 0), f->GetSize()); break;
   1.156 +  case CSSBoxType::Margin: {
   1.157 +    r = nsRect(nsPoint(0, 0), f->GetSize());
   1.158 +    r.Inflate(f->GetUsedMargin());
   1.159 +    break;
   1.160 +  }
   1.161 +  default: MOZ_ASSERT(false, "unknown box type"); return r;
   1.162 +  }
   1.163 +
   1.164 +  return r;
   1.165 +}
   1.166 +
   1.167 +class AccumulateQuadCallback : public nsLayoutUtils::BoxCallback {
   1.168 +public:
   1.169 +  AccumulateQuadCallback(nsISupports* aParentObject,
   1.170 +                         nsTArray<nsRefPtr<DOMQuad> >& aResult,
   1.171 +                         nsIFrame* aRelativeToFrame,
   1.172 +                         const nsPoint& aRelativeToBoxTopLeft,
   1.173 +                         CSSBoxType aBoxType)
   1.174 +    : mParentObject(aParentObject)
   1.175 +    , mResult(aResult)
   1.176 +    , mRelativeToFrame(aRelativeToFrame)
   1.177 +    , mRelativeToBoxTopLeft(aRelativeToBoxTopLeft)
   1.178 +    , mBoxType(aBoxType)
   1.179 +  {
   1.180 +  }
   1.181 +
   1.182 +  virtual void AddBox(nsIFrame* aFrame) MOZ_OVERRIDE
   1.183 +  {
   1.184 +    nsIFrame* f = aFrame;
   1.185 +    nsRect box = GetBoxRectForFrame(&f, mBoxType);
   1.186 +    nsPoint appUnits[4] =
   1.187 +      { box.TopLeft(), box.TopRight(), box.BottomRight(), box.BottomLeft() };
   1.188 +    CSSPoint points[4];
   1.189 +    for (uint32_t i = 0; i < 4; ++i) {
   1.190 +      points[i] = CSSPoint(nsPresContext::AppUnitsToFloatCSSPixels(appUnits[i].x),
   1.191 +                           nsPresContext::AppUnitsToFloatCSSPixels(appUnits[i].y));
   1.192 +    }
   1.193 +    nsLayoutUtils::TransformResult rv =
   1.194 +      nsLayoutUtils::TransformPoints(f, mRelativeToFrame, 4, points);
   1.195 +    if (rv == nsLayoutUtils::TRANSFORM_SUCCEEDED) {
   1.196 +      CSSPoint delta(nsPresContext::AppUnitsToFloatCSSPixels(mRelativeToBoxTopLeft.x),
   1.197 +                     nsPresContext::AppUnitsToFloatCSSPixels(mRelativeToBoxTopLeft.y));
   1.198 +      for (uint32_t i = 0; i < 4; ++i) {
   1.199 +        points[i] -= delta;
   1.200 +      }
   1.201 +    } else {
   1.202 +      PodArrayZero(points);
   1.203 +    }
   1.204 +    mResult.AppendElement(new DOMQuad(mParentObject, points));
   1.205 +  }
   1.206 +
   1.207 +  nsISupports* mParentObject;
   1.208 +  nsTArray<nsRefPtr<DOMQuad> >& mResult;
   1.209 +  nsIFrame* mRelativeToFrame;
   1.210 +  nsPoint mRelativeToBoxTopLeft;
   1.211 +  CSSBoxType mBoxType;
   1.212 +};
   1.213 +
   1.214 +static nsPresContext*
   1.215 +FindTopLevelPresContext(nsPresContext* aPC)
   1.216 +{
   1.217 +  bool isChrome = aPC->IsChrome();
   1.218 +  nsPresContext* pc = aPC;
   1.219 +  for (;;) {
   1.220 +    nsPresContext* parent = pc->GetParentPresContext();
   1.221 +    if (!parent || parent->IsChrome() != isChrome) {
   1.222 +      return pc;
   1.223 +    }
   1.224 +    pc = parent;
   1.225 +  }
   1.226 +}
   1.227 +
   1.228 +static bool
   1.229 +CheckFramesInSameTopLevelBrowsingContext(nsIFrame* aFrame1, nsIFrame* aFrame2)
   1.230 +{
   1.231 +  nsPresContext* pc1 = aFrame1->PresContext();
   1.232 +  nsPresContext* pc2 = aFrame2->PresContext();
   1.233 +  if (pc1 == pc2) {
   1.234 +    return true;
   1.235 +  }
   1.236 +  if (nsContentUtils::IsCallerChrome()) {
   1.237 +    return true;
   1.238 +  }
   1.239 +  if (FindTopLevelPresContext(pc1) == FindTopLevelPresContext(pc2)) {
   1.240 +    return true;
   1.241 +  }
   1.242 +  return false;
   1.243 +}
   1.244 +
   1.245 +void GetBoxQuads(nsINode* aNode,
   1.246 +                 const dom::BoxQuadOptions& aOptions,
   1.247 +                 nsTArray<nsRefPtr<DOMQuad> >& aResult,
   1.248 +                 ErrorResult& aRv)
   1.249 +{
   1.250 +  nsIFrame* frame = GetFrameForNode(aNode);
   1.251 +  if (!frame) {
   1.252 +    // No boxes to return
   1.253 +    return;
   1.254 +  }
   1.255 +  nsIDocument* ownerDoc = aNode->OwnerDoc();
   1.256 +  nsIFrame* relativeToFrame =
   1.257 +    GetFirstNonAnonymousFrameForGeometryNode(aOptions.mRelativeTo, ownerDoc);
   1.258 +  if (!relativeToFrame) {
   1.259 +    aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   1.260 +    return;
   1.261 +  }
   1.262 +  if (!CheckFramesInSameTopLevelBrowsingContext(frame, relativeToFrame)) {
   1.263 +    aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   1.264 +    return;
   1.265 +  }
   1.266 +  // GetBoxRectForFrame can modify relativeToFrame so call it first.
   1.267 +  nsPoint relativeToTopLeft =
   1.268 +      GetBoxRectForFrame(&relativeToFrame, CSSBoxType::Border).TopLeft();
   1.269 +  AccumulateQuadCallback callback(ownerDoc, aResult, relativeToFrame,
   1.270 +                                  relativeToTopLeft, aOptions.mBox);
   1.271 +  nsLayoutUtils::GetAllInFlowBoxes(frame, &callback);
   1.272 +}
   1.273 +
   1.274 +static void
   1.275 +TransformPoints(nsINode* aTo, const GeometryNode& aFrom,
   1.276 +                uint32_t aPointCount, CSSPoint* aPoints,
   1.277 +                const ConvertCoordinateOptions& aOptions, ErrorResult& aRv)
   1.278 +{
   1.279 +  nsIFrame* fromFrame = GetFirstNonAnonymousFrameForGeometryNode(aFrom);
   1.280 +  nsIFrame* toFrame = GetFirstNonAnonymousFrameForNode(aTo);
   1.281 +  if (!fromFrame || !toFrame) {
   1.282 +    aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   1.283 +    return;
   1.284 +  }
   1.285 +  if (!CheckFramesInSameTopLevelBrowsingContext(fromFrame, toFrame)) {
   1.286 +    aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   1.287 +    return;
   1.288 +  }
   1.289 +
   1.290 +  nsPoint fromOffset = GetBoxRectForFrame(&fromFrame, aOptions.mFromBox).TopLeft();
   1.291 +  nsPoint toOffset = GetBoxRectForFrame(&toFrame, aOptions.mToBox).TopLeft();
   1.292 +  CSSPoint fromOffsetGfx(nsPresContext::AppUnitsToFloatCSSPixels(fromOffset.x),
   1.293 +                         nsPresContext::AppUnitsToFloatCSSPixels(fromOffset.y));
   1.294 +  for (uint32_t i = 0; i < aPointCount; ++i) {
   1.295 +    aPoints[i] += fromOffsetGfx;
   1.296 +  }
   1.297 +  nsLayoutUtils::TransformResult rv =
   1.298 +    nsLayoutUtils::TransformPoints(fromFrame, toFrame, aPointCount, aPoints);
   1.299 +  if (rv == nsLayoutUtils::TRANSFORM_SUCCEEDED) {
   1.300 +    CSSPoint toOffsetGfx(nsPresContext::AppUnitsToFloatCSSPixels(toOffset.x),
   1.301 +                         nsPresContext::AppUnitsToFloatCSSPixels(toOffset.y));
   1.302 +    for (uint32_t i = 0; i < aPointCount; ++i) {
   1.303 +      aPoints[i] -= toOffsetGfx;
   1.304 +    }
   1.305 +  } else {
   1.306 +    PodZero(aPoints, aPointCount);
   1.307 +  }
   1.308 +}
   1.309 +
   1.310 +already_AddRefed<DOMQuad>
   1.311 +ConvertQuadFromNode(nsINode* aTo, dom::DOMQuad& aQuad,
   1.312 +                    const GeometryNode& aFrom,
   1.313 +                    const dom::ConvertCoordinateOptions& aOptions,
   1.314 +                    ErrorResult& aRv)
   1.315 +{
   1.316 +  CSSPoint points[4];
   1.317 +  for (uint32_t i = 0; i < 4; ++i) {
   1.318 +    DOMPoint* p = aQuad.Point(i);
   1.319 +    if (p->W() != 1.0 || p->Z() != 0.0) {
   1.320 +      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1.321 +      return nullptr;
   1.322 +    }
   1.323 +    points[i] = CSSPoint(p->X(), p->Y());
   1.324 +  }
   1.325 +  TransformPoints(aTo, aFrom, 4, points, aOptions, aRv);
   1.326 +  if (aRv.Failed()) {
   1.327 +    return nullptr;
   1.328 +  }
   1.329 +  nsRefPtr<DOMQuad> result = new DOMQuad(aTo->GetParentObject().mObject, points);
   1.330 +  return result.forget();
   1.331 +}
   1.332 +
   1.333 +already_AddRefed<DOMQuad>
   1.334 +ConvertRectFromNode(nsINode* aTo, dom::DOMRectReadOnly& aRect,
   1.335 +                    const GeometryNode& aFrom,
   1.336 +                    const dom::ConvertCoordinateOptions& aOptions,
   1.337 +                    ErrorResult& aRv)
   1.338 +{
   1.339 +  CSSPoint points[4];
   1.340 +  double x = aRect.X(), y = aRect.Y(), w = aRect.Width(), h = aRect.Height();
   1.341 +  points[0] = CSSPoint(x, y);
   1.342 +  points[1] = CSSPoint(x + w, y);
   1.343 +  points[2] = CSSPoint(x + w, y + h);
   1.344 +  points[3] = CSSPoint(x, y + h);
   1.345 +  TransformPoints(aTo, aFrom, 4, points, aOptions, aRv);
   1.346 +  if (aRv.Failed()) {
   1.347 +    return nullptr;
   1.348 +  }
   1.349 +  nsRefPtr<DOMQuad> result = new DOMQuad(aTo->GetParentObject().mObject, points);
   1.350 +  return result.forget();
   1.351 +}
   1.352 +
   1.353 +already_AddRefed<DOMPoint>
   1.354 +ConvertPointFromNode(nsINode* aTo, const dom::DOMPointInit& aPoint,
   1.355 +                     const GeometryNode& aFrom,
   1.356 +                     const dom::ConvertCoordinateOptions& aOptions,
   1.357 +                     ErrorResult& aRv)
   1.358 +{
   1.359 +  if (aPoint.mW != 1.0 || aPoint.mZ != 0.0) {
   1.360 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1.361 +    return nullptr;
   1.362 +  }
   1.363 +  CSSPoint point(aPoint.mX, aPoint.mY);
   1.364 +  TransformPoints(aTo, aFrom, 1, &point, aOptions, aRv);
   1.365 +  if (aRv.Failed()) {
   1.366 +    return nullptr;
   1.367 +  }
   1.368 +  nsRefPtr<DOMPoint> result = new DOMPoint(aTo->GetParentObject().mObject, point.x, point.y);
   1.369 +  return result.forget();
   1.370 +}
   1.371 +
   1.372 +}

mercurial