Coverage Report

Created: 2018-09-25 14:53

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