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

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

mercurial