layout/base/GeometryUtils.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:5c12ca3e1749
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/. */
5
6 #include "GeometryUtils.h"
7
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"
20
21 using namespace mozilla;
22 using namespace mozilla::dom;
23
24 namespace mozilla {
25
26 enum GeometryNodeType {
27 GEOMETRY_NODE_ELEMENT,
28 GEOMETRY_NODE_TEXT,
29 GEOMETRY_NODE_DOCUMENT
30 };
31
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 }
57
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 }
65
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 }
75
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 }
87
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 }
100
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 }
111
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 }
121
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 }
131
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 }
147
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 }
160
161 return r;
162 }
163
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 }
178
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 }
203
204 nsISupports* mParentObject;
205 nsTArray<nsRefPtr<DOMQuad> >& mResult;
206 nsIFrame* mRelativeToFrame;
207 nsPoint mRelativeToBoxTopLeft;
208 CSSBoxType mBoxType;
209 };
210
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 }
224
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 }
241
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 }
270
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 }
286
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 }
306
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 }
329
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 }
349
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 }
368
369 }

mercurial