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 +}