Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/base/nsLayoutUtils.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 "nsLayoutUtils.h"
8
9
#include "mozilla/ArrayUtils.h"
10
#include "mozilla/BasicEvents.h"
11
#include "mozilla/ClearOnShutdown.h"
12
#include "mozilla/EffectCompositor.h"
13
#include "mozilla/EffectSet.h"
14
#include "mozilla/EventDispatcher.h"
15
#include "mozilla/FloatingPoint.h"
16
#include "mozilla/gfx/gfxVars.h"
17
#include "mozilla/gfx/PathHelpers.h"
18
#include "mozilla/layers/PAPZ.h"
19
#include "mozilla/Likely.h"
20
#include "mozilla/Maybe.h"
21
#include "mozilla/MemoryReporting.h"
22
#include "mozilla/dom/ContentChild.h"
23
#include "mozilla/ServoStyleSetInlines.h"
24
#include "mozilla/StaticPrefs.h"
25
#include "mozilla/Unused.h"
26
#include "nsCharTraits.h"
27
#include "nsDocument.h"
28
#include "nsFontMetrics.h"
29
#include "nsPresContext.h"
30
#include "nsIContent.h"
31
#include "nsFrameList.h"
32
#include "nsGenericHTMLElement.h"
33
#include "nsGkAtoms.h"
34
#include "nsAtom.h"
35
#include "nsCaret.h"
36
#include "nsCSSPseudoElements.h"
37
#include "nsCSSAnonBoxes.h"
38
#include "nsCSSColorUtils.h"
39
#include "nsView.h"
40
#include "nsViewManager.h"
41
#include "nsPlaceholderFrame.h"
42
#include "nsIScrollableFrame.h"
43
#include "nsSubDocumentFrame.h"
44
#include "nsDisplayList.h"
45
#include "nsRegion.h"
46
#include "nsCSSFrameConstructor.h"
47
#include "nsBlockFrame.h"
48
#include "nsBidiPresUtils.h"
49
#include "imgIContainer.h"
50
#include "ImageOps.h"
51
#include "ImageRegion.h"
52
#include "gfxRect.h"
53
#include "gfxContext.h"
54
#include "gfxContext.h"
55
#include "nsIInterfaceRequestorUtils.h"
56
#include "nsCSSRendering.h"
57
#include "nsTextFragment.h"
58
#include "nsStyleConsts.h"
59
#include "nsPIDOMWindow.h"
60
#include "nsIDocShell.h"
61
#include "nsIWidget.h"
62
#include "gfxMatrix.h"
63
#include "gfxPrefs.h"
64
#include "gfxTypes.h"
65
#include "nsTArray.h"
66
#include "mozilla/dom/HTMLCanvasElement.h"
67
#include "nsICanvasRenderingContextInternal.h"
68
#include "gfxPlatform.h"
69
#include <algorithm>
70
#include <limits>
71
#include "mozilla/dom/AnonymousContent.h"
72
#include "mozilla/dom/HTMLBodyElement.h"
73
#include "mozilla/dom/HTMLMediaElementBinding.h"
74
#include "mozilla/dom/HTMLVideoElement.h"
75
#include "mozilla/dom/HTMLImageElement.h"
76
#include "mozilla/dom/DOMRect.h"
77
#include "mozilla/dom/DOMStringList.h"
78
#include "mozilla/dom/KeyframeEffect.h"
79
#include "mozilla/dom/SVGPathData.h"
80
#include "mozilla/layers/APZCCallbackHelper.h"
81
#include "imgIRequest.h"
82
#include "nsIImageLoadingContent.h"
83
#include "nsCOMPtr.h"
84
#include "nsCSSProps.h"
85
#include "nsListControlFrame.h"
86
#include "mozilla/dom/Element.h"
87
#include "nsCanvasFrame.h"
88
#include "gfxDrawable.h"
89
#include "gfxEnv.h"
90
#include "gfxUtils.h"
91
#include "nsDataHashtable.h"
92
#include "nsTableWrapperFrame.h"
93
#include "nsTextFrame.h"
94
#include "nsFontInflationData.h"
95
#include "nsSVGIntegrationUtils.h"
96
#include "nsSVGUtils.h"
97
#include "SVGImageContext.h"
98
#include "SVGTextFrame.h"
99
#include "nsStyleStructInlines.h"
100
#include "nsStyleTransformMatrix.h"
101
#include "nsIFrameInlines.h"
102
#include "ImageContainer.h"
103
#include "nsComputedDOMStyle.h"
104
#include "ActiveLayerTracker.h"
105
#include "mozilla/gfx/2D.h"
106
#include "gfx2DGlue.h"
107
#include "mozilla/LookAndFeel.h"
108
#include "UnitTransforms.h"
109
#include "TiledLayerBuffer.h" // For TILEDLAYERBUFFER_TILE_SIZE
110
#include "ClientLayerManager.h"
111
#include "nsRefreshDriver.h"
112
#include "nsIContentViewer.h"
113
#include "LayersLogging.h"
114
#include "mozilla/Preferences.h"
115
#include "nsFrameSelection.h"
116
#include "FrameLayerBuilder.h"
117
#include "mozilla/layers/APZUtils.h"    // for apz::CalculatePendingDisplayPort
118
#include "mozilla/layers/CompositorBridgeChild.h"
119
#include "mozilla/Telemetry.h"
120
#include "mozilla/EventDispatcher.h"
121
#include "mozilla/StyleAnimationValue.h"
122
#include "mozilla/ServoStyleSet.h"
123
#include "mozilla/WheelHandlingHelper.h" // for WheelHandlingUtils
124
#include "RegionBuilder.h"
125
#include "SVGViewportElement.h"
126
#include "DisplayItemClip.h"
127
#include "mozilla/layers/StackingContextHelper.h"
128
#include "mozilla/layers/WebRenderLayerManager.h"
129
#include "prenv.h"
130
#include "RetainedDisplayListBuilder.h"
131
#include "DisplayListChecker.h"
132
#include "TextDrawTarget.h"
133
#include "nsDeckFrame.h"
134
#include "mozilla/dom/InspectorFontFace.h"
135
136
#ifdef MOZ_XUL
137
#include "nsXULPopupManager.h"
138
#endif
139
140
#include "GeckoProfiler.h"
141
#include "nsAnimationManager.h"
142
#include "nsTransitionManager.h"
143
#include "mozilla/RestyleManager.h"
144
#include "LayoutLogging.h"
145
146
// Make sure getpid() works.
147
#ifdef XP_WIN
148
#include <process.h>
149
#define getpid _getpid
150
#else
151
#include <unistd.h>
152
#endif
153
154
using namespace mozilla;
155
using namespace mozilla::dom;
156
using namespace mozilla::image;
157
using namespace mozilla::layers;
158
using namespace mozilla::layout;
159
using namespace mozilla::gfx;
160
using mozilla::dom::HTMLMediaElement_Binding::HAVE_NOTHING;
161
using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;
162
163
0
#define INTERCHARACTER_RUBY_ENABLED_PREF_NAME "layout.css.ruby.intercharacter.enabled"
164
0
#define CONTENT_SELECT_ENABLED_PREF_NAME "dom.select_popup_in_content.enabled"
165
166
// The time in number of frames that we estimate for a refresh driver
167
// to be quiescent
168
3
#define DEFAULT_QUIESCENT_FRAMES 2
169
// The time (milliseconds) we estimate is needed between the end of an
170
// idle time and the next Tick.
171
3
#define DEFAULT_IDLE_PERIOD_TIME_LIMIT 1.0f
172
173
#ifdef DEBUG
174
// TODO: remove, see bug 598468.
175
bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
176
#endif // DEBUG
177
178
typedef FrameMetrics::ViewID ViewID;
179
typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
180
181
/* static */ uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine;
182
/* static */ uint32_t nsLayoutUtils::sFontSizeInflationMinTwips;
183
/* static */ uint32_t nsLayoutUtils::sFontSizeInflationLineThreshold;
184
/* static */ int32_t  nsLayoutUtils::sFontSizeInflationMappingIntercept;
185
/* static */ uint32_t nsLayoutUtils::sFontSizeInflationMaxRatio;
186
/* static */ bool nsLayoutUtils::sFontSizeInflationForceEnabled;
187
/* static */ bool nsLayoutUtils::sFontSizeInflationDisabledInMasterProcess;
188
/* static */ uint32_t nsLayoutUtils::sSystemFontScale;
189
/* static */ uint32_t nsLayoutUtils::sZoomMaxPercent;
190
/* static */ uint32_t nsLayoutUtils::sZoomMinPercent;
191
/* static */ bool nsLayoutUtils::sInvalidationDebuggingIsEnabled;
192
/* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled;
193
/* static */ bool nsLayoutUtils::sSVGTransformBoxEnabled;
194
/* static */ uint32_t nsLayoutUtils::sIdlePeriodDeadlineLimit;
195
/* static */ uint32_t nsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;
196
197
static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID;
198
199
typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
200
static ContentMap* sContentMap = nullptr;
201
0
static ContentMap& GetContentMap() {
202
0
  if (!sContentMap) {
203
0
    sContentMap = new ContentMap();
204
0
  }
205
0
  return *sContentMap;
206
0
}
207
208
template<typename TestType>
209
static bool
210
HasMatchingAnimations(EffectSet* aEffects, TestType&& aTest)
211
0
{
212
0
  for (KeyframeEffect* effect : *aEffects) {
213
0
    if (aTest(*effect)) {
214
0
      return true;
215
0
    }
216
0
  }
217
0
218
0
  return false;
219
0
}
Unexecuted instantiation: Unified_cpp_layout_base1.cpp:bool HasMatchingAnimations<nsLayoutUtils::HasCurrentTransitions(nsIFrame const*)::$_12&>(mozilla::EffectSet*, nsLayoutUtils::HasCurrentTransitions(nsIFrame const*)::$_12&)
Unexecuted instantiation: Unified_cpp_layout_base1.cpp:bool HasMatchingAnimations<nsLayoutUtils::HasAnimationOfProperty(mozilla::EffectSet*, nsCSSPropertyID)::$_13>(mozilla::EffectSet*, nsLayoutUtils::HasAnimationOfProperty(mozilla::EffectSet*, nsCSSPropertyID)::$_13&&)
Unexecuted instantiation: Unified_cpp_layout_base1.cpp:bool HasMatchingAnimations<nsLayoutUtils::HasAnimationOfProperty(nsIFrame const*, nsCSSPropertyID)::$_14&>(mozilla::EffectSet*, nsLayoutUtils::HasAnimationOfProperty(nsIFrame const*, nsCSSPropertyID)::$_14&)
Unexecuted instantiation: Unified_cpp_layout_base1.cpp:bool HasMatchingAnimations<nsLayoutUtils::HasEffectiveAnimation(nsIFrame const*, nsCSSPropertyID)::$_15>(mozilla::EffectSet*, nsLayoutUtils::HasEffectiveAnimation(nsIFrame const*, nsCSSPropertyID)::$_15&&)
220
221
template<typename TestType>
222
static bool
223
HasMatchingAnimations(const nsIFrame* aFrame, TestType&& aTest)
224
0
{
225
0
  EffectSet* effects = EffectSet::GetEffectSet(aFrame);
226
0
  if (!effects) {
227
0
    return false;
228
0
  }
229
0
230
0
  return HasMatchingAnimations(effects, aTest);
231
0
}
Unexecuted instantiation: Unified_cpp_layout_base1.cpp:bool HasMatchingAnimations<nsLayoutUtils::HasCurrentTransitions(nsIFrame const*)::$_12>(nsIFrame const*, nsLayoutUtils::HasCurrentTransitions(nsIFrame const*)::$_12&&)
Unexecuted instantiation: Unified_cpp_layout_base1.cpp:bool HasMatchingAnimations<nsLayoutUtils::HasAnimationOfProperty(nsIFrame const*, nsCSSPropertyID)::$_14>(nsIFrame const*, nsLayoutUtils::HasAnimationOfProperty(nsIFrame const*, nsCSSPropertyID)::$_14&&)
232
233
bool
234
nsLayoutUtils::HasCurrentTransitions(const nsIFrame* aFrame)
235
0
{
236
0
  return HasMatchingAnimations(aFrame,
237
0
    [](KeyframeEffect& aEffect)
238
0
    {
239
0
      // Since |aEffect| is current, it must have an associated Animation
240
0
      // so we don't need to null-check the result of GetAnimation().
241
0
      return aEffect.IsCurrent() && aEffect.GetAnimation()->AsCSSTransition();
242
0
    }
243
0
  );
244
0
}
245
246
static bool
247
MayHaveAnimationOfProperty(EffectSet* effects, nsCSSPropertyID aProperty)
248
0
{
249
0
  MOZ_ASSERT(effects);
250
0
251
0
  if (aProperty == eCSSProperty_transform &&
252
0
      !effects->MayHaveTransformAnimation()) {
253
0
    return false;
254
0
  }
255
0
  if (aProperty == eCSSProperty_opacity &&
256
0
      !effects->MayHaveOpacityAnimation()) {
257
0
    return false;
258
0
  }
259
0
260
0
  return true;
261
0
}
262
263
static bool
264
MayHaveAnimationOfProperty(const nsIFrame* aFrame, nsCSSPropertyID aProperty)
265
0
{
266
0
  switch (aProperty) {
267
0
    case eCSSProperty_transform:
268
0
      return aFrame->MayHaveTransformAnimation();
269
0
    case eCSSProperty_opacity:
270
0
      return aFrame->MayHaveOpacityAnimation();
271
0
    default:
272
0
      MOZ_ASSERT_UNREACHABLE("unexpected property");
273
0
      return false;
274
0
  }
275
0
}
276
277
bool
278
nsLayoutUtils::HasAnimationOfProperty(EffectSet* aEffectSet,
279
                                      nsCSSPropertyID aProperty)
280
0
{
281
0
  if (!aEffectSet || !MayHaveAnimationOfProperty(aEffectSet, aProperty)) {
282
0
    return false;
283
0
  }
284
0
285
0
  return HasMatchingAnimations(aEffectSet,
286
0
    [&aProperty](KeyframeEffect& aEffect)
287
0
    {
288
0
      return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
289
0
             aEffect.HasAnimationOfProperty(aProperty);
290
0
    }
291
0
  );
292
0
}
293
294
bool
295
nsLayoutUtils::HasAnimationOfProperty(const nsIFrame* aFrame,
296
                                      nsCSSPropertyID aProperty)
297
0
{
298
0
  if (!MayHaveAnimationOfProperty(aFrame, aProperty)) {
299
0
    return false;
300
0
  }
301
0
302
0
  return HasMatchingAnimations(aFrame,
303
0
    [&aProperty](KeyframeEffect& aEffect)
304
0
    {
305
0
      return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
306
0
             aEffect.HasAnimationOfProperty(aProperty);
307
0
    }
308
0
  );
309
0
310
0
}
311
312
bool
313
nsLayoutUtils::HasEffectiveAnimation(const nsIFrame* aFrame,
314
                                     nsCSSPropertyID aProperty)
315
0
{
316
0
  EffectSet* effects = EffectSet::GetEffectSet(aFrame);
317
0
  if (!effects || !MayHaveAnimationOfProperty(effects, aProperty)) {
318
0
    return false;
319
0
  }
320
0
321
0
322
0
  return HasMatchingAnimations(effects,
323
0
    [&aProperty](KeyframeEffect& aEffect)
324
0
    {
325
0
      return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
326
0
             aEffect.HasEffectiveAnimationOfProperty(aProperty);
327
0
    }
328
0
  );
329
0
}
330
331
static float
332
GetSuitableScale(float aMaxScale, float aMinScale,
333
                 nscoord aVisibleDimension, nscoord aDisplayDimension)
334
0
{
335
0
  float displayVisibleRatio = float(aDisplayDimension) /
336
0
                              float(aVisibleDimension);
337
0
  // We want to rasterize based on the largest scale used during the
338
0
  // transform animation, unless that would make us rasterize something
339
0
  // larger than the screen.  But we never want to go smaller than the
340
0
  // minimum scale over the animation.
341
0
  if (FuzzyEqualsMultiplicative(displayVisibleRatio, aMaxScale, .01f)) {
342
0
    // Using aMaxScale may make us rasterize something a fraction larger than
343
0
    // the screen. However, if aMaxScale happens to be the final scale of a
344
0
    // transform animation it is better to use aMaxScale so that for the
345
0
    // fraction of a second before we delayerize the composited texture it has
346
0
    // a better chance of being pixel aligned and composited without resampling
347
0
    // (avoiding visually clunky delayerization).
348
0
    return aMaxScale;
349
0
  }
350
0
  return std::max(std::min(aMaxScale, displayVisibleRatio), aMinScale);
351
0
}
352
353
static inline void
354
UpdateMinMaxScale(const nsIFrame* aFrame,
355
                  const AnimationValue& aValue,
356
                  Size& aMinScale,
357
                  Size& aMaxScale)
358
0
{
359
0
  Size size = aValue.GetScaleValue(aFrame);
360
0
  aMaxScale.width = std::max<float>(aMaxScale.width, size.width);
361
0
  aMaxScale.height = std::max<float>(aMaxScale.height, size.height);
362
0
  aMinScale.width = std::min<float>(aMinScale.width, size.width);
363
0
  aMinScale.height = std::min<float>(aMinScale.height, size.height);
364
0
}
365
366
static void
367
GetMinAndMaxScaleForAnimationProperty(const nsIFrame* aFrame,
368
                                      nsTArray<RefPtr<dom::Animation>>& aAnimations,
369
                                      Size& aMaxScale,
370
                                      Size& aMinScale)
371
0
{
372
0
  for (dom::Animation* anim : aAnimations) {
373
0
    // This method is only expected to be passed animations that are running on
374
0
    // the compositor and we only pass playing animations to the compositor,
375
0
    // which are, by definition, "relevant" animations (animations that are
376
0
    // not yet finished or which are filling forwards).
377
0
    MOZ_ASSERT(anim->IsRelevant());
378
0
379
0
    dom::KeyframeEffect* effect =
380
0
      anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr;
381
0
    MOZ_ASSERT(effect, "A playing animation should have a keyframe effect");
382
0
    for (size_t propIdx = effect->Properties().Length(); propIdx-- != 0; ) {
383
0
      const AnimationProperty& prop = effect->Properties()[propIdx];
384
0
      if (prop.mProperty != eCSSProperty_transform) {
385
0
        continue;
386
0
      }
387
0
388
0
      // We need to factor in the scale of the base style if the base style
389
0
      // will be used on the compositor.
390
0
      AnimationValue baseStyle = effect->BaseStyle(prop.mProperty);
391
0
      if (!baseStyle.IsNull()) {
392
0
        UpdateMinMaxScale(aFrame, baseStyle, aMinScale, aMaxScale);
393
0
      }
394
0
395
0
      for (const AnimationPropertySegment& segment : prop.mSegments) {
396
0
        // In case of add or accumulate composite, StyleAnimationValue does
397
0
        // not have a valid value.
398
0
        if (segment.HasReplaceableFromValue()) {
399
0
          UpdateMinMaxScale(aFrame, segment.mFromValue, aMinScale, aMaxScale);
400
0
        }
401
0
        if (segment.HasReplaceableToValue()) {
402
0
          UpdateMinMaxScale(aFrame, segment.mToValue, aMinScale, aMaxScale);
403
0
        }
404
0
      }
405
0
    }
406
0
  }
407
0
}
408
409
Size
410
nsLayoutUtils::ComputeSuitableScaleForAnimation(const nsIFrame* aFrame,
411
                                                const nsSize& aVisibleSize,
412
                                                const nsSize& aDisplaySize)
413
0
{
414
0
  Size maxScale(std::numeric_limits<float>::min(),
415
0
                std::numeric_limits<float>::min());
416
0
  Size minScale(std::numeric_limits<float>::max(),
417
0
                std::numeric_limits<float>::max());
418
0
419
0
  nsTArray<RefPtr<dom::Animation>> compositorAnimations =
420
0
    EffectCompositor::GetAnimationsForCompositor(aFrame,
421
0
                                                 eCSSProperty_transform);
422
0
  GetMinAndMaxScaleForAnimationProperty(aFrame, compositorAnimations,
423
0
                                        maxScale, minScale);
424
0
425
0
  if (maxScale.width == std::numeric_limits<float>::min()) {
426
0
    // We didn't encounter a transform
427
0
    return Size(1.0, 1.0);
428
0
  }
429
0
430
0
  return Size(GetSuitableScale(maxScale.width, minScale.width,
431
0
                               aVisibleSize.width, aDisplaySize.width),
432
0
              GetSuitableScale(maxScale.height, minScale.height,
433
0
                               aVisibleSize.height, aDisplaySize.height));
434
0
}
435
436
bool
437
nsLayoutUtils::AreAsyncAnimationsEnabled()
438
0
{
439
0
  static bool sAreAsyncAnimationsEnabled;
440
0
  static bool sAsyncPrefCached = false;
441
0
442
0
  if (!sAsyncPrefCached) {
443
0
    sAsyncPrefCached = true;
444
0
    Preferences::AddBoolVarCache(&sAreAsyncAnimationsEnabled,
445
0
                                 "layers.offmainthreadcomposition.async-animations");
446
0
  }
447
0
448
0
  return sAreAsyncAnimationsEnabled &&
449
0
    gfxPlatform::OffMainThreadCompositingEnabled();
450
0
}
451
452
bool
453
nsLayoutUtils::IsAnimationLoggingEnabled()
454
0
{
455
0
  static bool sShouldLog;
456
0
  static bool sShouldLogPrefCached;
457
0
458
0
  if (!sShouldLogPrefCached) {
459
0
    sShouldLogPrefCached = true;
460
0
    Preferences::AddBoolVarCache(&sShouldLog,
461
0
                                 "layers.offmainthreadcomposition.log-animations");
462
0
  }
463
0
464
0
  return sShouldLog;
465
0
}
466
467
bool
468
nsLayoutUtils::AreRetainedDisplayListsEnabled()
469
0
{
470
0
  if (XRE_IsContentProcess()) {
471
0
    return gfxPrefs::LayoutRetainDisplayList();
472
0
  }
473
0
474
0
  if (XRE_IsE10sParentProcess()) {
475
0
    return gfxPrefs::LayoutRetainDisplayListChrome();
476
0
  }
477
0
478
0
  // Retained display lists require e10s.
479
0
  return false;
480
0
}
481
482
bool
483
nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame)
484
0
{
485
0
  const nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
486
0
  MOZ_ASSERT(displayRoot);
487
0
  return displayRoot->HasProperty(RetainedDisplayListBuilder::Cached());
488
0
}
489
490
bool
491
nsLayoutUtils::GPUImageScalingEnabled()
492
0
{
493
0
  static bool sGPUImageScalingEnabled;
494
0
  static bool sGPUImageScalingPrefInitialised = false;
495
0
496
0
  if (!sGPUImageScalingPrefInitialised) {
497
0
    sGPUImageScalingPrefInitialised = true;
498
0
    sGPUImageScalingEnabled =
499
0
      Preferences::GetBool("layout.gpu-image-scaling.enabled", false);
500
0
  }
501
0
502
0
  return sGPUImageScalingEnabled;
503
0
}
504
505
bool
506
nsLayoutUtils::AnimatedImageLayersEnabled()
507
0
{
508
0
  static bool sAnimatedImageLayersEnabled;
509
0
  static bool sAnimatedImageLayersPrefCached = false;
510
0
511
0
  if (!sAnimatedImageLayersPrefCached) {
512
0
    sAnimatedImageLayersPrefCached = true;
513
0
    Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled,
514
0
                                 "layout.animated-image-layers.enabled",
515
0
                                 false);
516
0
  }
517
0
518
0
  return sAnimatedImageLayersEnabled;
519
0
}
520
521
bool
522
nsLayoutUtils::IsInterCharacterRubyEnabled()
523
0
{
524
0
  static bool sInterCharacterRubyEnabled;
525
0
  static bool sInterCharacterRubyEnabledPrefCached = false;
526
0
527
0
  if (!sInterCharacterRubyEnabledPrefCached) {
528
0
    sInterCharacterRubyEnabledPrefCached = true;
529
0
    Preferences::AddBoolVarCache(&sInterCharacterRubyEnabled,
530
0
                                 INTERCHARACTER_RUBY_ENABLED_PREF_NAME,
531
0
                                 false);
532
0
  }
533
0
534
0
  return sInterCharacterRubyEnabled;
535
0
}
536
537
bool
538
nsLayoutUtils::IsContentSelectEnabled()
539
0
{
540
0
  static bool sContentSelectEnabled;
541
0
  static bool sContentSelectEnabledPrefCached = false;
542
0
543
0
  if (!sContentSelectEnabledPrefCached) {
544
0
    sContentSelectEnabledPrefCached = true;
545
0
    Preferences::AddBoolVarCache(&sContentSelectEnabled,
546
0
                                 CONTENT_SELECT_ENABLED_PREF_NAME,
547
0
                                 false);
548
0
  }
549
0
550
0
  return sContentSelectEnabled;
551
0
}
552
553
void
554
nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
555
                                  nsOverflowAreas& aOverflowAreas,
556
                                  FrameChildListIDs aSkipChildLists)
557
0
{
558
0
  // Iterate over all children except pop-ups.
559
0
  FrameChildListIDs skip = aSkipChildLists |
560
0
      nsIFrame::kSelectPopupList | nsIFrame::kPopupList;
561
0
  for (nsIFrame::ChildListIterator childLists(aFrame);
562
0
       !childLists.IsDone(); childLists.Next()) {
563
0
    if (skip.Contains(childLists.CurrentID())) {
564
0
      continue;
565
0
    }
566
0
567
0
    nsFrameList children = childLists.CurrentList();
568
0
    for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
569
0
      nsIFrame* child = e.get();
570
0
      nsOverflowAreas childOverflow =
571
0
        child->GetOverflowAreas() + child->GetPosition();
572
0
      aOverflowAreas.UnionWith(childOverflow);
573
0
    }
574
0
  }
575
0
}
576
577
static void DestroyViewID(void* aObject, nsAtom* aPropertyName,
578
                          void* aPropertyValue, void* aData)
579
0
{
580
0
  ViewID* id = static_cast<ViewID*>(aPropertyValue);
581
0
  GetContentMap().Remove(*id);
582
0
  delete id;
583
0
}
584
585
/**
586
 * A namespace class for static layout utilities.
587
 */
588
589
bool
590
nsLayoutUtils::FindIDFor(const nsIContent* aContent, ViewID* aOutViewId)
591
0
{
592
0
  void* scrollIdProperty = aContent->GetProperty(nsGkAtoms::RemoteId);
593
0
  if (scrollIdProperty) {
594
0
    *aOutViewId = *static_cast<ViewID*>(scrollIdProperty);
595
0
    return true;
596
0
  }
597
0
  return false;
598
0
}
599
600
ViewID
601
nsLayoutUtils::FindOrCreateIDFor(nsIContent* aContent)
602
0
{
603
0
  ViewID scrollId;
604
0
605
0
  if (!FindIDFor(aContent, &scrollId)) {
606
0
    scrollId = sScrollIdCounter++;
607
0
    aContent->SetProperty(nsGkAtoms::RemoteId, new ViewID(scrollId),
608
0
                          DestroyViewID);
609
0
    GetContentMap().Put(scrollId, aContent);
610
0
  }
611
0
612
0
  return scrollId;
613
0
}
614
615
nsIContent*
616
nsLayoutUtils::FindContentFor(ViewID aId)
617
0
{
618
0
  MOZ_ASSERT(aId != FrameMetrics::NULL_SCROLL_ID,
619
0
             "Cannot find a content element in map for null IDs.");
620
0
  nsIContent* content;
621
0
  bool exists = GetContentMap().Get(aId, &content);
622
0
623
0
  if (exists) {
624
0
    return content;
625
0
  } else {
626
0
    return nullptr;
627
0
  }
628
0
}
629
630
static nsIFrame*
631
GetScrollFrameFromContent(nsIContent* aContent)
632
0
{
633
0
  nsIFrame* frame = aContent->GetPrimaryFrame();
634
0
  if (aContent->OwnerDoc()->GetRootElement() == aContent) {
635
0
    nsIPresShell* presShell = frame ? frame->PresShell() : nullptr;
636
0
    if (!presShell) {
637
0
      presShell = aContent->OwnerDoc()->GetShell();
638
0
    }
639
0
    // We want the scroll frame, the root scroll frame differs from all
640
0
    // others in that the primary frame is not the scroll frame.
641
0
    nsIFrame* rootScrollFrame = presShell ? presShell->GetRootScrollFrame() : nullptr;
642
0
    if (rootScrollFrame) {
643
0
      frame = rootScrollFrame;
644
0
    }
645
0
  }
646
0
  return frame;
647
0
}
648
649
nsIScrollableFrame*
650
nsLayoutUtils::FindScrollableFrameFor(ViewID aId)
651
0
{
652
0
  nsIContent* content = FindContentFor(aId);
653
0
  if (!content) {
654
0
    return nullptr;
655
0
  }
656
0
657
0
  nsIFrame* scrollFrame = GetScrollFrameFromContent(content);
658
0
  return scrollFrame ? scrollFrame->GetScrollTargetFrame() : nullptr;
659
0
}
660
661
ViewID
662
nsLayoutUtils::FindIDForScrollableFrame(nsIScrollableFrame* aScrollable)
663
0
{
664
0
  if (!aScrollable) {
665
0
    return FrameMetrics::NULL_SCROLL_ID;
666
0
  }
667
0
668
0
  nsIFrame* scrollFrame = do_QueryFrame(aScrollable);
669
0
  nsIContent* scrollContent = scrollFrame->GetContent();
670
0
671
0
  FrameMetrics::ViewID scrollId;
672
0
  if (scrollContent &&
673
0
      nsLayoutUtils::FindIDFor(scrollContent, &scrollId)) {
674
0
    return scrollId;
675
0
  }
676
0
677
0
  return FrameMetrics::NULL_SCROLL_ID;
678
0
}
679
680
static nsRect
681
ApplyRectMultiplier(nsRect aRect, float aMultiplier)
682
0
{
683
0
  if (aMultiplier == 1.0f) {
684
0
    return aRect;
685
0
  }
686
0
  float newWidth = aRect.width * aMultiplier;
687
0
  float newHeight = aRect.height * aMultiplier;
688
0
  float newX = aRect.x - ((newWidth - aRect.width) / 2.0f);
689
0
  float newY = aRect.y - ((newHeight - aRect.height) / 2.0f);
690
0
  // Rounding doesn't matter too much here, do a round-in
691
0
  return nsRect(ceil(newX), ceil(newY), floor(newWidth), floor(newHeight));
692
0
}
693
694
bool
695
nsLayoutUtils::UsesAsyncScrolling(nsIFrame* aFrame)
696
0
{
697
#ifdef MOZ_WIDGET_ANDROID
698
  // We always have async scrolling for android
699
  return true;
700
#endif
701
702
0
  return AsyncPanZoomEnabled(aFrame);
703
0
}
704
705
bool
706
nsLayoutUtils::AsyncPanZoomEnabled(nsIFrame* aFrame)
707
0
{
708
0
  // We use this as a shortcut, since if the compositor will never use APZ,
709
0
  // no widget will either.
710
0
  if (!gfxPlatform::AsyncPanZoomEnabled()) {
711
0
    return false;
712
0
  }
713
0
714
0
  nsIFrame *frame = nsLayoutUtils::GetDisplayRootFrame(aFrame);
715
0
  nsIWidget* widget = frame->GetNearestWidget();
716
0
  if (!widget) {
717
0
    return false;
718
0
  }
719
0
  return widget->AsyncPanZoomEnabled();
720
0
}
721
722
float
723
0
nsLayoutUtils::GetCurrentAPZResolutionScale(nsIPresShell* aShell) {
724
0
  return aShell ? aShell->GetCumulativeNonRootScaleResolution() : 1.0;
725
0
}
726
727
// Return the maximum displayport size, based on the LayerManager's maximum
728
// supported texture size. The result is in app units.
729
static nscoord
730
GetMaxDisplayPortSize(nsIContent* aContent, nsPresContext* aFallbackPrescontext)
731
0
{
732
0
  MOZ_ASSERT(!gfxPrefs::LayersTilesEnabled(), "Do not clamp displayports if tiling is enabled");
733
0
734
0
  // Pick a safe maximum displayport size for sanity purposes. This is the
735
0
  // lowest maximum texture size on tileless-platforms (Windows, D3D10).
736
0
  // If the gfx.max-texture-size pref is set, further restrict the displayport
737
0
  // size to fit within that, because the compositor won't upload stuff larger
738
0
  // than this size.
739
0
  nscoord safeMaximum = aFallbackPrescontext
740
0
      ? aFallbackPrescontext->DevPixelsToAppUnits(
741
0
            std::min(8192, gfxPlatform::MaxTextureSize()))
742
0
      : nscoord_MAX;
743
0
744
0
  nsIFrame* frame = aContent->GetPrimaryFrame();
745
0
  if (!frame) {
746
0
    return safeMaximum;
747
0
  }
748
0
  frame = nsLayoutUtils::GetDisplayRootFrame(frame);
749
0
750
0
  nsIWidget* widget = frame->GetNearestWidget();
751
0
  if (!widget) {
752
0
    return safeMaximum;
753
0
  }
754
0
  LayerManager* lm = widget->GetLayerManager();
755
0
  if (!lm) {
756
0
    return safeMaximum;
757
0
  }
758
0
  nsPresContext* presContext = frame->PresContext();
759
0
760
0
  int32_t maxSizeInDevPixels = lm->GetMaxTextureSize();
761
0
  if (maxSizeInDevPixels < 0 || maxSizeInDevPixels == INT_MAX) {
762
0
    return safeMaximum;
763
0
  }
764
0
  maxSizeInDevPixels = std::min(maxSizeInDevPixels, gfxPlatform::MaxTextureSize());
765
0
  return presContext->DevPixelsToAppUnits(maxSizeInDevPixels);
766
0
}
767
768
static nsRect
769
GetDisplayPortFromRectData(nsIContent* aContent,
770
                           DisplayPortPropertyData* aRectData,
771
                           float aMultiplier)
772
0
{
773
0
  // In the case where the displayport is set as a rect, we assume it is
774
0
  // already aligned and clamped as necessary. The burden to do that is
775
0
  // on the setter of the displayport. In practice very few places set the
776
0
  // displayport directly as a rect (mostly tests). We still do need to
777
0
  // expand it by the multiplier though.
778
0
  return ApplyRectMultiplier(aRectData->mRect, aMultiplier);
779
0
}
780
781
static nsRect
782
GetDisplayPortFromMarginsData(nsIContent* aContent,
783
                              DisplayPortMarginsPropertyData* aMarginsData,
784
                              float aMultiplier)
785
0
{
786
0
  // In the case where the displayport is set via margins, we apply the margins
787
0
  // to a base rect. Then we align the expanded rect based on the alignment
788
0
  // requested, further expand the rect by the multiplier, and finally, clamp it
789
0
  // to the size of the scrollable rect.
790
0
791
0
  nsRect base;
792
0
  if (nsRect* baseData = static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
793
0
    base = *baseData;
794
0
  } else {
795
0
    // In theory we shouldn't get here, but we do sometimes (see bug 1212136).
796
0
    // Fall through for graceful handling.
797
0
  }
798
0
799
0
  nsIFrame* frame = GetScrollFrameFromContent(aContent);
800
0
  if (!frame) {
801
0
    // Turns out we can't really compute it. Oops. We still should return
802
0
    // something sane. Note that since we can't clamp the rect without a
803
0
    // frame, we don't apply the multiplier either as it can cause the result
804
0
    // to leak outside the scrollable area.
805
0
    NS_WARNING("Attempting to get a displayport from a content with no primary frame!");
806
0
    return base;
807
0
  }
808
0
809
0
  bool isRoot = false;
810
0
  if (aContent->OwnerDoc()->GetRootElement() == aContent) {
811
0
    isRoot = true;
812
0
  }
813
0
814
0
  nsPoint scrollPos;
815
0
  if (nsIScrollableFrame* scrollableFrame = frame->GetScrollTargetFrame()) {
816
0
    scrollPos = scrollableFrame->GetScrollPosition();
817
0
  }
818
0
819
0
  nsPresContext* presContext = frame->PresContext();
820
0
  int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
821
0
822
0
  LayoutDeviceToScreenScale2D res(presContext->PresShell()->GetCumulativeResolution()
823
0
                                * nsLayoutUtils::GetTransformToAncestorScale(frame));
824
0
825
0
  // Calculate the expanded scrollable rect, which we'll be clamping the
826
0
  // displayport to.
827
0
  nsRect expandedScrollableRect =
828
0
    nsLayoutUtils::CalculateExpandedScrollableRect(frame);
829
0
830
0
  // GetTransformToAncestorScale() can return 0. In this case, just return the
831
0
  // base rect (clamped to the expanded scrollable rect), as other calculations
832
0
  // would run into divisions by zero.
833
0
  if (res == LayoutDeviceToScreenScale2D(0, 0)) {
834
0
    // Make sure the displayport remains within the scrollable rect.
835
0
    return base.MoveInsideAndClamp(expandedScrollableRect - scrollPos);
836
0
  }
837
0
838
0
  // First convert the base rect to screen pixels
839
0
  LayoutDeviceToScreenScale2D parentRes = res;
840
0
  if (isRoot) {
841
0
    // the base rect for root scroll frames is specified in the parent document
842
0
    // coordinate space, so it doesn't include the local resolution.
843
0
    float localRes = presContext->PresShell()->GetResolution();
844
0
    parentRes.xScale /= localRes;
845
0
    parentRes.yScale /= localRes;
846
0
  }
847
0
  ScreenRect screenRect = LayoutDeviceRect::FromAppUnits(base, auPerDevPixel)
848
0
                        * parentRes;
849
0
850
0
  // Note on the correctness of applying the alignment in Screen space:
851
0
  //   The correct space to apply the alignment in would be Layer space, but
852
0
  //   we don't necessarily know the scale to convert to Layer space at this
853
0
  //   point because Layout may not yet have chosen the resolution at which to
854
0
  //   render (it chooses that in FrameLayerBuilder, but this can be called
855
0
  //   during display list building). Therefore, we perform the alignment in
856
0
  //   Screen space, which basically assumes that Layout chose to render at
857
0
  //   screen resolution; since this is what Layout does most of the time,
858
0
  //   this is a good approximation. A proper solution would involve moving
859
0
  //   the choosing of the resolution to display-list building time.
860
0
  ScreenSize alignment;
861
0
862
0
  nsIPresShell* presShell = presContext->PresShell();
863
0
  MOZ_ASSERT(presShell);
864
0
865
0
  if (presShell->IsDisplayportSuppressed()) {
866
0
    alignment = ScreenSize(1, 1);
867
0
  } else if (gfxPrefs::LayersTilesEnabled()) {
868
0
    // Don't align to tiles if they are too large, because we could expand
869
0
    // the displayport by a lot which can take more paint time. It's a tradeoff
870
0
    // though because if we don't align to tiles we have more waste on upload.
871
0
    IntSize tileSize = gfxVars::TileSize();
872
0
    alignment = ScreenSize(std::min(256, tileSize.width), std::min(256, tileSize.height));
873
0
  } else {
874
0
    // If we're not drawing with tiles then we need to be careful about not
875
0
    // hitting the max texture size and we only need 1 draw call per layer
876
0
    // so we can align to a smaller multiple.
877
0
    alignment = ScreenSize(128, 128);
878
0
  }
879
0
880
0
  // Avoid division by zero.
881
0
  if (alignment.width == 0) {
882
0
    alignment.width = 128;
883
0
  }
884
0
  if (alignment.height == 0) {
885
0
    alignment.height = 128;
886
0
  }
887
0
888
0
  if (gfxPrefs::LayersTilesEnabled()) {
889
0
    // Expand the rect by the margins
890
0
    screenRect.Inflate(aMarginsData->mMargins);
891
0
  } else {
892
0
    // Calculate the displayport to make sure we fit within the max texture size
893
0
    // when not tiling.
894
0
    nscoord maxSizeAppUnits = GetMaxDisplayPortSize(aContent, presContext);
895
0
    MOZ_ASSERT(maxSizeAppUnits < nscoord_MAX);
896
0
897
0
    // The alignment code can round up to 3 tiles, we want to make sure
898
0
    // that the displayport can grow by up to 3 tiles without going
899
0
    // over the max texture size.
900
0
    const int MAX_ALIGN_ROUNDING = 3;
901
0
902
0
    // Find the maximum size in screen pixels.
903
0
    int32_t maxSizeDevPx = presContext->AppUnitsToDevPixels(maxSizeAppUnits);
904
0
    int32_t maxWidthScreenPx = floor(double(maxSizeDevPx) * res.xScale) -
905
0
      MAX_ALIGN_ROUNDING * alignment.width;
906
0
    int32_t maxHeightScreenPx = floor(double(maxSizeDevPx) * res.yScale) -
907
0
      MAX_ALIGN_ROUNDING * alignment.height;
908
0
909
0
    // For each axis, inflate the margins up to the maximum size.
910
0
    const ScreenMargin& margins = aMarginsData->mMargins;
911
0
    if (screenRect.height < maxHeightScreenPx) {
912
0
      int32_t budget = maxHeightScreenPx - screenRect.height;
913
0
      // Scale the margins down to fit into the budget if necessary, maintaining
914
0
      // their relative ratio.
915
0
      float scale = 1.0f;
916
0
      if (float(budget) < margins.TopBottom()) {
917
0
        scale = float(budget) / margins.TopBottom();
918
0
      }
919
0
      float top = margins.top * scale;
920
0
      float bottom = margins.bottom * scale;
921
0
      screenRect.y -= top;
922
0
      screenRect.height += top + bottom;
923
0
    }
924
0
    if (screenRect.width < maxWidthScreenPx) {
925
0
      int32_t budget = maxWidthScreenPx - screenRect.width;
926
0
      float scale = 1.0f;
927
0
      if (float(budget) < margins.LeftRight()) {
928
0
        scale = float(budget) / margins.LeftRight();
929
0
      }
930
0
      float left = margins.left * scale;
931
0
      float right = margins.right * scale;
932
0
      screenRect.x -= left;
933
0
      screenRect.width += left + right;
934
0
    }
935
0
  }
936
0
937
0
  ScreenPoint scrollPosScreen = LayoutDevicePoint::FromAppUnits(scrollPos, auPerDevPixel)
938
0
                              * res;
939
0
940
0
  // Round-out the display port to the nearest alignment (tiles)
941
0
  screenRect += scrollPosScreen;
942
0
  float x = alignment.width * floor(screenRect.x / alignment.width);
943
0
  float y = alignment.height * floor(screenRect.y / alignment.height);
944
0
  float w = alignment.width * ceil(screenRect.width / alignment.width + 1);
945
0
  float h = alignment.height * ceil(screenRect.height / alignment.height + 1);
946
0
  screenRect = ScreenRect(x, y, w, h);
947
0
  screenRect -= scrollPosScreen;
948
0
949
0
  // Convert the aligned rect back into app units.
950
0
  nsRect result = LayoutDeviceRect::ToAppUnits(screenRect / res, auPerDevPixel);
951
0
952
0
  // If we have non-zero margins, expand the displayport for the low-res buffer
953
0
  // if that's what we're drawing. If we have zero margins, we want the
954
0
  // displayport to reflect the scrollport.
955
0
  if (aMarginsData->mMargins != ScreenMargin()) {
956
0
    result = ApplyRectMultiplier(result, aMultiplier);
957
0
  }
958
0
959
0
  // Make sure the displayport remains within the scrollable rect.
960
0
  result = result.MoveInsideAndClamp(expandedScrollableRect - scrollPos);
961
0
962
0
  return result;
963
0
}
964
965
static bool
966
HasVisibleAnonymousContents(nsIDocument* aDoc)
967
0
{
968
0
  for (RefPtr<AnonymousContent>& ac : aDoc->GetAnonymousContents()) {
969
0
    // We check to see if the anonymous content node has a frame. If it doesn't,
970
0
    // that means that's not visible to the user because e.g. it's display:none.
971
0
    // For now we assume that if it has a frame, it is visible. We might be able
972
0
    // to refine this further by adding complexity if it turns out this condition
973
0
    // results in a lot of false positives.
974
0
    if (ac->ContentNode().GetPrimaryFrame()) {
975
0
      return true;
976
0
    }
977
0
  }
978
0
  return false;
979
0
}
980
981
bool
982
nsLayoutUtils::ShouldDisableApzForElement(nsIContent* aContent)
983
0
{
984
0
  if (!aContent) {
985
0
    return false;
986
0
  }
987
0
988
0
  nsIDocument* doc = aContent->GetComposedDoc();
989
0
  nsIPresShell* rootShell = APZCCallbackHelper::GetRootContentDocumentPresShellForContent(aContent);
990
0
  if (rootShell) {
991
0
    if (nsIDocument* rootDoc = rootShell->GetDocument()) {
992
0
      nsIContent* rootContent = rootShell->GetRootScrollFrame()
993
0
          ? rootShell->GetRootScrollFrame()->GetContent()
994
0
          : rootDoc->GetDocumentElement();
995
0
      // For the AccessibleCaret: disable APZ on any scrollable subframes that
996
0
      // are not the root scrollframe of a document, if the document has any
997
0
      // visible anonymous contents.
998
0
      // If we find this is triggering in too many scenarios then we might
999
0
      // want to tighten this check further. The main use cases for which we want
1000
0
      // to disable APZ as of this writing are listed in bug 1316318.
1001
0
      if (aContent != rootContent && HasVisibleAnonymousContents(rootDoc)) {
1002
0
        return true;
1003
0
      }
1004
0
    }
1005
0
  }
1006
0
1007
0
  if (!doc) {
1008
0
    return false;
1009
0
  }
1010
0
  return gfxPrefs::APZDisableForScrollLinkedEffects() &&
1011
0
         doc->HasScrollLinkedEffect();
1012
0
}
1013
1014
static bool
1015
GetDisplayPortData(nsIContent* aContent,
1016
                   DisplayPortPropertyData** aOutRectData,
1017
                   DisplayPortMarginsPropertyData** aOutMarginsData)
1018
0
{
1019
0
  MOZ_ASSERT(aOutRectData && aOutMarginsData);
1020
0
1021
0
  *aOutRectData =
1022
0
    static_cast<DisplayPortPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPort));
1023
0
  *aOutMarginsData =
1024
0
    static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
1025
0
1026
0
  if (!*aOutRectData && !*aOutMarginsData) {
1027
0
    // This content element has no displayport data at all
1028
0
    return false;
1029
0
  }
1030
0
1031
0
  if (*aOutRectData && *aOutMarginsData) {
1032
0
    // choose margins if equal priority
1033
0
    if ((*aOutRectData)->mPriority > (*aOutMarginsData)->mPriority) {
1034
0
      *aOutMarginsData = nullptr;
1035
0
    } else {
1036
0
      *aOutRectData = nullptr;
1037
0
    }
1038
0
  }
1039
0
1040
0
  NS_ASSERTION((*aOutRectData == nullptr) != (*aOutMarginsData == nullptr),
1041
0
               "Only one of aOutRectData or aOutMarginsData should be set!");
1042
0
1043
0
  return true;
1044
0
}
1045
1046
bool
1047
nsLayoutUtils::IsMissingDisplayPortBaseRect(nsIContent* aContent)
1048
0
{
1049
0
  DisplayPortPropertyData* rectData = nullptr;
1050
0
  DisplayPortMarginsPropertyData* marginsData = nullptr;
1051
0
1052
0
  if (GetDisplayPortData(aContent, &rectData, &marginsData) && marginsData) {
1053
0
    return !aContent->GetProperty(nsGkAtoms::DisplayPortBase);
1054
0
  }
1055
0
1056
0
  return false;
1057
0
}
1058
1059
enum class MaxSizeExceededBehaviour {
1060
  // Ask GetDisplayPortImpl to assert if the calculated displayport exceeds
1061
  // the maximum allowed size.
1062
  eAssert,
1063
  // Ask GetDisplayPortImpl to pretend like there's no displayport at all, if
1064
  // the calculated displayport exceeds the maximum allowed size.
1065
  eDrop,
1066
};
1067
1068
static bool
1069
GetDisplayPortImpl(nsIContent* aContent, nsRect* aResult, float aMultiplier,
1070
                   MaxSizeExceededBehaviour aBehaviour = MaxSizeExceededBehaviour::eAssert)
1071
0
{
1072
0
  DisplayPortPropertyData* rectData = nullptr;
1073
0
  DisplayPortMarginsPropertyData* marginsData = nullptr;
1074
0
1075
0
  if (!GetDisplayPortData(aContent, &rectData, &marginsData)) {
1076
0
    return false;
1077
0
  }
1078
0
1079
0
  if (!aResult) {
1080
0
    // We have displayport data, but the caller doesn't want the actual
1081
0
    // rect, so we don't need to actually compute it.
1082
0
    return true;
1083
0
  }
1084
0
1085
0
  bool isDisplayportSuppressed = false;
1086
0
1087
0
  nsIFrame* frame = aContent->GetPrimaryFrame();
1088
0
  if (frame) {
1089
0
    nsPresContext* presContext = frame->PresContext();
1090
0
    MOZ_ASSERT(presContext);
1091
0
    nsIPresShell* presShell = presContext->PresShell();
1092
0
    MOZ_ASSERT(presShell);
1093
0
    isDisplayportSuppressed = presShell->IsDisplayportSuppressed();
1094
0
  }
1095
0
1096
0
  nsRect result;
1097
0
  if (rectData) {
1098
0
    result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier);
1099
0
  } else if (isDisplayportSuppressed ||
1100
0
      nsLayoutUtils::ShouldDisableApzForElement(aContent)) {
1101
0
    DisplayPortMarginsPropertyData noMargins(ScreenMargin(), 1);
1102
0
    result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier);
1103
0
  } else {
1104
0
    result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier);
1105
0
  }
1106
0
1107
0
  if (!gfxPrefs::LayersTilesEnabled()) {
1108
0
    // Perform the desired error handling if the displayport dimensions
1109
0
    // exceeds the maximum allowed size
1110
0
    nscoord maxSize = GetMaxDisplayPortSize(aContent, nullptr);
1111
0
    if (result.width > maxSize || result.height > maxSize) {
1112
0
      switch (aBehaviour) {
1113
0
      case MaxSizeExceededBehaviour::eAssert:
1114
0
        NS_ASSERTION(false, "Displayport must be a valid texture size");
1115
0
        break;
1116
0
      case MaxSizeExceededBehaviour::eDrop:
1117
0
        return false;
1118
0
      }
1119
0
    }
1120
0
  }
1121
0
1122
0
  *aResult = result;
1123
0
  return true;
1124
0
}
1125
1126
static void
1127
TranslateFromScrollPortToScrollFrame(nsIContent* aContent, nsRect* aRect)
1128
0
{
1129
0
  MOZ_ASSERT(aRect);
1130
0
  nsIFrame* frame = GetScrollFrameFromContent(aContent);
1131
0
  nsIScrollableFrame* scrollableFrame = frame ? frame->GetScrollTargetFrame() : nullptr;
1132
0
  if (scrollableFrame) {
1133
0
    *aRect += scrollableFrame->GetScrollPortRect().TopLeft();
1134
0
  }
1135
0
}
1136
1137
bool
1138
nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult,
1139
  RelativeTo aRelativeTo /* = RelativeTo::ScrollPort */)
1140
0
{
1141
0
  float multiplier =
1142
0
    gfxPrefs::UseLowPrecisionBuffer() ? 1.0f / gfxPrefs::LowPrecisionResolution() : 1.0f;
1143
0
  bool usingDisplayPort = GetDisplayPortImpl(aContent, aResult, multiplier);
1144
0
  if (aResult && usingDisplayPort && aRelativeTo == RelativeTo::ScrollFrame) {
1145
0
    TranslateFromScrollPortToScrollFrame(aContent, aResult);
1146
0
  }
1147
0
  return usingDisplayPort;
1148
0
}
1149
1150
bool
1151
0
nsLayoutUtils::HasDisplayPort(nsIContent* aContent) {
1152
0
  return GetDisplayPort(aContent, nullptr);
1153
0
}
1154
1155
/* static */ bool
1156
nsLayoutUtils::GetDisplayPortForVisibilityTesting(
1157
  nsIContent* aContent,
1158
  nsRect* aResult,
1159
  RelativeTo aRelativeTo /* = RelativeTo::ScrollPort */)
1160
0
{
1161
0
  MOZ_ASSERT(aResult);
1162
0
  // Since the base rect might not have been updated very recently, it's
1163
0
  // possible to end up with an extra-large displayport at this point, if the
1164
0
  // zoom level is changed by a lot. Instead of using the default behaviour of
1165
0
  // asserting, we can just ignore the displayport if that happens, as this
1166
0
  // call site is best-effort.
1167
0
  bool usingDisplayPort = GetDisplayPortImpl(aContent, aResult, 1.0f,
1168
0
      MaxSizeExceededBehaviour::eDrop);
1169
0
  if (usingDisplayPort && aRelativeTo == RelativeTo::ScrollFrame) {
1170
0
    TranslateFromScrollPortToScrollFrame(aContent, aResult);
1171
0
  }
1172
0
  return usingDisplayPort;
1173
0
}
1174
1175
void
1176
nsLayoutUtils::InvalidateForDisplayPortChange(nsIContent* aContent,
1177
                                              bool aHadDisplayPort,
1178
                                              const nsRect& aOldDisplayPort,
1179
                                              const nsRect& aNewDisplayPort,
1180
                                              RepaintMode aRepaintMode)
1181
0
{
1182
0
  if (aRepaintMode != RepaintMode::Repaint) {
1183
0
    return;
1184
0
  }
1185
0
1186
0
  bool changed = !aHadDisplayPort ||
1187
0
        !aOldDisplayPort.IsEqualEdges(aNewDisplayPort);
1188
0
1189
0
  nsIFrame* frame = GetScrollFrameFromContent(aContent);
1190
0
  if (frame) {
1191
0
    frame = do_QueryFrame(frame->GetScrollTargetFrame());
1192
0
  }
1193
0
1194
0
  if (changed && frame) {
1195
0
    // It is important to call SchedulePaint on the same frame that we set the dirty
1196
0
    // rect properties on so we can find the frame later to remove the properties.
1197
0
    frame->SchedulePaint();
1198
0
1199
0
    if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() ||
1200
0
        !nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(frame)) {
1201
0
      return;
1202
0
    }
1203
0
1204
0
    nsRect* rect =
1205
0
      frame->GetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect());
1206
0
1207
0
    if (!rect) {
1208
0
      rect = new nsRect();
1209
0
      frame->SetProperty(nsDisplayListBuilder::DisplayListBuildingDisplayPortRect(), rect);
1210
0
      frame->SetHasOverrideDirtyRegion(true);
1211
0
1212
0
      nsIFrame* rootFrame = frame->PresShell()->GetRootFrame();
1213
0
      MOZ_ASSERT(rootFrame);
1214
0
1215
0
      RetainedDisplayListData* data =
1216
0
        GetOrSetRetainedDisplayListData(rootFrame);
1217
0
      data->Flags(frame) |= RetainedDisplayListData::FrameFlags::HasProps;
1218
0
    }
1219
0
1220
0
    if (aHadDisplayPort) {
1221
0
      // We only need to build a display list for any new areas added
1222
0
      nsRegion newRegion(aNewDisplayPort);
1223
0
      newRegion.SubOut(aOldDisplayPort);
1224
0
      rect->UnionRect(*rect, newRegion.GetBounds());
1225
0
    } else {
1226
0
      rect->UnionRect(*rect, aNewDisplayPort);
1227
0
    }
1228
0
  }
1229
0
}
1230
1231
bool
1232
nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
1233
                                     nsIPresShell* aPresShell,
1234
                                     const ScreenMargin& aMargins,
1235
                                     uint32_t aPriority,
1236
                                     RepaintMode aRepaintMode)
1237
0
{
1238
0
  MOZ_ASSERT(aContent);
1239
0
  MOZ_ASSERT(aContent->GetComposedDoc() == aPresShell->GetDocument());
1240
0
1241
0
  DisplayPortMarginsPropertyData* currentData =
1242
0
    static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
1243
0
  if (currentData && currentData->mPriority > aPriority) {
1244
0
    return false;
1245
0
  }
1246
0
1247
0
  nsRect oldDisplayPort;
1248
0
  bool hadDisplayPort = GetHighResolutionDisplayPort(aContent, &oldDisplayPort);
1249
0
1250
0
  aContent->SetProperty(nsGkAtoms::DisplayPortMargins,
1251
0
                        new DisplayPortMarginsPropertyData(
1252
0
                            aMargins, aPriority),
1253
0
                        nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
1254
0
1255
0
  nsRect newDisplayPort;
1256
0
  DebugOnly<bool> hasDisplayPort = GetHighResolutionDisplayPort(aContent, &newDisplayPort);
1257
0
  MOZ_ASSERT(hasDisplayPort);
1258
0
1259
0
  if (gfxPrefs::LayoutUseContainersForRootFrames()) {
1260
0
    nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame();
1261
0
    if (rootScrollFrame &&
1262
0
        aContent == rootScrollFrame->GetContent() &&
1263
0
        nsLayoutUtils::UsesAsyncScrolling(rootScrollFrame))
1264
0
    {
1265
0
      // We are setting a root displayport for a document.
1266
0
      // If we have APZ, then set a special flag on the pres shell so
1267
0
      // that we don't get scrollbars drawn.
1268
0
      aPresShell->SetIgnoreViewportScrolling(true);
1269
0
    }
1270
0
  }
1271
0
1272
0
  InvalidateForDisplayPortChange(aContent, hadDisplayPort, oldDisplayPort,
1273
0
    newDisplayPort, aRepaintMode);
1274
0
1275
0
  nsIFrame* frame = GetScrollFrameFromContent(aContent);
1276
0
  nsIScrollableFrame* scrollableFrame = frame ? frame->GetScrollTargetFrame() : nullptr;
1277
0
  if (!scrollableFrame) {
1278
0
    return true;
1279
0
  }
1280
0
1281
0
  scrollableFrame->TriggerDisplayPortExpiration();
1282
0
1283
0
  // Display port margins changing means that the set of visible frames may
1284
0
  // have drastically changed. Check if we should schedule an update.
1285
0
  hadDisplayPort =
1286
0
    scrollableFrame->GetDisplayPortAtLastApproximateFrameVisibilityUpdate(&oldDisplayPort);
1287
0
1288
0
  bool needVisibilityUpdate = !hadDisplayPort;
1289
0
  // Check if the total size has changed by a large factor.
1290
0
  if (!needVisibilityUpdate) {
1291
0
    if ((newDisplayPort.width > 2 * oldDisplayPort.width) ||
1292
0
        (oldDisplayPort.width > 2 * newDisplayPort.width) ||
1293
0
        (newDisplayPort.height > 2 * oldDisplayPort.height) ||
1294
0
        (oldDisplayPort.height > 2 * newDisplayPort.height)) {
1295
0
      needVisibilityUpdate = true;
1296
0
    }
1297
0
  }
1298
0
  // Check if it's moved by a significant amount.
1299
0
  if (!needVisibilityUpdate) {
1300
0
    if (nsRect* baseData = static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
1301
0
      nsRect base = *baseData;
1302
0
      if ((std::abs(newDisplayPort.X() - oldDisplayPort.X()) > base.width) ||
1303
0
          (std::abs(newDisplayPort.XMost() - oldDisplayPort.XMost()) > base.width) ||
1304
0
          (std::abs(newDisplayPort.Y() - oldDisplayPort.Y()) > base.height) ||
1305
0
          (std::abs(newDisplayPort.YMost() - oldDisplayPort.YMost()) > base.height)) {
1306
0
        needVisibilityUpdate = true;
1307
0
      }
1308
0
    }
1309
0
  }
1310
0
  if (needVisibilityUpdate) {
1311
0
    aPresShell->ScheduleApproximateFrameVisibilityUpdateNow();
1312
0
  }
1313
0
1314
0
  return true;
1315
0
}
1316
1317
void
1318
nsLayoutUtils::SetDisplayPortBase(nsIContent* aContent, const nsRect& aBase)
1319
0
{
1320
0
  aContent->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aBase),
1321
0
                        nsINode::DeleteProperty<nsRect>);
1322
0
}
1323
1324
void
1325
nsLayoutUtils::SetDisplayPortBaseIfNotSet(nsIContent* aContent, const nsRect& aBase)
1326
0
{
1327
0
  if (!aContent->GetProperty(nsGkAtoms::DisplayPortBase)) {
1328
0
    SetDisplayPortBase(aContent, aBase);
1329
0
  }
1330
0
}
1331
1332
bool
1333
nsLayoutUtils::GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult)
1334
0
{
1335
0
  if (gfxPrefs::UseLowPrecisionBuffer()) {
1336
0
    return GetDisplayPortImpl(aContent, aResult, 1.0f);
1337
0
  }
1338
0
  return false;
1339
0
}
1340
1341
bool
1342
nsLayoutUtils::HasCriticalDisplayPort(nsIContent* aContent)
1343
0
{
1344
0
  return GetCriticalDisplayPort(aContent, nullptr);
1345
0
}
1346
1347
bool
1348
nsLayoutUtils::GetHighResolutionDisplayPort(nsIContent* aContent, nsRect* aResult)
1349
0
{
1350
0
  if (gfxPrefs::UseLowPrecisionBuffer()) {
1351
0
    return GetCriticalDisplayPort(aContent, aResult);
1352
0
  }
1353
0
  return GetDisplayPort(aContent, aResult);
1354
0
}
1355
1356
void
1357
nsLayoutUtils::RemoveDisplayPort(nsIContent* aContent)
1358
0
{
1359
0
  aContent->DeleteProperty(nsGkAtoms::DisplayPort);
1360
0
  aContent->DeleteProperty(nsGkAtoms::DisplayPortMargins);
1361
0
}
1362
1363
nsContainerFrame*
1364
nsLayoutUtils::LastContinuationWithChild(nsContainerFrame* aFrame)
1365
0
{
1366
0
  MOZ_ASSERT(aFrame, "NULL frame pointer");
1367
0
  for (auto f = aFrame->LastContinuation(); f; f = f->GetPrevContinuation()) {
1368
0
    for (nsIFrame::ChildListIterator lists(f); !lists.IsDone(); lists.Next()) {
1369
0
      if (MOZ_LIKELY(!lists.CurrentList().IsEmpty())) {
1370
0
        return static_cast<nsContainerFrame*>(f);
1371
0
      }
1372
0
    }
1373
0
  }
1374
0
  return aFrame;
1375
0
}
1376
1377
//static
1378
FrameChildListID
1379
nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame)
1380
0
{
1381
0
  nsIFrame::ChildListID id = nsIFrame::kPrincipalList;
1382
0
1383
0
  MOZ_DIAGNOSTIC_ASSERT(!(aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
1384
0
1385
0
  if (aChildFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
1386
0
    nsIFrame* pif = aChildFrame->GetPrevInFlow();
1387
0
    if (pif->GetParent() == aChildFrame->GetParent()) {
1388
0
      id = nsIFrame::kExcessOverflowContainersList;
1389
0
    }
1390
0
    else {
1391
0
      id = nsIFrame::kOverflowContainersList;
1392
0
    }
1393
0
  } else {
1394
0
    LayoutFrameType childType = aChildFrame->Type();
1395
0
    if (LayoutFrameType::MenuPopup == childType) {
1396
0
      nsIFrame* parent = aChildFrame->GetParent();
1397
0
      MOZ_ASSERT(parent, "nsMenuPopupFrame can't be the root frame");
1398
0
      if (parent) {
1399
0
        if (parent->IsPopupSetFrame()) {
1400
0
          id = nsIFrame::kPopupList;
1401
0
        } else {
1402
0
          nsIFrame* firstPopup = parent->GetChildList(nsIFrame::kPopupList).FirstChild();
1403
0
          MOZ_ASSERT(!firstPopup || !firstPopup->GetNextSibling(),
1404
0
                     "We assume popupList only has one child, but it has more.");
1405
0
          id = firstPopup == aChildFrame
1406
0
                 ? nsIFrame::kPopupList
1407
0
                 : nsIFrame::kPrincipalList;
1408
0
        }
1409
0
      } else {
1410
0
        id = nsIFrame::kPrincipalList;
1411
0
      }
1412
0
    } else if (LayoutFrameType::TableColGroup == childType) {
1413
0
      id = nsIFrame::kColGroupList;
1414
0
    } else if (aChildFrame->IsTableCaption()) {
1415
0
      id = nsIFrame::kCaptionList;
1416
0
    } else {
1417
0
      id = nsIFrame::kPrincipalList;
1418
0
    }
1419
0
  }
1420
0
1421
#ifdef DEBUG
1422
  // Verify that the frame is actually in that child list or in the
1423
  // corresponding overflow list.
1424
  nsContainerFrame* parent = aChildFrame->GetParent();
1425
  bool found = parent->GetChildList(id).ContainsFrame(aChildFrame);
1426
  if (!found) {
1427
    found = parent->GetChildList(nsIFrame::kOverflowList)
1428
              .ContainsFrame(aChildFrame);
1429
    MOZ_ASSERT(found, "not in child list");
1430
  }
1431
#endif
1432
1433
0
  return id;
1434
0
}
1435
1436
static Element*
1437
GetPseudo(const nsIContent* aContent, nsAtom* aPseudoProperty)
1438
0
{
1439
0
  MOZ_ASSERT(aPseudoProperty == nsGkAtoms::beforePseudoProperty ||
1440
0
             aPseudoProperty == nsGkAtoms::afterPseudoProperty);
1441
0
  if (!aContent->MayHaveAnonymousChildren()) {
1442
0
    return nullptr;
1443
0
  }
1444
0
  return static_cast<Element*>(aContent->GetProperty(aPseudoProperty));
1445
0
}
1446
1447
/*static*/ Element*
1448
nsLayoutUtils::GetBeforePseudo(const nsIContent* aContent)
1449
0
{
1450
0
  return GetPseudo(aContent, nsGkAtoms::beforePseudoProperty);
1451
0
}
1452
1453
/*static*/ nsIFrame*
1454
nsLayoutUtils::GetBeforeFrame(const nsIContent* aContent)
1455
0
{
1456
0
  Element* pseudo = GetBeforePseudo(aContent);
1457
0
  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
1458
0
}
1459
1460
/*static*/ Element*
1461
nsLayoutUtils::GetAfterPseudo(const nsIContent* aContent)
1462
0
{
1463
0
  return GetPseudo(aContent, nsGkAtoms::afterPseudoProperty);
1464
0
}
1465
1466
/*static*/ nsIFrame*
1467
nsLayoutUtils::GetAfterFrame(const nsIContent* aContent)
1468
0
{
1469
0
  Element* pseudo = GetAfterPseudo(aContent);
1470
0
  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
1471
0
}
1472
1473
// static
1474
nsIFrame*
1475
nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame,
1476
                                     LayoutFrameType aFrameType,
1477
                                     nsIFrame* aStopAt)
1478
0
{
1479
0
  for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
1480
0
    if (frame->Type() == aFrameType) {
1481
0
      return frame;
1482
0
    }
1483
0
    if (frame == aStopAt) {
1484
0
      break;
1485
0
    }
1486
0
  }
1487
0
  return nullptr;
1488
0
}
1489
1490
/* static */ nsIFrame*
1491
nsLayoutUtils::GetPageFrame(nsIFrame* aFrame)
1492
0
{
1493
0
  return GetClosestFrameOfType(aFrame, LayoutFrameType::Page);
1494
0
}
1495
1496
// static
1497
nsIFrame*
1498
nsLayoutUtils::GetStyleFrame(nsIFrame* aFrame)
1499
0
{
1500
0
  if (aFrame->IsTableWrapperFrame()) {
1501
0
    nsIFrame* inner = aFrame->PrincipalChildList().FirstChild();
1502
0
    // inner may be null, if aFrame is mid-destruction
1503
0
    return inner;
1504
0
  }
1505
0
1506
0
  return aFrame;
1507
0
}
1508
1509
nsIFrame*
1510
nsLayoutUtils::GetStyleFrame(const nsIContent* aContent)
1511
0
{
1512
0
  nsIFrame *frame = aContent->GetPrimaryFrame();
1513
0
  if (!frame) {
1514
0
    return nullptr;
1515
0
  }
1516
0
1517
0
  return nsLayoutUtils::GetStyleFrame(frame);
1518
0
}
1519
1520
/* static */ nsIFrame*
1521
nsLayoutUtils::GetRealPrimaryFrameFor(const nsIContent* aContent)
1522
0
{
1523
0
  nsIFrame *frame = aContent->GetPrimaryFrame();
1524
0
  if (!frame) {
1525
0
    return nullptr;
1526
0
  }
1527
0
1528
0
  return nsPlaceholderFrame::GetRealFrameFor(frame);
1529
0
}
1530
1531
nsIFrame*
1532
0
nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
1533
0
  NS_ASSERTION(aFrame->IsPlaceholderFrame(), "Must have a placeholder here");
1534
0
  if (aFrame->GetStateBits() & PLACEHOLDER_FOR_FLOAT) {
1535
0
    nsIFrame *outOfFlowFrame =
1536
0
      nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
1537
0
    NS_ASSERTION(outOfFlowFrame && outOfFlowFrame->IsFloating(),
1538
0
                 "How did that happen?");
1539
0
    return outOfFlowFrame;
1540
0
  }
1541
0
1542
0
  return nullptr;
1543
0
}
1544
1545
// static
1546
nsIFrame*
1547
nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame,
1548
                                      nsPoint* aExtraOffset)
1549
0
{
1550
0
  nsIFrame* p = aFrame->GetParent();
1551
0
  if (p)
1552
0
    return p;
1553
0
1554
0
  nsView* v = aFrame->GetView();
1555
0
  if (!v)
1556
0
    return nullptr;
1557
0
  v = v->GetParent(); // anonymous inner view
1558
0
  if (!v)
1559
0
    return nullptr;
1560
0
  if (aExtraOffset) {
1561
0
    *aExtraOffset += v->GetPosition();
1562
0
  }
1563
0
  v = v->GetParent(); // subdocumentframe's view
1564
0
  return v ? v->GetFrame() : nullptr;
1565
0
}
1566
1567
// static
1568
bool
1569
nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
1570
                                             nsIFrame* aCommonAncestor)
1571
0
{
1572
0
  if (aFrame == aAncestorFrame)
1573
0
    return false;
1574
0
  return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
1575
0
}
1576
1577
// static
1578
bool
1579
nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
1580
                                       const nsIFrame* aCommonAncestor)
1581
0
{
1582
0
  for (const nsIFrame* f = aFrame; f != aCommonAncestor;
1583
0
       f = GetCrossDocParentFrame(f)) {
1584
0
    if (f == aAncestorFrame)
1585
0
      return true;
1586
0
  }
1587
0
  return aCommonAncestor == aAncestorFrame;
1588
0
}
1589
1590
// static
1591
bool
1592
nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
1593
                                     nsIFrame* aCommonAncestor)
1594
0
{
1595
0
  if (aFrame == aAncestorFrame)
1596
0
    return false;
1597
0
  for (nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) {
1598
0
    if (f == aAncestorFrame)
1599
0
      return true;
1600
0
  }
1601
0
  return aCommonAncestor == aAncestorFrame;
1602
0
}
1603
1604
// static
1605
int32_t
1606
nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1,
1607
                                     nsIContent* aContent2,
1608
                                     int32_t aIf1Ancestor,
1609
                                     int32_t aIf2Ancestor,
1610
                                     const nsIContent* aCommonAncestor)
1611
0
{
1612
0
  MOZ_ASSERT(aContent1, "aContent1 must not be null");
1613
0
  MOZ_ASSERT(aContent2, "aContent2 must not be null");
1614
0
1615
0
  AutoTArray<nsINode*, 32> content1Ancestors;
1616
0
  nsINode* c1;
1617
0
  for (c1 = aContent1;
1618
0
       c1 && c1 != aCommonAncestor;
1619
0
       c1 = c1->GetParentOrHostNode()) {
1620
0
    content1Ancestors.AppendElement(c1);
1621
0
  }
1622
0
  if (!c1 && aCommonAncestor) {
1623
0
    // So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
1624
0
    // Never mind. We can continue as if aCommonAncestor was null.
1625
0
    aCommonAncestor = nullptr;
1626
0
  }
1627
0
1628
0
  AutoTArray<nsINode*, 32> content2Ancestors;
1629
0
  nsINode* c2;
1630
0
  for (c2 = aContent2;
1631
0
       c2 && c2 != aCommonAncestor;
1632
0
       c2 = c2->GetParentOrHostNode()) {
1633
0
    content2Ancestors.AppendElement(c2);
1634
0
  }
1635
0
  if (!c2 && aCommonAncestor) {
1636
0
    // So, it turns out aCommonAncestor was not an ancestor of c2.
1637
0
    // We need to retry with no common ancestor hint.
1638
0
    return DoCompareTreePosition(aContent1, aContent2,
1639
0
                                 aIf1Ancestor, aIf2Ancestor, nullptr);
1640
0
  }
1641
0
1642
0
  int last1 = content1Ancestors.Length() - 1;
1643
0
  int last2 = content2Ancestors.Length() - 1;
1644
0
  nsINode* content1Ancestor = nullptr;
1645
0
  nsINode* content2Ancestor = nullptr;
1646
0
  while (last1 >= 0 && last2 >= 0
1647
0
         && ((content1Ancestor = content1Ancestors.ElementAt(last1)) ==
1648
0
             (content2Ancestor = content2Ancestors.ElementAt(last2)))) {
1649
0
    last1--;
1650
0
    last2--;
1651
0
  }
1652
0
1653
0
  if (last1 < 0) {
1654
0
    if (last2 < 0) {
1655
0
      NS_ASSERTION(aContent1 == aContent2, "internal error?");
1656
0
      return 0;
1657
0
    }
1658
0
    // aContent1 is an ancestor of aContent2
1659
0
    return aIf1Ancestor;
1660
0
  }
1661
0
1662
0
  if (last2 < 0) {
1663
0
    // aContent2 is an ancestor of aContent1
1664
0
    return aIf2Ancestor;
1665
0
  }
1666
0
1667
0
  // content1Ancestor != content2Ancestor, so they must be siblings with the same parent
1668
0
  nsINode* parent = content1Ancestor->GetParentOrHostNode();
1669
#ifdef DEBUG
1670
  // TODO: remove the uglyness, see bug 598468.
1671
  NS_ASSERTION(gPreventAssertInCompareTreePosition || parent,
1672
               "no common ancestor at all???");
1673
#endif // DEBUG
1674
0
  if (!parent) { // different documents??
1675
0
    return 0;
1676
0
  }
1677
0
1678
0
  int32_t index1 = parent->ComputeIndexOf(content1Ancestor);
1679
0
  int32_t index2 = parent->ComputeIndexOf(content2Ancestor);
1680
0
  if (index1 < 0 || index2 < 0) {
1681
0
    // one of them must be anonymous; we can't determine the order
1682
0
    return 0;
1683
0
  }
1684
0
1685
0
  return index1 - index2;
1686
0
}
1687
1688
// static
1689
nsIFrame*
1690
nsLayoutUtils::FillAncestors(nsIFrame* aFrame,
1691
                             nsIFrame* aStopAtAncestor,
1692
                             nsTArray<nsIFrame*>* aAncestors)
1693
0
{
1694
0
  while (aFrame && aFrame != aStopAtAncestor) {
1695
0
    aAncestors->AppendElement(aFrame);
1696
0
    aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
1697
0
  }
1698
0
  return aFrame;
1699
0
}
1700
1701
// Return true if aFrame1 is after aFrame2
1702
static bool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2)
1703
0
{
1704
0
  nsIFrame* f = aFrame2;
1705
0
  do {
1706
0
    f = f->GetNextSibling();
1707
0
    if (f == aFrame1)
1708
0
      return true;
1709
0
  } while (f);
1710
0
  return false;
1711
0
}
1712
1713
// static
1714
int32_t
1715
nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
1716
                                     nsIFrame* aFrame2,
1717
                                     int32_t aIf1Ancestor,
1718
                                     int32_t aIf2Ancestor,
1719
                                     nsIFrame* aCommonAncestor)
1720
0
{
1721
0
  MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
1722
0
  MOZ_ASSERT(aFrame2, "aFrame2 must not be null");
1723
0
1724
0
  AutoTArray<nsIFrame*,20> frame2Ancestors;
1725
0
  nsIFrame* nonCommonAncestor =
1726
0
    FillAncestors(aFrame2, aCommonAncestor, &frame2Ancestors);
1727
0
1728
0
  return DoCompareTreePosition(aFrame1, aFrame2, frame2Ancestors,
1729
0
                               aIf1Ancestor, aIf2Ancestor,
1730
0
                               nonCommonAncestor ? aCommonAncestor : nullptr);
1731
0
}
1732
1733
// static
1734
int32_t
1735
nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
1736
                                     nsIFrame* aFrame2,
1737
                                     nsTArray<nsIFrame*>& aFrame2Ancestors,
1738
                                     int32_t aIf1Ancestor,
1739
                                     int32_t aIf2Ancestor,
1740
                                     nsIFrame* aCommonAncestor)
1741
0
{
1742
0
  MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
1743
0
  MOZ_ASSERT(aFrame2, "aFrame2 must not be null");
1744
0
1745
0
  nsPresContext* presContext = aFrame1->PresContext();
1746
0
  if (presContext != aFrame2->PresContext()) {
1747
0
    NS_ERROR("no common ancestor at all, different documents");
1748
0
    return 0;
1749
0
  }
1750
0
1751
0
  AutoTArray<nsIFrame*,20> frame1Ancestors;
1752
0
  if (aCommonAncestor &&
1753
0
      !FillAncestors(aFrame1, aCommonAncestor, &frame1Ancestors)) {
1754
0
    // We reached the root of the frame tree ... if aCommonAncestor was set,
1755
0
    // it is wrong
1756
0
    return DoCompareTreePosition(aFrame1, aFrame2,
1757
0
                                 aIf1Ancestor, aIf2Ancestor, nullptr);
1758
0
  }
1759
0
1760
0
  int32_t last1 = int32_t(frame1Ancestors.Length()) - 1;
1761
0
  int32_t last2 = int32_t(aFrame2Ancestors.Length()) - 1;
1762
0
  while (last1 >= 0 && last2 >= 0 &&
1763
0
         frame1Ancestors[last1] == aFrame2Ancestors[last2]) {
1764
0
    last1--;
1765
0
    last2--;
1766
0
  }
1767
0
1768
0
  if (last1 < 0) {
1769
0
    if (last2 < 0) {
1770
0
      NS_ASSERTION(aFrame1 == aFrame2, "internal error?");
1771
0
      return 0;
1772
0
    }
1773
0
    // aFrame1 is an ancestor of aFrame2
1774
0
    return aIf1Ancestor;
1775
0
  }
1776
0
1777
0
  if (last2 < 0) {
1778
0
    // aFrame2 is an ancestor of aFrame1
1779
0
    return aIf2Ancestor;
1780
0
  }
1781
0
1782
0
  nsIFrame* ancestor1 = frame1Ancestors[last1];
1783
0
  nsIFrame* ancestor2 = aFrame2Ancestors[last2];
1784
0
  // Now we should be able to walk sibling chains to find which one is first
1785
0
  if (IsFrameAfter(ancestor2, ancestor1))
1786
0
    return -1;
1787
0
  if (IsFrameAfter(ancestor1, ancestor2))
1788
0
    return 1;
1789
0
  NS_WARNING("Frames were in different child lists???");
1790
0
  return 0;
1791
0
}
1792
1793
// static
1794
0
nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
1795
0
  if (!aFrame) {
1796
0
    return nullptr;
1797
0
  }
1798
0
1799
0
  nsIFrame* next;
1800
0
  while ((next = aFrame->GetNextSibling()) != nullptr) {
1801
0
    aFrame = next;
1802
0
  }
1803
0
  return aFrame;
1804
0
}
1805
1806
// static
1807
nsView*
1808
0
nsLayoutUtils::FindSiblingViewFor(nsView* aParentView, nsIFrame* aFrame) {
1809
0
  nsIFrame* parentViewFrame = aParentView->GetFrame();
1810
0
  nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nullptr;
1811
0
  for (nsView* insertBefore = aParentView->GetFirstChild(); insertBefore;
1812
0
       insertBefore = insertBefore->GetNextSibling()) {
1813
0
    nsIFrame* f = insertBefore->GetFrame();
1814
0
    if (!f) {
1815
0
      // this view could be some anonymous view attached to a meaningful parent
1816
0
      for (nsView* searchView = insertBefore->GetParent(); searchView;
1817
0
           searchView = searchView->GetParent()) {
1818
0
        f = searchView->GetFrame();
1819
0
        if (f) {
1820
0
          break;
1821
0
        }
1822
0
      }
1823
0
      NS_ASSERTION(f, "Can't find a frame anywhere!");
1824
0
    }
1825
0
    if (!f || !aFrame->GetContent() || !f->GetContent() ||
1826
0
        CompareTreePosition(aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) {
1827
0
      // aFrame's content is after f's content (or we just don't know),
1828
0
      // so put our view before f's view
1829
0
      return insertBefore;
1830
0
    }
1831
0
  }
1832
0
  return nullptr;
1833
0
}
1834
1835
//static
1836
nsIScrollableFrame*
1837
nsLayoutUtils::GetScrollableFrameFor(const nsIFrame *aScrolledFrame)
1838
0
{
1839
0
  nsIFrame *frame = aScrolledFrame->GetParent();
1840
0
  nsIScrollableFrame *sf = do_QueryFrame(frame);
1841
0
  return (sf && sf->GetScrolledFrame() == aScrolledFrame) ? sf : nullptr;
1842
0
}
1843
1844
/* static */ void
1845
nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer,
1846
                                         const nsIFrame* aViewportFrame,
1847
                                         const nsRect& aAnchorRect,
1848
                                         const nsIFrame* aFixedPosFrame,
1849
                                         nsPresContext* aPresContext,
1850
0
                                         const ContainerLayerParameters& aContainerParameters) {
1851
0
  // Find out the rect of the viewport frame relative to the reference frame.
1852
0
  // This, in conjunction with the container scale, will correspond to the
1853
0
  // coordinate-space of the built layer.
1854
0
  float factor = aPresContext->AppUnitsPerDevPixel();
1855
0
  Rect anchorRect(NSAppUnitsToFloatPixels(aAnchorRect.x, factor) *
1856
0
                    aContainerParameters.mXScale,
1857
0
                  NSAppUnitsToFloatPixels(aAnchorRect.y, factor) *
1858
0
                    aContainerParameters.mYScale,
1859
0
                  NSAppUnitsToFloatPixels(aAnchorRect.width, factor) *
1860
0
                    aContainerParameters.mXScale,
1861
0
                  NSAppUnitsToFloatPixels(aAnchorRect.height, factor) *
1862
0
                    aContainerParameters.mYScale);
1863
0
  // Need to transform anchorRect from the container layer's coordinate system
1864
0
  // into aLayer's coordinate system.
1865
0
  Matrix transform2d;
1866
0
  if (aLayer->GetTransform().Is2D(&transform2d)) {
1867
0
    transform2d.Invert();
1868
0
    anchorRect = transform2d.TransformBounds(anchorRect);
1869
0
  } else {
1870
0
    NS_ERROR("3D transform found between fixedpos content and its viewport (should never happen)");
1871
0
    anchorRect = Rect(0,0,0,0);
1872
0
  }
1873
0
1874
0
  // Work out the anchor point for this fixed position layer. We assume that
1875
0
  // any positioning set (left/top/right/bottom) indicates that the
1876
0
  // corresponding side of its container should be the anchor point,
1877
0
  // defaulting to top-left.
1878
0
  LayerPoint anchor(anchorRect.x, anchorRect.y);
1879
0
1880
0
  int32_t sides = eSideBitsNone;
1881
0
  if (aFixedPosFrame != aViewportFrame) {
1882
0
    const nsStylePosition* position = aFixedPosFrame->StylePosition();
1883
0
    if (position->mOffset.GetRightUnit() != eStyleUnit_Auto) {
1884
0
      sides |= eSideBitsRight;
1885
0
      if (position->mOffset.GetLeftUnit() != eStyleUnit_Auto) {
1886
0
        sides |= eSideBitsLeft;
1887
0
        anchor.x = anchorRect.x + anchorRect.width / 2.f;
1888
0
      } else {
1889
0
        anchor.x = anchorRect.XMost();
1890
0
      }
1891
0
    } else if (position->mOffset.GetLeftUnit() != eStyleUnit_Auto) {
1892
0
      sides |= eSideBitsLeft;
1893
0
    }
1894
0
    if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto) {
1895
0
      sides |= eSideBitsBottom;
1896
0
      if (position->mOffset.GetTopUnit() != eStyleUnit_Auto) {
1897
0
        sides |= eSideBitsTop;
1898
0
        anchor.y = anchorRect.y + anchorRect.height / 2.f;
1899
0
      } else {
1900
0
        anchor.y = anchorRect.YMost();
1901
0
      }
1902
0
    } else if (position->mOffset.GetTopUnit() != eStyleUnit_Auto) {
1903
0
      sides |= eSideBitsTop;
1904
0
    }
1905
0
  }
1906
0
1907
0
  ViewID id = ScrollIdForRootScrollFrame(aPresContext);
1908
0
  aLayer->SetFixedPositionData(id, anchor, sides);
1909
0
}
1910
1911
FrameMetrics::ViewID
1912
nsLayoutUtils::ScrollIdForRootScrollFrame(nsPresContext* aPresContext)
1913
0
{
1914
0
  ViewID id = FrameMetrics::NULL_SCROLL_ID;
1915
0
  if (nsIFrame* rootScrollFrame = aPresContext->PresShell()->GetRootScrollFrame()) {
1916
0
    if (nsIContent* content = rootScrollFrame->GetContent()) {
1917
0
      id = FindOrCreateIDFor(content);
1918
0
    }
1919
0
  }
1920
0
  return id;
1921
0
}
1922
1923
bool
1924
nsLayoutUtils::ViewportHasDisplayPort(nsPresContext* aPresContext)
1925
0
{
1926
0
  nsIFrame* rootScrollFrame =
1927
0
    aPresContext->PresShell()->GetRootScrollFrame();
1928
0
  return rootScrollFrame &&
1929
0
    nsLayoutUtils::HasDisplayPort(rootScrollFrame->GetContent());
1930
0
}
1931
1932
bool
1933
nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame)
1934
0
{
1935
0
  // Fixed-pos frames are parented by the viewport frame or the page content frame.
1936
0
  // We'll assume that printing/print preview don't have displayports for their
1937
0
  // pages!
1938
0
  nsIFrame* parent = aFrame->GetParent();
1939
0
  if (!parent || parent->GetParent() ||
1940
0
      aFrame->StyleDisplay()->mPosition != NS_STYLE_POSITION_FIXED) {
1941
0
    return false;
1942
0
  }
1943
0
  return ViewportHasDisplayPort(aFrame->PresContext());
1944
0
}
1945
1946
// static
1947
nsIScrollableFrame*
1948
nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,
1949
                                                     Direction aDirection)
1950
0
{
1951
0
  NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
1952
0
  for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
1953
0
    nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
1954
0
    if (scrollableFrame) {
1955
0
      ScrollStyles ss = scrollableFrame->GetScrollStyles();
1956
0
      uint32_t directions = scrollableFrame->GetPerceivedScrollingDirections();
1957
0
      if (aDirection == eVertical ?
1958
0
          (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN &&
1959
0
           (directions & nsIScrollableFrame::VERTICAL)) :
1960
0
          (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
1961
0
           (directions & nsIScrollableFrame::HORIZONTAL)))
1962
0
        return scrollableFrame;
1963
0
    }
1964
0
  }
1965
0
  return nullptr;
1966
0
}
1967
1968
// static
1969
nsIScrollableFrame*
1970
nsLayoutUtils::GetNearestScrollableFrame(nsIFrame* aFrame, uint32_t aFlags)
1971
0
{
1972
0
  NS_ASSERTION(aFrame, "GetNearestScrollableFrame expects a non-null frame");
1973
0
  for (nsIFrame* f = aFrame; f; f = (aFlags & SCROLLABLE_SAME_DOC) ?
1974
0
       f->GetParent() : nsLayoutUtils::GetCrossDocParentFrame(f)) {
1975
0
    nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
1976
0
    if (scrollableFrame) {
1977
0
      if (aFlags & SCROLLABLE_ONLY_ASYNC_SCROLLABLE) {
1978
0
        if (scrollableFrame->WantAsyncScroll()) {
1979
0
          return scrollableFrame;
1980
0
        }
1981
0
      } else {
1982
0
        ScrollStyles ss = scrollableFrame->GetScrollStyles();
1983
0
        if ((aFlags & SCROLLABLE_INCLUDE_HIDDEN) ||
1984
0
            ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN ||
1985
0
            ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
1986
0
          return scrollableFrame;
1987
0
        }
1988
0
      }
1989
0
      if (aFlags & SCROLLABLE_ALWAYS_MATCH_ROOT) {
1990
0
        nsIPresShell* ps = f->PresShell();
1991
0
        if (ps->GetRootScrollFrame() == f &&
1992
0
            ps->GetDocument() && ps->GetDocument()->IsRootDisplayDocument()) {
1993
0
          return scrollableFrame;
1994
0
        }
1995
0
      }
1996
0
    }
1997
0
    if ((aFlags & SCROLLABLE_FIXEDPOS_FINDS_ROOT) &&
1998
0
        f->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED &&
1999
0
        nsLayoutUtils::IsReallyFixedPos(f)) {
2000
0
      return f->PresShell()->GetRootScrollFrameAsScrollable();
2001
0
    }
2002
0
  }
2003
0
  return nullptr;
2004
0
}
2005
2006
// static
2007
nsRect
2008
nsLayoutUtils::GetScrolledRect(nsIFrame* aScrolledFrame,
2009
                               const nsRect& aScrolledFrameOverflowArea,
2010
                               const nsSize& aScrollPortSize,
2011
                               uint8_t aDirection)
2012
0
{
2013
0
  WritingMode wm = aScrolledFrame->GetWritingMode();
2014
0
  // Potentially override the frame's direction to use the direction found
2015
0
  // by ScrollFrameHelper::GetScrolledFrameDir()
2016
0
  wm.SetDirectionFromBidiLevel(aDirection == NS_STYLE_DIRECTION_RTL ? 1 : 0);
2017
0
2018
0
  nscoord x1 = aScrolledFrameOverflowArea.x,
2019
0
          x2 = aScrolledFrameOverflowArea.XMost(),
2020
0
          y1 = aScrolledFrameOverflowArea.y,
2021
0
          y2 = aScrolledFrameOverflowArea.YMost();
2022
0
2023
0
  bool horizontal = !wm.IsVertical();
2024
0
2025
0
  // Clamp the horizontal start-edge (x1 or x2, depending whether the logical
2026
0
  // axis that corresponds to horizontal progresses from L-R or R-L).
2027
0
  // In horizontal writing mode, we need to check IsInlineReversed() to see
2028
0
  // which side to clamp; in vertical mode, it depends on the block direction.
2029
0
  if ((horizontal && !wm.IsInlineReversed()) || wm.IsVerticalLR()) {
2030
0
    if (x1 < 0) {
2031
0
      x1 = 0;
2032
0
    }
2033
0
  } else {
2034
0
    if (x2 > aScrollPortSize.width) {
2035
0
      x2 = aScrollPortSize.width;
2036
0
    }
2037
0
    // When the scrolled frame chooses a size larger than its available width
2038
0
    // (because its padding alone is larger than the available width), we need
2039
0
    // to keep the start-edge of the scroll frame anchored to the start-edge of
2040
0
    // the scrollport.
2041
0
    // When the scrolled frame is RTL, this means moving it in our left-based
2042
0
    // coordinate system, so we need to compensate for its extra width here by
2043
0
    // effectively repositioning the frame.
2044
0
    nscoord extraWidth =
2045
0
      std::max(0, aScrolledFrame->GetSize().width - aScrollPortSize.width);
2046
0
    x2 += extraWidth;
2047
0
  }
2048
0
2049
0
  // Similarly, clamp the vertical start-edge.
2050
0
  // In horizontal writing mode, the block direction is always top-to-bottom;
2051
0
  // in vertical writing mode, we need to check IsInlineReversed().
2052
0
  if (horizontal || !wm.IsInlineReversed()) {
2053
0
    if (y1 < 0) {
2054
0
      y1 = 0;
2055
0
    }
2056
0
  } else {
2057
0
    if (y2 > aScrollPortSize.height) {
2058
0
      y2 = aScrollPortSize.height;
2059
0
    }
2060
0
    nscoord extraHeight =
2061
0
      std::max(0, aScrolledFrame->GetSize().height - aScrollPortSize.height);
2062
0
    y2 += extraHeight;
2063
0
  }
2064
0
2065
0
  return nsRect(x1, y1, x2 - x1, y2 - y1);
2066
0
}
2067
2068
//static
2069
bool
2070
nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
2071
                              ComputedStyle* aComputedStyle,
2072
                              CSSPseudoElementType aPseudoElement,
2073
                              nsPresContext* aPresContext)
2074
0
{
2075
0
  MOZ_ASSERT(aPresContext, "Must have a prescontext");
2076
0
2077
0
  RefPtr<ComputedStyle> pseudoContext;
2078
0
  if (aContent) {
2079
0
    pseudoContext = aPresContext->StyleSet()->
2080
0
      ProbePseudoElementStyle(*aContent->AsElement(), aPseudoElement,
2081
0
                              aComputedStyle);
2082
0
  }
2083
0
  return pseudoContext != nullptr;
2084
0
}
2085
2086
nsPoint
2087
nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(Event* aDOMEvent, nsIFrame* aFrame)
2088
0
{
2089
0
  if (!aDOMEvent)
2090
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2091
0
  WidgetEvent* event = aDOMEvent->WidgetEventPtr();
2092
0
  if (!event)
2093
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2094
0
  return GetEventCoordinatesRelativeTo(event, aFrame);
2095
0
}
2096
2097
nsPoint
2098
nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
2099
                                             nsIFrame* aFrame)
2100
0
{
2101
0
  if (!aEvent || (aEvent->mClass != eMouseEventClass &&
2102
0
                  aEvent->mClass != eMouseScrollEventClass &&
2103
0
                  aEvent->mClass != eWheelEventClass &&
2104
0
                  aEvent->mClass != eDragEventClass &&
2105
0
                  aEvent->mClass != eSimpleGestureEventClass &&
2106
0
                  aEvent->mClass != ePointerEventClass &&
2107
0
                  aEvent->mClass != eGestureNotifyEventClass &&
2108
0
                  aEvent->mClass != eTouchEventClass &&
2109
0
                  aEvent->mClass != eQueryContentEventClass))
2110
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2111
0
2112
0
  return GetEventCoordinatesRelativeTo(aEvent,
2113
0
           aEvent->AsGUIEvent()->mRefPoint,
2114
0
           aFrame);
2115
0
}
2116
2117
nsPoint
2118
nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
2119
                                             const LayoutDeviceIntPoint& aPoint,
2120
                                             nsIFrame* aFrame)
2121
0
{
2122
0
  if (!aFrame) {
2123
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2124
0
  }
2125
0
2126
0
  nsIWidget* widget = aEvent->AsGUIEvent()->mWidget;
2127
0
  if (!widget) {
2128
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2129
0
  }
2130
0
2131
0
  return GetEventCoordinatesRelativeTo(widget, aPoint, aFrame);
2132
0
}
2133
2134
nsPoint
2135
nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget,
2136
                                             const LayoutDeviceIntPoint& aPoint,
2137
                                             nsIFrame* aFrame)
2138
0
{
2139
0
  if (!aFrame || !aWidget) {
2140
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2141
0
  }
2142
0
2143
0
  nsView* view = aFrame->GetView();
2144
0
  if (view) {
2145
0
    nsIWidget* frameWidget = view->GetWidget();
2146
0
    if (frameWidget && frameWidget == aWidget) {
2147
0
      // Special case this cause it happens a lot.
2148
0
      // This also fixes bug 664707, events in the extra-special case of select
2149
0
      // dropdown popups that are transformed.
2150
0
      nsPresContext* presContext = aFrame->PresContext();
2151
0
      nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x),
2152
0
                 presContext->DevPixelsToAppUnits(aPoint.y));
2153
0
      pt = pt - view->ViewToWidgetOffset();
2154
0
      pt = pt.RemoveResolution(GetCurrentAPZResolutionScale(presContext->PresShell()));
2155
0
      return pt;
2156
0
    }
2157
0
  }
2158
0
2159
0
  /* If we walk up the frame tree and discover that any of the frames are
2160
0
   * transformed, we need to do extra work to convert from the global
2161
0
   * space to the local space.
2162
0
   */
2163
0
  nsIFrame* rootFrame = aFrame;
2164
0
  bool transformFound = false;
2165
0
  for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) {
2166
0
    if (f->IsTransformed()) {
2167
0
      transformFound = true;
2168
0
    }
2169
0
2170
0
    rootFrame = f;
2171
0
  }
2172
0
2173
0
  nsView* rootView = rootFrame->GetView();
2174
0
  if (!rootView) {
2175
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2176
0
  }
2177
0
2178
0
  nsPoint widgetToView = TranslateWidgetToView(rootFrame->PresContext(),
2179
0
                                               aWidget, aPoint, rootView);
2180
0
2181
0
  if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
2182
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2183
0
  }
2184
0
2185
0
  // Convert from root document app units to app units of the document aFrame
2186
0
  // is in.
2187
0
  int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
2188
0
  int32_t localAPD = aFrame->PresContext()->AppUnitsPerDevPixel();
2189
0
  widgetToView = widgetToView.ScaleToOtherAppUnits(rootAPD, localAPD);
2190
0
  nsIPresShell* shell = aFrame->PresShell();
2191
0
2192
0
  // XXX Bug 1224748 - Update nsLayoutUtils functions to correctly handle nsPresShell resolution
2193
0
  widgetToView = widgetToView.RemoveResolution(GetCurrentAPZResolutionScale(shell));
2194
0
2195
0
  /* If we encountered a transform, we can't do simple arithmetic to figure
2196
0
   * out how to convert back to aFrame's coordinates and must use the CTM.
2197
0
   */
2198
0
  if (transformFound || nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
2199
0
    return TransformRootPointToFrame(aFrame, widgetToView);
2200
0
  }
2201
0
2202
0
  /* Otherwise, all coordinate systems are translations of one another,
2203
0
   * so we can just subtract out the difference.
2204
0
   */
2205
0
  return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame);
2206
0
}
2207
2208
nsIFrame*
2209
nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
2210
                                                const WidgetEvent* aEvent)
2211
0
{
2212
0
#ifdef MOZ_XUL
2213
0
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2214
0
  if (!pm) {
2215
0
    return nullptr;
2216
0
  }
2217
0
  nsTArray<nsIFrame*> popups;
2218
0
  pm->GetVisiblePopups(popups);
2219
0
  uint32_t i;
2220
0
  // Search from top to bottom
2221
0
  for (i = 0; i < popups.Length(); i++) {
2222
0
    nsIFrame* popup = popups[i];
2223
0
    if (popup->PresContext()->GetRootPresContext() == aPresContext &&
2224
0
        popup->GetScrollableOverflowRect().Contains(
2225
0
          GetEventCoordinatesRelativeTo(aEvent, popup))) {
2226
0
      return popup;
2227
0
    }
2228
0
  }
2229
0
#endif
2230
0
  return nullptr;
2231
0
}
2232
2233
static void ConstrainToCoordValues(float& aStart, float& aSize)
2234
0
{
2235
0
  MOZ_ASSERT(aSize >= 0);
2236
0
2237
0
  // Here we try to make sure that the resulting nsRect will continue to cover
2238
0
  // as much of the area that was covered by the original gfx Rect as possible.
2239
0
2240
0
  // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since
2241
0
  // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this
2242
0
  // range:
2243
0
  float end = aStart + aSize;
2244
0
  aStart = clamped(aStart, float(nscoord_MIN), float(nscoord_MAX));
2245
0
  end = clamped(end, float(nscoord_MIN), float(nscoord_MAX));
2246
0
2247
0
  aSize = end - aStart;
2248
0
2249
0
  // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()
2250
0
  // can't return a value greater than nscoord_MAX. If aSize is greater than
2251
0
  // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect
2252
0
  // centered:
2253
0
  if (aSize > nscoord_MAX) {
2254
0
    float excess = aSize - nscoord_MAX;
2255
0
    excess /= 2;
2256
0
    aStart += excess;
2257
0
    aSize = (float)nscoord_MAX;
2258
0
  }
2259
0
}
2260
2261
/**
2262
 * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX.
2263
 *
2264
 * @param aVal The value to constrain (in/out)
2265
 */
2266
static void ConstrainToCoordValues(gfxFloat& aVal)
2267
0
{
2268
0
  if (aVal <= nscoord_MIN)
2269
0
    aVal = nscoord_MIN;
2270
0
  else if (aVal >= nscoord_MAX)
2271
0
    aVal = nscoord_MAX;
2272
0
}
2273
2274
static void ConstrainToCoordValues(gfxFloat& aStart, gfxFloat& aSize)
2275
0
{
2276
0
  gfxFloat max = aStart + aSize;
2277
0
2278
0
  // Clamp the end points to within nscoord range
2279
0
  ConstrainToCoordValues(aStart);
2280
0
  ConstrainToCoordValues(max);
2281
0
2282
0
  aSize = max - aStart;
2283
0
  // If the width if still greater than the max nscoord, then bring both
2284
0
  // endpoints in by the same amount until it fits.
2285
0
  if (aSize > nscoord_MAX) {
2286
0
    gfxFloat excess = aSize - nscoord_MAX;
2287
0
    excess /= 2;
2288
0
2289
0
    aStart += excess;
2290
0
    aSize = nscoord_MAX;
2291
0
  } else if (aSize < nscoord_MIN) {
2292
0
    gfxFloat excess = aSize - nscoord_MIN;
2293
0
    excess /= 2;
2294
0
2295
0
    aStart -= excess;
2296
0
    aSize = nscoord_MIN;
2297
0
  }
2298
0
}
2299
2300
nsRect
2301
nsLayoutUtils::RoundGfxRectToAppRect(const Rect &aRect, float aFactor)
2302
0
{
2303
0
  /* Get a new Rect whose units are app units by scaling by the specified factor. */
2304
0
  Rect scaledRect = aRect;
2305
0
  scaledRect.ScaleRoundOut(aFactor);
2306
0
2307
0
  /* We now need to constrain our results to the max and min values for coords. */
2308
0
  ConstrainToCoordValues(scaledRect.x, scaledRect.width);
2309
0
  ConstrainToCoordValues(scaledRect.y, scaledRect.height);
2310
0
2311
0
  /* Now typecast everything back.  This is guaranteed to be safe. */
2312
0
  if (aRect.IsEmpty()) {
2313
0
    return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()), 0, 0);
2314
0
  } else {
2315
0
    return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()),
2316
0
                  nscoord(scaledRect.Width()), nscoord(scaledRect.Height()));
2317
0
  }
2318
0
}
2319
2320
nsRect
2321
nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor)
2322
0
{
2323
0
  /* Get a new gfxRect whose units are app units by scaling by the specified factor. */
2324
0
  gfxRect scaledRect = aRect;
2325
0
  scaledRect.ScaleRoundOut(aFactor);
2326
0
2327
0
  /* We now need to constrain our results to the max and min values for coords. */
2328
0
  ConstrainToCoordValues(scaledRect.x, scaledRect.width);
2329
0
  ConstrainToCoordValues(scaledRect.y, scaledRect.height);
2330
0
2331
0
  /* Now typecast everything back.  This is guaranteed to be safe. */
2332
0
  if (aRect.IsEmpty()) {
2333
0
    return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()), 0, 0);
2334
0
  } else {
2335
0
    return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()),
2336
0
                  nscoord(scaledRect.Width()), nscoord(scaledRect.Height()));
2337
0
  }
2338
0
}
2339
2340
2341
nsRegion
2342
nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect,
2343
                                        const nscoord aRadii[8],
2344
                                        const nsRect& aContainedRect)
2345
0
{
2346
0
  // rectFullHeight and rectFullWidth together will approximately contain
2347
0
  // the total area of the frame minus the rounded corners.
2348
0
  nsRect rectFullHeight = aRoundedRect;
2349
0
  nscoord xDiff = std::max(aRadii[eCornerTopLeftX], aRadii[eCornerBottomLeftX]);
2350
0
  rectFullHeight.x += xDiff;
2351
0
  rectFullHeight.width -= std::max(aRadii[eCornerTopRightX],
2352
0
                                   aRadii[eCornerBottomRightX]) + xDiff;
2353
0
  nsRect r1;
2354
0
  r1.IntersectRect(rectFullHeight, aContainedRect);
2355
0
2356
0
  nsRect rectFullWidth = aRoundedRect;
2357
0
  nscoord yDiff = std::max(aRadii[eCornerTopLeftY], aRadii[eCornerTopRightY]);
2358
0
  rectFullWidth.y += yDiff;
2359
0
  rectFullWidth.height -= std::max(aRadii[eCornerBottomLeftY],
2360
0
                                   aRadii[eCornerBottomRightY]) + yDiff;
2361
0
  nsRect r2;
2362
0
  r2.IntersectRect(rectFullWidth, aContainedRect);
2363
0
2364
0
  nsRegion result;
2365
0
  result.Or(r1, r2);
2366
0
  return result;
2367
0
}
2368
2369
nsIntRegion
2370
nsLayoutUtils::RoundedRectIntersectIntRect(const nsIntRect& aRoundedRect,
2371
                                           const RectCornerRadii& aCornerRadii,
2372
                                           const nsIntRect& aContainedRect)
2373
0
{
2374
0
  // rectFullHeight and rectFullWidth together will approximately contain
2375
0
  // the total area of the frame minus the rounded corners.
2376
0
  nsIntRect rectFullHeight = aRoundedRect;
2377
0
  uint32_t xDiff = std::max(aCornerRadii.TopLeft().width,
2378
0
                            aCornerRadii.BottomLeft().width);
2379
0
  rectFullHeight.x += xDiff;
2380
0
  rectFullHeight.width -= std::max(aCornerRadii.TopRight().width,
2381
0
                                   aCornerRadii.BottomRight().width) + xDiff;
2382
0
  nsIntRect r1;
2383
0
  r1.IntersectRect(rectFullHeight, aContainedRect);
2384
0
2385
0
  nsIntRect rectFullWidth = aRoundedRect;
2386
0
  uint32_t yDiff = std::max(aCornerRadii.TopLeft().height,
2387
0
                            aCornerRadii.TopRight().height);
2388
0
  rectFullWidth.y += yDiff;
2389
0
  rectFullWidth.height -= std::max(aCornerRadii.BottomLeft().height,
2390
0
                                   aCornerRadii.BottomRight().height) + yDiff;
2391
0
  nsIntRect r2;
2392
0
  r2.IntersectRect(rectFullWidth, aContainedRect);
2393
0
2394
0
  nsIntRegion result;
2395
0
  result.Or(r1, r2);
2396
0
  return result;
2397
0
}
2398
2399
// Helper for RoundedRectIntersectsRect.
2400
static bool
2401
CheckCorner(nscoord aXOffset, nscoord aYOffset,
2402
            nscoord aXRadius, nscoord aYRadius)
2403
0
{
2404
0
  MOZ_ASSERT(aXOffset > 0 && aYOffset > 0,
2405
0
             "must not pass nonpositives to CheckCorner");
2406
0
  MOZ_ASSERT(aXRadius >= 0 && aYRadius >= 0,
2407
0
             "must not pass negatives to CheckCorner");
2408
0
2409
0
  // Avoid floating point math unless we're either (1) within the
2410
0
  // quarter-ellipse area at the rounded corner or (2) outside the
2411
0
  // rounding.
2412
0
  if (aXOffset >= aXRadius || aYOffset >= aYRadius)
2413
0
    return true;
2414
0
2415
0
  // Convert coordinates to a unit circle with (0,0) as the center of
2416
0
  // curvature, and see if we're inside the circle or outside.
2417
0
  float scaledX = float(aXRadius - aXOffset) / float(aXRadius);
2418
0
  float scaledY = float(aYRadius - aYOffset) / float(aYRadius);
2419
0
  return scaledX * scaledX + scaledY * scaledY < 1.0f;
2420
0
}
2421
2422
bool
2423
nsLayoutUtils::RoundedRectIntersectsRect(const nsRect& aRoundedRect,
2424
                                         const nscoord aRadii[8],
2425
                                         const nsRect& aTestRect)
2426
0
{
2427
0
  if (!aTestRect.Intersects(aRoundedRect))
2428
0
    return false;
2429
0
2430
0
  // distances from this edge of aRoundedRect to opposite edge of aTestRect,
2431
0
  // which we know are positive due to the Intersects check above.
2432
0
  nsMargin insets;
2433
0
  insets.top = aTestRect.YMost() - aRoundedRect.y;
2434
0
  insets.right = aRoundedRect.XMost() - aTestRect.x;
2435
0
  insets.bottom = aRoundedRect.YMost() - aTestRect.y;
2436
0
  insets.left = aTestRect.XMost() - aRoundedRect.x;
2437
0
2438
0
  // Check whether the bottom-right corner of aTestRect is inside the
2439
0
  // top left corner of aBounds when rounded by aRadii, etc.  If any
2440
0
  // corner is not, then fail; otherwise succeed.
2441
0
  return CheckCorner(insets.left, insets.top,
2442
0
                     aRadii[eCornerTopLeftX],
2443
0
                     aRadii[eCornerTopLeftY]) &&
2444
0
         CheckCorner(insets.right, insets.top,
2445
0
                     aRadii[eCornerTopRightX],
2446
0
                     aRadii[eCornerTopRightY]) &&
2447
0
         CheckCorner(insets.right, insets.bottom,
2448
0
                     aRadii[eCornerBottomRightX],
2449
0
                     aRadii[eCornerBottomRightY]) &&
2450
0
         CheckCorner(insets.left, insets.bottom,
2451
0
                     aRadii[eCornerBottomLeftX],
2452
0
                     aRadii[eCornerBottomLeftY]);
2453
0
}
2454
2455
nsRect
2456
nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds,
2457
                                   const Matrix4x4 &aMatrix, float aFactor)
2458
0
{
2459
0
  RectDouble image = RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
2460
0
                                NSAppUnitsToDoublePixels(aBounds.y, aFactor),
2461
0
                                NSAppUnitsToDoublePixels(aBounds.width, aFactor),
2462
0
                                NSAppUnitsToDoublePixels(aBounds.height, aFactor));
2463
0
2464
0
  RectDouble maxBounds = RectDouble(double(nscoord_MIN) / aFactor * 0.5,
2465
0
                                    double(nscoord_MIN) / aFactor * 0.5,
2466
0
                                    double(nscoord_MAX) / aFactor,
2467
0
                                    double(nscoord_MAX) / aFactor);
2468
0
2469
0
  image = aMatrix.TransformAndClipBounds(image, maxBounds);
2470
0
2471
0
  return RoundGfxRectToAppRect(ThebesRect(image), aFactor);
2472
0
}
2473
2474
nsRect
2475
nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds,
2476
                                   const Matrix4x4Flagged &aMatrix, float aFactor)
2477
0
{
2478
0
  RectDouble image = RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
2479
0
                                NSAppUnitsToDoublePixels(aBounds.y, aFactor),
2480
0
                                NSAppUnitsToDoublePixels(aBounds.width, aFactor),
2481
0
                                NSAppUnitsToDoublePixels(aBounds.height, aFactor));
2482
0
2483
0
  RectDouble maxBounds = RectDouble(double(nscoord_MIN) / aFactor * 0.5,
2484
0
                                    double(nscoord_MIN) / aFactor * 0.5,
2485
0
                                    double(nscoord_MAX) / aFactor,
2486
0
                                    double(nscoord_MAX) / aFactor);
2487
0
2488
0
  image = aMatrix.TransformAndClipBounds(image, maxBounds);
2489
0
2490
0
  return RoundGfxRectToAppRect(ThebesRect(image), aFactor);
2491
0
}
2492
2493
nsPoint
2494
nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint,
2495
                                    const Matrix4x4 &aMatrix, float aFactor)
2496
0
{
2497
0
  gfxPoint image = gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
2498
0
                            NSAppUnitsToFloatPixels(aPoint.y, aFactor));
2499
0
  image = aMatrix.TransformPoint(image);
2500
0
  return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
2501
0
                 NSFloatPixelsToAppUnits(float(image.y), aFactor));
2502
0
}
2503
2504
void
2505
nsLayoutUtils::PostTranslate(Matrix4x4& aTransform, const nsPoint& aOrigin, float aAppUnitsPerPixel, bool aRounded)
2506
0
{
2507
0
  Point3D gfxOrigin =
2508
0
    Point3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel),
2509
0
            NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel),
2510
0
            0.0f);
2511
0
  if (aRounded) {
2512
0
    gfxOrigin.x = NS_round(gfxOrigin.x);
2513
0
    gfxOrigin.y = NS_round(gfxOrigin.y);
2514
0
  }
2515
0
  aTransform.PostTranslate(gfxOrigin);
2516
0
}
2517
2518
// We want to this return true for the scroll frame, but not the
2519
// scrolled frame (which has the same content).
2520
bool
2521
nsLayoutUtils::FrameHasDisplayPort(nsIFrame* aFrame, const nsIFrame* aScrolledFrame)
2522
0
{
2523
0
  if (!aFrame->GetContent() || !HasDisplayPort(aFrame->GetContent())) {
2524
0
    return false;
2525
0
  }
2526
0
  nsIScrollableFrame* sf = do_QueryFrame(aFrame);
2527
0
  if (sf) {
2528
0
    if (aScrolledFrame && aScrolledFrame != sf->GetScrolledFrame()) {
2529
0
      return false;
2530
0
    }
2531
0
    return true;
2532
0
  }
2533
0
  return false;
2534
0
}
2535
2536
Matrix4x4Flagged
2537
nsLayoutUtils::GetTransformToAncestor(const nsIFrame *aFrame,
2538
                                      const nsIFrame *aAncestor,
2539
                                      uint32_t aFlags,
2540
                                      nsIFrame** aOutAncestor)
2541
0
{
2542
0
  nsIFrame* parent;
2543
0
  Matrix4x4Flagged ctm;
2544
0
  if (aFrame == aAncestor) {
2545
0
    return ctm;
2546
0
  }
2547
0
  ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aFlags);
2548
0
  while (parent && parent != aAncestor &&
2549
0
    (!(aFlags & nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
2550
0
      (!parent->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
2551
0
       !parent->IsStackingContext() &&
2552
0
       !FrameHasDisplayPort(parent)))) {
2553
0
    if (!parent->Extend3DContext()) {
2554
0
      ctm.ProjectTo2D();
2555
0
    }
2556
0
    ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent, aFlags);
2557
0
  }
2558
0
  if (aOutAncestor) {
2559
0
    *aOutAncestor = parent;
2560
0
  }
2561
0
  return ctm;
2562
0
}
2563
2564
gfxSize
2565
nsLayoutUtils::GetTransformToAncestorScale(nsIFrame* aFrame)
2566
0
{
2567
0
  Matrix4x4Flagged transform = GetTransformToAncestor(aFrame,
2568
0
      nsLayoutUtils::GetDisplayRootFrame(aFrame));
2569
0
  Matrix transform2D;
2570
0
  if (transform.Is2D(&transform2D)) {
2571
0
    return ThebesMatrix(transform2D).ScaleFactors(true);
2572
0
  }
2573
0
  return gfxSize(1, 1);
2574
0
}
2575
2576
static Matrix4x4Flagged
2577
GetTransformToAncestorExcludingAnimated(nsIFrame* aFrame,
2578
                                        const nsIFrame* aAncestor)
2579
0
{
2580
0
  nsIFrame* parent;
2581
0
  Matrix4x4Flagged ctm;
2582
0
  if (aFrame == aAncestor) {
2583
0
    return ctm;
2584
0
  }
2585
0
  if (ActiveLayerTracker::IsScaleSubjectToAnimation(aFrame)) {
2586
0
    return ctm;
2587
0
  }
2588
0
  ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
2589
0
  while (parent && parent != aAncestor) {
2590
0
    if (ActiveLayerTracker::IsScaleSubjectToAnimation(parent)) {
2591
0
      return Matrix4x4Flagged();
2592
0
    }
2593
0
    if (!parent->Extend3DContext()) {
2594
0
      ctm.ProjectTo2D();
2595
0
    }
2596
0
    ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
2597
0
  }
2598
0
  return ctm;
2599
0
}
2600
2601
gfxSize
2602
nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(nsIFrame* aFrame)
2603
0
{
2604
0
  Matrix4x4Flagged transform = GetTransformToAncestorExcludingAnimated(aFrame,
2605
0
      nsLayoutUtils::GetDisplayRootFrame(aFrame));
2606
0
  Matrix transform2D;
2607
0
  if (transform.Is2D(&transform2D)) {
2608
0
    return ThebesMatrix(transform2D).ScaleFactors(true);
2609
0
  }
2610
0
  return gfxSize(1, 1);
2611
0
}
2612
2613
nsIFrame*
2614
nsLayoutUtils::FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2)
2615
0
{
2616
0
  AutoTArray<nsIFrame*,100> ancestors1;
2617
0
  AutoTArray<nsIFrame*,100> ancestors2;
2618
0
  nsIFrame* commonAncestor = nullptr;
2619
0
  if (aFrame1->PresContext() == aFrame2->PresContext()) {
2620
0
    commonAncestor = aFrame1->PresShell()->GetRootFrame();
2621
0
  }
2622
0
  for (nsIFrame* f = aFrame1; f != commonAncestor;
2623
0
       f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
2624
0
    ancestors1.AppendElement(f);
2625
0
  }
2626
0
  for (nsIFrame* f = aFrame2; f != commonAncestor;
2627
0
       f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
2628
0
    ancestors2.AppendElement(f);
2629
0
  }
2630
0
  uint32_t minLengths = std::min(ancestors1.Length(), ancestors2.Length());
2631
0
  for (uint32_t i = 1; i <= minLengths; ++i) {
2632
0
    if (ancestors1[ancestors1.Length() - i] == ancestors2[ancestors2.Length() - i]) {
2633
0
      commonAncestor = ancestors1[ancestors1.Length() - i];
2634
0
    } else {
2635
0
      break;
2636
0
    }
2637
0
  }
2638
0
  return commonAncestor;
2639
0
}
2640
2641
nsLayoutUtils::TransformResult
2642
nsLayoutUtils::TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame,
2643
                               uint32_t aPointCount, CSSPoint* aPoints)
2644
0
{
2645
0
  nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
2646
0
  if (!nearestCommonAncestor) {
2647
0
    return NO_COMMON_ANCESTOR;
2648
0
  }
2649
0
  Matrix4x4Flagged downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
2650
0
  if (downToDest.IsSingular()) {
2651
0
    return NONINVERTIBLE_TRANSFORM;
2652
0
  }
2653
0
  downToDest.Invert();
2654
0
  Matrix4x4Flagged upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
2655
0
  CSSToLayoutDeviceScale devPixelsPerCSSPixelFromFrame =
2656
0
      aFromFrame->PresContext()->CSSToDevPixelScale();
2657
0
  CSSToLayoutDeviceScale devPixelsPerCSSPixelToFrame =
2658
0
      aToFrame->PresContext()->CSSToDevPixelScale();
2659
0
  for (uint32_t i = 0; i < aPointCount; ++i) {
2660
0
    LayoutDevicePoint devPixels = aPoints[i] * devPixelsPerCSSPixelFromFrame;
2661
0
    // What should the behaviour be if some of the points aren't invertible
2662
0
    // and others are? Just assume all points are for now.
2663
0
    Point toDevPixels = downToDest.ProjectPoint(
2664
0
        (upToAncestor.TransformPoint(Point(devPixels.x, devPixels.y)))).As2DPoint();
2665
0
    // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct
2666
0
    // answer instead of some inaccuracy multiplying a number by its reciprocal.
2667
0
    aPoints[i] = LayoutDevicePoint(toDevPixels.x, toDevPixels.y) /
2668
0
        devPixelsPerCSSPixelToFrame;
2669
0
  }
2670
0
  return TRANSFORM_SUCCEEDED;
2671
0
}
2672
2673
nsLayoutUtils::TransformResult
2674
nsLayoutUtils::TransformPoint(nsIFrame* aFromFrame, nsIFrame* aToFrame,
2675
                              nsPoint& aPoint)
2676
0
{
2677
0
  nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
2678
0
  if (!nearestCommonAncestor) {
2679
0
    return NO_COMMON_ANCESTOR;
2680
0
  }
2681
0
  Matrix4x4Flagged downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
2682
0
  if (downToDest.IsSingular()) {
2683
0
    return NONINVERTIBLE_TRANSFORM;
2684
0
  }
2685
0
  downToDest.Invert();
2686
0
  Matrix4x4Flagged upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
2687
0
2688
0
  float devPixelsPerAppUnitFromFrame =
2689
0
    1.0f / aFromFrame->PresContext()->AppUnitsPerDevPixel();
2690
0
  float devPixelsPerAppUnitToFrame =
2691
0
    1.0f / aToFrame->PresContext()->AppUnitsPerDevPixel();
2692
0
  Point4D toDevPixels = downToDest.ProjectPoint(
2693
0
      upToAncestor.TransformPoint(Point(aPoint.x * devPixelsPerAppUnitFromFrame,
2694
0
                                        aPoint.y * devPixelsPerAppUnitFromFrame)));
2695
0
  if (!toDevPixels.HasPositiveWCoord()) {
2696
0
    // Not strictly true, but we failed to get a valid point in this
2697
0
    // coordinate space.
2698
0
    return NONINVERTIBLE_TRANSFORM;
2699
0
  }
2700
0
  aPoint.x = NSToCoordRound(toDevPixels.x / devPixelsPerAppUnitToFrame);
2701
0
  aPoint.y = NSToCoordRound(toDevPixels.y / devPixelsPerAppUnitToFrame);
2702
0
  return TRANSFORM_SUCCEEDED;
2703
0
}
2704
2705
nsLayoutUtils::TransformResult
2706
nsLayoutUtils::TransformRect(nsIFrame* aFromFrame, nsIFrame* aToFrame,
2707
                             nsRect& aRect)
2708
0
{
2709
0
  nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
2710
0
  if (!nearestCommonAncestor) {
2711
0
    return NO_COMMON_ANCESTOR;
2712
0
  }
2713
0
  Matrix4x4Flagged downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
2714
0
  if (downToDest.IsSingular()) {
2715
0
    return NONINVERTIBLE_TRANSFORM;
2716
0
  }
2717
0
  downToDest.Invert();
2718
0
  Matrix4x4Flagged upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
2719
0
2720
0
  float devPixelsPerAppUnitFromFrame =
2721
0
    1.0f / aFromFrame->PresContext()->AppUnitsPerDevPixel();
2722
0
  float devPixelsPerAppUnitToFrame =
2723
0
    1.0f / aToFrame->PresContext()->AppUnitsPerDevPixel();
2724
0
  gfx::Rect toDevPixels = downToDest.ProjectRectBounds(
2725
0
    upToAncestor.ProjectRectBounds(
2726
0
      gfx::Rect(aRect.x * devPixelsPerAppUnitFromFrame,
2727
0
                aRect.y * devPixelsPerAppUnitFromFrame,
2728
0
                aRect.width * devPixelsPerAppUnitFromFrame,
2729
0
                aRect.height * devPixelsPerAppUnitFromFrame),
2730
0
      Rect(-std::numeric_limits<Float>::max() * 0.5f,
2731
0
           -std::numeric_limits<Float>::max() * 0.5f,
2732
0
           std::numeric_limits<Float>::max(),
2733
0
           std::numeric_limits<Float>::max())),
2734
0
    Rect(-std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame * 0.5f,
2735
0
         -std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame * 0.5f,
2736
0
         std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame,
2737
0
         std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame));
2738
0
  aRect.x = NSToCoordRound(toDevPixels.x / devPixelsPerAppUnitToFrame);
2739
0
  aRect.y = NSToCoordRound(toDevPixels.y / devPixelsPerAppUnitToFrame);
2740
0
  aRect.width = NSToCoordRound(toDevPixels.width / devPixelsPerAppUnitToFrame);
2741
0
  aRect.height = NSToCoordRound(toDevPixels.height / devPixelsPerAppUnitToFrame);
2742
0
  return TRANSFORM_SUCCEEDED;
2743
0
}
2744
2745
nsRect
2746
nsLayoutUtils::GetRectRelativeToFrame(Element* aElement, nsIFrame* aFrame)
2747
0
{
2748
0
  if (!aElement || !aFrame) {
2749
0
    return nsRect();
2750
0
  }
2751
0
2752
0
  nsIFrame* frame = aElement->GetPrimaryFrame();
2753
0
  if (!frame) {
2754
0
    return nsRect();
2755
0
  }
2756
0
2757
0
  nsRect rect = frame->GetRectRelativeToSelf();
2758
0
  nsLayoutUtils::TransformResult rv =
2759
0
    nsLayoutUtils::TransformRect(frame, aFrame, rect);
2760
0
  if (rv != nsLayoutUtils::TRANSFORM_SUCCEEDED) {
2761
0
    return nsRect();
2762
0
  }
2763
0
2764
0
  return rect;
2765
0
}
2766
2767
bool
2768
nsLayoutUtils::ContainsPoint(const nsRect& aRect, const nsPoint& aPoint,
2769
                             nscoord aInflateSize)
2770
0
{
2771
0
  nsRect rect = aRect;
2772
0
  rect.Inflate(aInflateSize);
2773
0
  return rect.Contains(aPoint);
2774
0
}
2775
2776
nsRect
2777
nsLayoutUtils::ClampRectToScrollFrames(nsIFrame* aFrame, const nsRect& aRect)
2778
0
{
2779
0
  nsIFrame* closestScrollFrame =
2780
0
    nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::Scroll);
2781
0
2782
0
  nsRect resultRect = aRect;
2783
0
2784
0
  while (closestScrollFrame) {
2785
0
    nsIScrollableFrame* sf = do_QueryFrame(closestScrollFrame);
2786
0
2787
0
    nsRect scrollPortRect = sf->GetScrollPortRect();
2788
0
    nsLayoutUtils::TransformRect(closestScrollFrame, aFrame, scrollPortRect);
2789
0
2790
0
    resultRect = resultRect.Intersect(scrollPortRect);
2791
0
2792
0
    // Check whether aRect is visible in the scroll frame or not.
2793
0
    if (resultRect.IsEmpty()) {
2794
0
      break;
2795
0
    }
2796
0
2797
0
    // Get next ancestor scroll frame.
2798
0
    closestScrollFrame = nsLayoutUtils::GetClosestFrameOfType(
2799
0
      closestScrollFrame->GetParent(), LayoutFrameType::Scroll);
2800
0
  }
2801
0
2802
0
  return resultRect;
2803
0
}
2804
2805
bool
2806
nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
2807
                                         Matrix4x4Flagged* aTransform)
2808
0
{
2809
0
  // FIXME/bug 796690: we can sometimes compute a transform in these
2810
0
  // cases, it just increases complexity considerably.  Punt for now.
2811
0
  if (aFrame->Extend3DContext() || aFrame->HasTransformGetter()) {
2812
0
    return false;
2813
0
  }
2814
0
2815
0
  nsIFrame* root = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2816
0
  if (root->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)) {
2817
0
    // Content may have been invalidated, so we can't reliably compute
2818
0
    // the "layer transform" in general.
2819
0
    return false;
2820
0
  }
2821
0
  // If the caller doesn't care about the value, early-return to skip
2822
0
  // overhead below.
2823
0
  if (!aTransform) {
2824
0
    return true;
2825
0
  }
2826
0
2827
0
  nsDisplayListBuilder builder(root,
2828
0
                               nsDisplayListBuilderMode::TRANSFORM_COMPUTATION,
2829
0
                               false/*don't build caret*/);
2830
0
  builder.BeginFrame();
2831
0
  nsDisplayList list;
2832
0
  nsDisplayTransform* item =
2833
0
    MakeDisplayItem<nsDisplayTransform>(&builder, aFrame, &list, nsRect());
2834
0
2835
0
  *aTransform = item->GetTransform();
2836
0
  item->Destroy(&builder);
2837
0
2838
0
  builder.EndFrame();
2839
0
2840
0
  return true;
2841
0
}
2842
2843
static bool
2844
TransformGfxPointFromAncestor(nsIFrame *aFrame,
2845
                              const Point &aPoint,
2846
                              nsIFrame *aAncestor,
2847
                              Point* aOut)
2848
0
{
2849
0
  Matrix4x4Flagged ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
2850
0
  ctm.Invert();
2851
0
  Point4D point = ctm.ProjectPoint(aPoint);
2852
0
  if (!point.HasPositiveWCoord()) {
2853
0
    return false;
2854
0
  }
2855
0
  *aOut = point.As2DPoint();
2856
0
  return true;
2857
0
}
2858
2859
static Rect
2860
TransformGfxRectToAncestor(const nsIFrame *aFrame,
2861
                           const Rect &aRect,
2862
                           const nsIFrame *aAncestor,
2863
                           bool* aPreservesAxisAlignedRectangles = nullptr,
2864
                           Maybe<Matrix4x4Flagged>* aMatrixCache = nullptr,
2865
                           bool aStopAtStackingContextAndDisplayPortAndOOFFrame = false,
2866
                           nsIFrame** aOutAncestor = nullptr)
2867
0
{
2868
0
  Matrix4x4Flagged ctm;
2869
0
  if (aMatrixCache && *aMatrixCache) {
2870
0
    // We are given a matrix to use, so use it
2871
0
    ctm = aMatrixCache->value();
2872
0
  } else {
2873
0
    // Else, compute it
2874
0
    uint32_t flags = 0;
2875
0
    if (aStopAtStackingContextAndDisplayPortAndOOFFrame) {
2876
0
      flags |= nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT;
2877
0
    }
2878
0
    ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor, flags, aOutAncestor);
2879
0
    if (aMatrixCache) {
2880
0
      // and put it in the cache, if provided
2881
0
      *aMatrixCache = Some(ctm);
2882
0
    }
2883
0
  }
2884
0
  // Fill out the axis-alignment flag
2885
0
  if (aPreservesAxisAlignedRectangles) {
2886
0
    Matrix matrix2d;
2887
0
    *aPreservesAxisAlignedRectangles =
2888
0
      ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles();
2889
0
  }
2890
0
  const nsIFrame* ancestor = aOutAncestor ? *aOutAncestor : aAncestor;
2891
0
  float factor = ancestor->PresContext()->AppUnitsPerDevPixel();
2892
0
  Rect maxBounds = Rect(float(nscoord_MIN) / factor * 0.5,
2893
0
                        float(nscoord_MIN) / factor * 0.5,
2894
0
                        float(nscoord_MAX) / factor,
2895
0
                        float(nscoord_MAX) / factor);
2896
0
  return ctm.TransformAndClipBounds(aRect, maxBounds);
2897
0
}
2898
2899
static SVGTextFrame*
2900
GetContainingSVGTextFrame(const nsIFrame* aFrame)
2901
0
{
2902
0
  if (!nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
2903
0
    return nullptr;
2904
0
  }
2905
0
2906
0
  return static_cast<SVGTextFrame*>(nsLayoutUtils::GetClosestFrameOfType(
2907
0
    aFrame->GetParent(), LayoutFrameType::SVGText));
2908
0
}
2909
2910
nsPoint
2911
nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame,
2912
                                             const nsPoint& aPoint,
2913
                                             nsIFrame* aAncestor)
2914
0
{
2915
0
    SVGTextFrame* text = GetContainingSVGTextFrame(aFrame);
2916
0
2917
0
    float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
2918
0
    Point result(NSAppUnitsToFloatPixels(aPoint.x, factor),
2919
0
                 NSAppUnitsToFloatPixels(aPoint.y, factor));
2920
0
2921
0
    if (text) {
2922
0
        if (!TransformGfxPointFromAncestor(text, result, aAncestor, &result)) {
2923
0
            return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2924
0
        }
2925
0
        result = text->TransformFramePointToTextChild(result, aFrame);
2926
0
    } else {
2927
0
        if (!TransformGfxPointFromAncestor(aFrame, result, nullptr, &result)) {
2928
0
            return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2929
0
        }
2930
0
    }
2931
0
2932
0
    return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
2933
0
                   NSFloatPixelsToAppUnits(float(result.y), factor));
2934
0
}
2935
2936
nsRect
2937
nsLayoutUtils::TransformFrameRectToAncestor(const nsIFrame* aFrame,
2938
                                            const nsRect& aRect,
2939
                                            const nsIFrame* aAncestor,
2940
                                            bool* aPreservesAxisAlignedRectangles /* = nullptr */,
2941
                                            Maybe<Matrix4x4Flagged>* aMatrixCache /* = nullptr */,
2942
                                            bool aStopAtStackingContextAndDisplayPortAndOOFFrame /* = false */,
2943
                                            nsIFrame** aOutAncestor /* = nullptr */)
2944
0
{
2945
0
  SVGTextFrame* text = GetContainingSVGTextFrame(aFrame);
2946
0
2947
0
  float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2948
0
  Rect result;
2949
0
2950
0
  if (text) {
2951
0
    result = ToRect(text->TransformFrameRectFromTextChild(aRect, aFrame));
2952
0
    result = TransformGfxRectToAncestor(text, result, aAncestor,
2953
0
                                        nullptr, aMatrixCache,
2954
0
                                        aStopAtStackingContextAndDisplayPortAndOOFFrame, aOutAncestor);
2955
0
    // TransformFrameRectFromTextChild could involve any kind of transform, we
2956
0
    // could drill down into it to get an answer out of it but we don't yet.
2957
0
    if (aPreservesAxisAlignedRectangles)
2958
0
      *aPreservesAxisAlignedRectangles = false;
2959
0
  } else {
2960
0
    result = Rect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
2961
0
                  NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
2962
0
                  NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
2963
0
                  NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
2964
0
    result = TransformGfxRectToAncestor(aFrame, result, aAncestor,
2965
0
                                        aPreservesAxisAlignedRectangles, aMatrixCache,
2966
0
                                        aStopAtStackingContextAndDisplayPortAndOOFFrame, aOutAncestor);
2967
0
  }
2968
0
2969
0
  float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
2970
0
  return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),
2971
0
                NSFloatPixelsToAppUnits(float(result.y), destAppUnitsPerDevPixel),
2972
0
                NSFloatPixelsToAppUnits(float(result.width), destAppUnitsPerDevPixel),
2973
0
                NSFloatPixelsToAppUnits(float(result.height), destAppUnitsPerDevPixel));
2974
0
}
2975
2976
0
static LayoutDeviceIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
2977
0
  LayoutDeviceIntPoint offset(0, 0);
2978
0
  while ((aWidget->WindowType() == eWindowType_child ||
2979
0
          aWidget->IsPlugin())) {
2980
0
    nsIWidget* parent = aWidget->GetParent();
2981
0
    if (!parent) {
2982
0
      break;
2983
0
    }
2984
0
    LayoutDeviceIntRect bounds = aWidget->GetBounds();
2985
0
    offset += bounds.TopLeft();
2986
0
    aWidget = parent;
2987
0
  }
2988
0
  aRootWidget = aWidget;
2989
0
  return offset;
2990
0
}
2991
2992
LayoutDeviceIntPoint
2993
0
nsLayoutUtils::WidgetToWidgetOffset(nsIWidget* aFrom, nsIWidget* aTo) {
2994
0
  nsIWidget* fromRoot;
2995
0
  LayoutDeviceIntPoint fromOffset = GetWidgetOffset(aFrom, fromRoot);
2996
0
  nsIWidget* toRoot;
2997
0
  LayoutDeviceIntPoint toOffset = GetWidgetOffset(aTo, toRoot);
2998
0
2999
0
  if (fromRoot == toRoot) {
3000
0
    return fromOffset - toOffset;
3001
0
  }
3002
0
  return aFrom->WidgetToScreenOffset() - aTo->WidgetToScreenOffset();
3003
0
}
3004
3005
nsPoint
3006
nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext,
3007
                                     nsIWidget* aWidget, const LayoutDeviceIntPoint& aPt,
3008
                                     nsView* aView)
3009
0
{
3010
0
  nsPoint viewOffset;
3011
0
  nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset);
3012
0
  if (!viewWidget) {
3013
0
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
3014
0
  }
3015
0
3016
0
  LayoutDeviceIntPoint widgetPoint = aPt + WidgetToWidgetOffset(aWidget, viewWidget);
3017
0
  nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x),
3018
0
                         aPresContext->DevPixelsToAppUnits(widgetPoint.y));
3019
0
  return widgetAppUnits - viewOffset;
3020
0
}
3021
3022
LayoutDeviceIntPoint
3023
nsLayoutUtils::TranslateViewToWidget(nsPresContext* aPresContext,
3024
                                     nsView* aView, nsPoint aPt,
3025
                                     nsIWidget* aWidget)
3026
0
{
3027
0
  nsPoint viewOffset;
3028
0
  nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset);
3029
0
  if (!viewWidget) {
3030
0
    return LayoutDeviceIntPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
3031
0
  }
3032
0
3033
0
  nsPoint pt = (aPt +
3034
0
  viewOffset).ApplyResolution(GetCurrentAPZResolutionScale(aPresContext->PresShell()));
3035
0
  LayoutDeviceIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(pt.x),
3036
0
                                            aPresContext->AppUnitsToDevPixels(pt.y));
3037
0
  return relativeToViewWidget + WidgetToWidgetOffset(viewWidget, aWidget);
3038
0
}
3039
3040
// Combine aNewBreakType with aOrigBreakType, but limit the break types
3041
// to StyleClear::Left, Right, Both.
3042
StyleClear
3043
nsLayoutUtils::CombineBreakType(StyleClear aOrigBreakType,
3044
                                StyleClear aNewBreakType)
3045
0
{
3046
0
  StyleClear breakType = aOrigBreakType;
3047
0
  switch(breakType) {
3048
0
    case StyleClear::Left:
3049
0
      if (StyleClear::Right == aNewBreakType ||
3050
0
          StyleClear::Both == aNewBreakType) {
3051
0
        breakType = StyleClear::Both;
3052
0
      }
3053
0
      break;
3054
0
    case StyleClear::Right:
3055
0
      if (StyleClear::Left == aNewBreakType ||
3056
0
          StyleClear::Both == aNewBreakType) {
3057
0
        breakType = StyleClear::Both;
3058
0
      }
3059
0
      break;
3060
0
    case StyleClear::None:
3061
0
      if (StyleClear::Left == aNewBreakType ||
3062
0
          StyleClear::Right == aNewBreakType ||
3063
0
          StyleClear::Both == aNewBreakType) {
3064
0
        breakType = aNewBreakType;
3065
0
      }
3066
0
      break;
3067
0
    default:
3068
0
      break;
3069
0
  }
3070
0
  return breakType;
3071
0
}
3072
3073
#ifdef MOZ_DUMP_PAINTING
3074
#include <stdio.h>
3075
3076
static bool gDumpEventList = false;
3077
3078
// nsLayoutUtils::PaintFrame() can call itself recursively, so rather than
3079
// maintaining a single paint count, we need a stack.
3080
StaticAutoPtr<nsTArray<int>> gPaintCountStack;
3081
3082
struct AutoNestedPaintCount {
3083
  AutoNestedPaintCount() {
3084
    gPaintCountStack->AppendElement(0);
3085
  }
3086
  ~AutoNestedPaintCount() {
3087
    gPaintCountStack->RemoveLastElement();
3088
  }
3089
};
3090
3091
#endif
3092
3093
nsIFrame*
3094
nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt, uint32_t aFlags)
3095
0
{
3096
0
  AUTO_PROFILER_LABEL("nsLayoutUtils::GetFrameForPoint", LAYOUT);
3097
0
3098
0
  nsresult rv;
3099
0
  AutoTArray<nsIFrame*,8> outFrames;
3100
0
  rv = GetFramesForArea(aFrame, nsRect(aPt, nsSize(1, 1)), outFrames, aFlags);
3101
0
  NS_ENSURE_SUCCESS(rv, nullptr);
3102
0
  return outFrames.Length() ? outFrames.ElementAt(0) : nullptr;
3103
0
}
3104
3105
nsresult
3106
nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
3107
                                nsTArray<nsIFrame*> &aOutFrames,
3108
                                uint32_t aFlags)
3109
0
{
3110
0
  AUTO_PROFILER_LABEL("nsLayoutUtils::GetFramesForArea", LAYOUT);
3111
0
3112
0
  nsDisplayListBuilder builder(aFrame,
3113
0
                               nsDisplayListBuilderMode::EVENT_DELIVERY,
3114
0
                               false);
3115
0
  builder.BeginFrame();
3116
0
  nsDisplayList list;
3117
0
3118
0
  if (aFlags & IGNORE_PAINT_SUPPRESSION) {
3119
0
    builder.IgnorePaintSuppression();
3120
0
  }
3121
0
3122
0
  if (aFlags & IGNORE_ROOT_SCROLL_FRAME) {
3123
0
    nsIFrame* rootScrollFrame = aFrame->PresShell()->GetRootScrollFrame();
3124
0
    if (rootScrollFrame) {
3125
0
      builder.SetIgnoreScrollFrame(rootScrollFrame);
3126
0
    }
3127
0
  }
3128
0
  if (aFlags & IGNORE_CROSS_DOC) {
3129
0
    builder.SetDescendIntoSubdocuments(false);
3130
0
  }
3131
0
3132
0
  builder.SetHitTestIsForVisibility(aFlags & ONLY_VISIBLE);
3133
0
3134
0
  builder.EnterPresShell(aFrame);
3135
0
3136
0
  builder.SetVisibleRect(aRect);
3137
0
  builder.SetDirtyRect(aRect);
3138
0
3139
0
  aFrame->BuildDisplayListForStackingContext(&builder, &list);
3140
0
  builder.LeavePresShell(aFrame, nullptr);
3141
0
3142
#ifdef MOZ_DUMP_PAINTING
3143
  if (gDumpEventList) {
3144
    fprintf_stderr(stderr, "Event handling --- (%d,%d):\n", aRect.x, aRect.y);
3145
3146
    std::stringstream ss;
3147
    nsFrame::PrintDisplayList(&builder, list, ss);
3148
    print_stderr(ss);
3149
  }
3150
#endif
3151
3152
0
  nsDisplayItem::HitTestState hitTestState;
3153
0
  list.HitTest(&builder, aRect, &hitTestState, &aOutFrames);
3154
0
  list.DeleteAll(&builder);
3155
0
  builder.EndFrame();
3156
0
  return NS_OK;
3157
0
}
3158
3159
// aScrollFrameAsScrollable must be non-nullptr and queryable to an nsIFrame
3160
FrameMetrics
3161
0
nsLayoutUtils::CalculateBasicFrameMetrics(nsIScrollableFrame* aScrollFrame) {
3162
0
  nsIFrame* frame = do_QueryFrame(aScrollFrame);
3163
0
  MOZ_ASSERT(frame);
3164
0
3165
0
  // Calculate the metrics necessary for calculating the displayport.
3166
0
  // This code has a lot in common with the code in ComputeFrameMetrics();
3167
0
  // we may want to refactor this at some point.
3168
0
  FrameMetrics metrics;
3169
0
  nsPresContext* presContext = frame->PresContext();
3170
0
  nsIPresShell* presShell = presContext->PresShell();
3171
0
  CSSToLayoutDeviceScale deviceScale = presContext->CSSToDevPixelScale();
3172
0
  float resolution = 1.0f;
3173
0
  if (frame == presShell->GetRootScrollFrame()) {
3174
0
    // Only the root scrollable frame for a given presShell should pick up
3175
0
    // the presShell's resolution. All the other frames are 1.0.
3176
0
    resolution = presShell->GetResolution();
3177
0
  }
3178
0
  // Note: unlike in ComputeFrameMetrics(), we don't know the full cumulative
3179
0
  // resolution including FrameMetrics::mExtraResolution, because layout hasn't
3180
0
  // chosen a resolution to paint at yet. However, the display port calculation
3181
0
  // divides out mExtraResolution anyways, so we get the correct result by
3182
0
  // setting the mCumulativeResolution to everything except the extra resolution
3183
0
  // and leaving mExtraResolution at 1.
3184
0
  LayoutDeviceToLayerScale2D cumulativeResolution(
3185
0
      presShell->GetCumulativeResolution()
3186
0
    * nsLayoutUtils::GetTransformToAncestorScale(frame));
3187
0
3188
0
  LayerToParentLayerScale layerToParentLayerScale(1.0f);
3189
0
  metrics.SetDevPixelsPerCSSPixel(deviceScale);
3190
0
  metrics.SetPresShellResolution(resolution);
3191
0
  metrics.SetCumulativeResolution(cumulativeResolution);
3192
0
  metrics.SetZoom(deviceScale * cumulativeResolution * layerToParentLayerScale);
3193
0
3194
0
  // Only the size of the composition bounds is relevant to the
3195
0
  // displayport calculation, not its origin.
3196
0
  nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(frame);
3197
0
  LayoutDeviceToParentLayerScale2D compBoundsScale;
3198
0
  if (frame == presShell->GetRootScrollFrame() && presContext->IsRootContentDocument()) {
3199
0
    if (presContext->GetParentPresContext()) {
3200
0
      float res = presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
3201
0
      compBoundsScale = LayoutDeviceToParentLayerScale2D(
3202
0
          LayoutDeviceToParentLayerScale(res));
3203
0
    }
3204
0
  } else {
3205
0
    compBoundsScale = cumulativeResolution * layerToParentLayerScale;
3206
0
  }
3207
0
  metrics.SetCompositionBounds(
3208
0
      LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0, 0), compositionSize),
3209
0
                                       presContext->AppUnitsPerDevPixel())
3210
0
      * compBoundsScale);
3211
0
3212
0
  metrics.SetRootCompositionSize(
3213
0
      nsLayoutUtils::CalculateRootCompositionSize(frame, false, metrics));
3214
0
3215
0
  metrics.SetScrollOffset(CSSPoint::FromAppUnits(
3216
0
      aScrollFrame->GetScrollPosition()));
3217
0
3218
0
  metrics.SetScrollableRect(CSSRect::FromAppUnits(
3219
0
      nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrame, nullptr)));
3220
0
3221
0
  return metrics;
3222
0
}
3223
3224
bool
3225
nsLayoutUtils::CalculateAndSetDisplayPortMargins(nsIScrollableFrame* aScrollFrame,
3226
0
                                                 RepaintMode aRepaintMode) {
3227
0
  nsIFrame* frame = do_QueryFrame(aScrollFrame);
3228
0
  MOZ_ASSERT(frame);
3229
0
  nsIContent* content = frame->GetContent();
3230
0
  MOZ_ASSERT(content);
3231
0
3232
0
  FrameMetrics metrics = CalculateBasicFrameMetrics(aScrollFrame);
3233
0
  ScreenMargin displayportMargins = apz::CalculatePendingDisplayPort(
3234
0
      metrics, ParentLayerPoint(0.0f, 0.0f));
3235
0
  nsIPresShell* presShell = frame->PresContext()->GetPresShell();
3236
0
  return nsLayoutUtils::SetDisplayPortMargins(
3237
0
      content, presShell, displayportMargins, 0, aRepaintMode);
3238
0
}
3239
3240
bool
3241
nsLayoutUtils::MaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
3242
                                      nsIFrame* aScrollFrame,
3243
                                      RepaintMode aRepaintMode)
3244
0
{
3245
0
  nsIContent* content = aScrollFrame->GetContent();
3246
0
  nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame);
3247
0
  if (!content || !scrollableFrame) {
3248
0
    return false;
3249
0
  }
3250
0
3251
0
  bool haveDisplayPort = HasDisplayPort(content);
3252
0
3253
0
  // We perform an optimization where we ensure that at least one
3254
0
  // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.
3255
0
  // If that's not the case yet, and we are async-scrollable, we will get a
3256
0
  // displayport.
3257
0
  if (aBuilder.IsPaintingToWindow() &&
3258
0
      nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) &&
3259
0
      !aBuilder.HaveScrollableDisplayPort() &&
3260
0
      scrollableFrame->WantAsyncScroll()) {
3261
0
3262
0
    // If we don't already have a displayport, calculate and set one.
3263
0
    if (!haveDisplayPort) {
3264
0
      CalculateAndSetDisplayPortMargins(scrollableFrame, aRepaintMode);
3265
#ifdef DEBUG
3266
      haveDisplayPort = HasDisplayPort(content);
3267
      MOZ_ASSERT(haveDisplayPort, "should have a displayport after having just set it");
3268
#endif
3269
    }
3270
0
3271
0
    // Record that the we now have a scrollable display port.
3272
0
    aBuilder.SetHaveScrollableDisplayPort();
3273
0
    return true;
3274
0
  }
3275
0
  return false;
3276
0
}
3277
3278
nsIScrollableFrame*
3279
nsLayoutUtils::GetAsyncScrollableAncestorFrame(nsIFrame* aTarget)
3280
0
{
3281
0
  uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
3282
0
                 | nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE
3283
0
                 | nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT;
3284
0
  return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
3285
0
}
3286
3287
void
3288
nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(nsIFrame* aFrame,
3289
                                                                  RepaintMode aRepaintMode)
3290
0
{
3291
0
  nsIFrame* frame = aFrame;
3292
0
  while (frame) {
3293
0
    frame = nsLayoutUtils::GetCrossDocParentFrame(frame);
3294
0
    if (!frame) {
3295
0
      break;
3296
0
    }
3297
0
    nsIScrollableFrame* scrollAncestor = GetAsyncScrollableAncestorFrame(frame);
3298
0
    if (!scrollAncestor) {
3299
0
      break;
3300
0
    }
3301
0
    frame = do_QueryFrame(scrollAncestor);
3302
0
    MOZ_ASSERT(frame);
3303
0
    MOZ_ASSERT(scrollAncestor->WantAsyncScroll() ||
3304
0
      frame->PresShell()->GetRootScrollFrame() == frame);
3305
0
    if (nsLayoutUtils::AsyncPanZoomEnabled(frame) &&
3306
0
        !nsLayoutUtils::HasDisplayPort(frame->GetContent())) {
3307
0
      nsLayoutUtils::SetDisplayPortMargins(
3308
0
        frame->GetContent(), frame->PresShell(), ScreenMargin(), 0,
3309
0
        aRepaintMode);
3310
0
    }
3311
0
  }
3312
0
}
3313
3314
bool
3315
nsLayoutUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
3316
  nsIFrame* aFrame, nsDisplayListBuilder& aBuilder)
3317
0
{
3318
0
  nsIScrollableFrame* sf = do_QueryFrame(aFrame);
3319
0
  if (sf) {
3320
0
    if (MaybeCreateDisplayPort(aBuilder, aFrame, RepaintMode::Repaint)) {
3321
0
      return true;
3322
0
    }
3323
0
  }
3324
0
  if (aFrame->IsPlaceholderFrame()) {
3325
0
    nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(aFrame);
3326
0
    if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(
3327
0
          placeholder->GetOutOfFlowFrame(), aBuilder)) {
3328
0
      return true;
3329
0
    }
3330
0
  }
3331
0
  if (aFrame->IsSubDocumentFrame()) {
3332
0
    nsIPresShell* presShell =
3333
0
      static_cast<nsSubDocumentFrame*>(aFrame)->GetSubdocumentPresShellForPainting(0);
3334
0
    nsIFrame* root = presShell ? presShell->GetRootFrame() : nullptr;
3335
0
    if (root) {
3336
0
      if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(root, aBuilder)) {
3337
0
        return true;
3338
0
      }
3339
0
    }
3340
0
  }
3341
0
  if (aFrame->IsDeckFrame()) {
3342
0
    // only descend the visible card of a decks
3343
0
    nsIFrame* child = static_cast<nsDeckFrame*>(aFrame)->GetSelectedBox();
3344
0
    if (child) {
3345
0
      return MaybeCreateDisplayPortInFirstScrollFrameEncountered(child, aBuilder);
3346
0
    }
3347
0
  }
3348
0
3349
0
  for (nsIFrame* child : aFrame->PrincipalChildList()) {
3350
0
    if (MaybeCreateDisplayPortInFirstScrollFrameEncountered(child, aBuilder)) {
3351
0
      return true;
3352
0
    }
3353
0
  }
3354
0
3355
0
  return false;
3356
0
}
3357
3358
void
3359
nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame)
3360
0
{
3361
0
  nsIFrame* frame = aFrame;
3362
0
  while (frame) {
3363
0
    frame = nsLayoutUtils::GetCrossDocParentFrame(frame);
3364
0
    if (!frame) {
3365
0
      break;
3366
0
    }
3367
0
    nsIScrollableFrame* scrollAncestor = GetAsyncScrollableAncestorFrame(frame);
3368
0
    if (!scrollAncestor) {
3369
0
      break;
3370
0
    }
3371
0
    frame = do_QueryFrame(scrollAncestor);
3372
0
    MOZ_ASSERT(frame);
3373
0
    if (!frame) {
3374
0
      break;
3375
0
    }
3376
0
    MOZ_ASSERT(scrollAncestor->WantAsyncScroll() ||
3377
0
      frame->PresShell()->GetRootScrollFrame() == frame);
3378
0
    if (nsLayoutUtils::HasDisplayPort(frame->GetContent())) {
3379
0
      scrollAncestor->TriggerDisplayPortExpiration();
3380
0
      // Stop after the first trigger. If it failed, there's no point in
3381
0
      // continuing because all the rest of the frames we encounter are going
3382
0
      // to be ancestors of |scrollAncestor| which will keep its displayport.
3383
0
      // If the trigger succeeded, we stop because when the trigger executes
3384
0
      // it will call this function again to trigger the next ancestor up the
3385
0
      // chain.
3386
0
      break;
3387
0
    }
3388
0
  }
3389
0
}
3390
3391
void
3392
nsLayoutUtils::AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
3393
                                       nsDisplayList& aList,
3394
                                       nsIFrame* aFrame,
3395
                                       const nsRect& aCanvasArea,
3396
                                       const nsRegion& aVisibleRegion,
3397
                                       nscolor aBackstop)
3398
0
{
3399
0
  LayoutFrameType frameType = aFrame->Type();
3400
0
  nsPresContext* presContext = aFrame->PresContext();
3401
0
  nsIPresShell* presShell = presContext->PresShell();
3402
0
3403
0
  // For the viewport frame in print preview/page layout we want to paint
3404
0
  // the grey background behind the page, not the canvas color.
3405
0
  if (frameType == LayoutFrameType::Viewport &&
3406
0
      nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
3407
0
    nsRect bounds = nsRect(aBuilder.ToReferenceFrame(aFrame),
3408
0
                           aFrame->GetSize());
3409
0
    nsDisplayListBuilder::AutoBuildingDisplayList
3410
0
      buildingDisplayList(&aBuilder, aFrame, bounds, bounds, false);
3411
0
    presShell->AddPrintPreviewBackgroundItem(aBuilder, aList, aFrame, bounds);
3412
0
  } else if (frameType != LayoutFrameType::Page) {
3413
0
    // For printing, this function is first called on an nsPageFrame, which
3414
0
    // creates a display list with a PageContent item. The PageContent item's
3415
0
    // paint function calls this function on the nsPageFrame's child which is
3416
0
    // an nsPageContentFrame. We only want to add the canvas background color
3417
0
    // item once, for the nsPageContentFrame.
3418
0
3419
0
    // Add the canvas background color to the bottom of the list. This
3420
0
    // happens after we've built the list so that AddCanvasBackgroundColorItem
3421
0
    // can monkey with the contents if necessary.
3422
0
    nsRect canvasArea = aVisibleRegion.GetBounds();
3423
0
    canvasArea.IntersectRect(aCanvasArea, canvasArea);
3424
0
    nsDisplayListBuilder::AutoBuildingDisplayList
3425
0
      buildingDisplayList(&aBuilder, aFrame, canvasArea, canvasArea, false);
3426
0
    presShell->AddCanvasBackgroundColorItem(
3427
0
      aBuilder, aList, aFrame, canvasArea, aBackstop);
3428
0
  }
3429
0
}
3430
3431
/**
3432
 * Returns a retained display list builder for frame |aFrame|. If there is no
3433
 * retained display list builder property set for the frame, and if the flag
3434
 * |aRetainingEnabled| is true, a new retained display list builder is created,
3435
 * stored as a property for the frame, and returned.
3436
 */
3437
static RetainedDisplayListBuilder*
3438
GetOrCreateRetainedDisplayListBuilder(nsIFrame* aFrame, bool aRetainingEnabled,
3439
                                      bool aBuildCaret)
3440
0
{
3441
0
  RetainedDisplayListBuilder* retainedBuilder =
3442
0
    aFrame->GetProperty(RetainedDisplayListBuilder::Cached());
3443
0
3444
0
  if (retainedBuilder) {
3445
0
    return retainedBuilder;
3446
0
  }
3447
0
3448
0
  if (aRetainingEnabled) {
3449
0
    retainedBuilder =
3450
0
      new RetainedDisplayListBuilder(aFrame, nsDisplayListBuilderMode::PAINTING,
3451
0
                                     aBuildCaret);
3452
0
    aFrame->SetProperty(RetainedDisplayListBuilder::Cached(), retainedBuilder);
3453
0
  }
3454
0
3455
0
  return retainedBuilder;
3456
0
}
3457
3458
nsresult
3459
nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
3460
                          const nsRegion& aDirtyRegion, nscolor aBackstop,
3461
                          nsDisplayListBuilderMode aBuilderMode,
3462
                          PaintFrameFlags aFlags)
3463
0
{
3464
0
  AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame", GRAPHICS);
3465
0
  typedef RetainedDisplayListBuilder::PartialUpdateResult PartialUpdateResult;
3466
0
3467
#ifdef MOZ_DUMP_PAINTING
3468
  if (!gPaintCountStack) {
3469
    gPaintCountStack = new nsTArray<int>();
3470
    ClearOnShutdown(&gPaintCountStack);
3471
3472
    gPaintCountStack->AppendElement(0);
3473
  }
3474
  ++gPaintCountStack->LastElement();
3475
  AutoNestedPaintCount nestedPaintCount;
3476
#endif
3477
3478
0
  if (aFlags & PaintFrameFlags::PAINT_WIDGET_LAYERS) {
3479
0
    nsView* view = aFrame->GetView();
3480
0
    if (!(view && view->GetWidget() && GetDisplayRootFrame(aFrame) == aFrame)) {
3481
0
      aFlags &= ~PaintFrameFlags::PAINT_WIDGET_LAYERS;
3482
0
      NS_ASSERTION(aRenderingContext, "need a rendering context");
3483
0
    }
3484
0
  }
3485
0
3486
0
  nsPresContext* presContext = aFrame->PresContext();
3487
0
  nsIPresShell* presShell = presContext->PresShell();
3488
0
  nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
3489
0
  if (!rootPresContext) {
3490
0
    return NS_OK;
3491
0
  }
3492
0
3493
0
  TimeStamp startBuildDisplayList = TimeStamp::Now();
3494
0
3495
0
  const bool buildCaret = !(aFlags & PaintFrameFlags::PAINT_HIDE_CARET);
3496
0
  const bool isForPainting = (aFlags & PaintFrameFlags::PAINT_WIDGET_LAYERS) &&
3497
0
    aBuilderMode == nsDisplayListBuilderMode::PAINTING;
3498
0
3499
0
  // Only allow retaining for painting when preffed on, and for root frames (since
3500
0
  // the modified frame tracking is per-root-frame).
3501
0
  const bool retainingEnabled =
3502
0
    isForPainting && AreRetainedDisplayListsEnabled() && !aFrame->GetParent();
3503
0
3504
0
  RetainedDisplayListBuilder* retainedBuilder =
3505
0
    GetOrCreateRetainedDisplayListBuilder(aFrame, retainingEnabled, buildCaret);
3506
0
3507
0
  // Only use the retained display list builder if the retaining is currently
3508
0
  // enabled. This check is needed because it is possible that the pref has been
3509
0
  // disabled after creating the retained display list builder.
3510
0
  const bool useRetainedBuilder = retainedBuilder && retainingEnabled;
3511
0
3512
0
  Maybe<nsDisplayListBuilder> nonRetainedBuilder;
3513
0
  Maybe<nsDisplayList> nonRetainedList;
3514
0
  nsDisplayListBuilder* builderPtr = nullptr;
3515
0
  nsDisplayList* listPtr = nullptr;
3516
0
3517
0
  if (useRetainedBuilder) {
3518
0
    builderPtr = retainedBuilder->Builder();
3519
0
    listPtr = retainedBuilder->List();
3520
0
  } else {
3521
0
    nonRetainedBuilder.emplace(aFrame, aBuilderMode, buildCaret);
3522
0
    builderPtr = nonRetainedBuilder.ptr();
3523
0
    nonRetainedList.emplace();
3524
0
    listPtr = nonRetainedList.ptr();
3525
0
  }
3526
0
3527
0
  // Retained builder exists, but display list retaining is disabled.
3528
0
  if (!useRetainedBuilder && retainedBuilder) {
3529
0
    // Clear the modified frames lists and frame properties.
3530
0
    retainedBuilder->ClearFramesWithProps();
3531
0
3532
0
    // Clear the retained display list.
3533
0
    retainedBuilder->List()->DeleteAll(retainedBuilder->Builder());
3534
0
  }
3535
0
3536
0
  nsDisplayListBuilder& builder = *builderPtr;
3537
0
  nsDisplayList& list = *listPtr;
3538
0
3539
0
  builder.BeginFrame();
3540
0
3541
0
  if (aFlags & PaintFrameFlags::PAINT_IN_TRANSFORM) {
3542
0
    builder.SetInTransform(true);
3543
0
  }
3544
0
  if (aFlags & PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES) {
3545
0
    builder.SetSyncDecodeImages(true);
3546
0
  }
3547
0
  if (aFlags & (PaintFrameFlags::PAINT_WIDGET_LAYERS |
3548
0
                PaintFrameFlags::PAINT_TO_WINDOW)) {
3549
0
    builder.SetPaintingToWindow(true);
3550
0
  }
3551
0
  if (aFlags & PaintFrameFlags::PAINT_IGNORE_SUPPRESSION) {
3552
0
    builder.IgnorePaintSuppression();
3553
0
  }
3554
0
3555
0
  nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
3556
0
  if (rootScrollFrame && !aFrame->GetParent()) {
3557
0
    nsIScrollableFrame* rootScrollableFrame = presShell->GetRootScrollFrameAsScrollable();
3558
0
    MOZ_ASSERT(rootScrollableFrame);
3559
0
    nsRect displayPortBase = aFrame->GetVisualOverflowRectRelativeToSelf();
3560
0
    nsRect temp = displayPortBase;
3561
0
    Unused << rootScrollableFrame->DecideScrollableLayer(&builder, &displayPortBase, &temp,
3562
0
                /* aSetBase = */ true);
3563
0
  }
3564
0
3565
0
  nsRegion visibleRegion;
3566
0
  if (aFlags & PaintFrameFlags::PAINT_WIDGET_LAYERS) {
3567
0
    // This layer tree will be reused, so we'll need to calculate it
3568
0
    // for the whole "visible" area of the window
3569
0
    //
3570
0
    // |ignoreViewportScrolling| and |usingDisplayPort| are persistent
3571
0
    // document-rendering state.  We rely on PresShell to flush
3572
0
    // retained layers as needed when that persistent state changes.
3573
0
    visibleRegion = aFrame->GetVisualOverflowRectRelativeToSelf();
3574
0
  } else {
3575
0
    visibleRegion = aDirtyRegion;
3576
0
  }
3577
0
3578
0
  // If the root has embedded plugins, flag the builder so we know we'll need
3579
0
  // to update plugin geometry after painting.
3580
0
  if ((aFlags & PaintFrameFlags::PAINT_WIDGET_LAYERS) &&
3581
0
      !(aFlags & PaintFrameFlags::PAINT_DOCUMENT_RELATIVE) &&
3582
0
      rootPresContext->NeedToComputePluginGeometryUpdates()) {
3583
0
    builder.SetWillComputePluginGeometry(true);
3584
0
3585
0
    // Disable partial updates for this paint as the list we're about to
3586
0
    // build has plugin-specific differences that won't trigger invalidations.
3587
0
    builder.SetDisablePartialUpdates(true);
3588
0
  }
3589
0
3590
0
  nsRect canvasArea(nsPoint(0, 0), aFrame->GetSize());
3591
0
  bool ignoreViewportScrolling =
3592
0
    aFrame->GetParent() ? false : presShell->IgnoringViewportScrolling();
3593
0
  if (ignoreViewportScrolling && rootScrollFrame) {
3594
0
    nsIScrollableFrame* rootScrollableFrame =
3595
0
      presShell->GetRootScrollFrameAsScrollable();
3596
0
    if (aFlags & PaintFrameFlags::PAINT_DOCUMENT_RELATIVE) {
3597
0
      // Make visibleRegion and aRenderingContext relative to the
3598
0
      // scrolled frame instead of the root frame.
3599
0
      nsPoint pos = rootScrollableFrame->GetScrollPosition();
3600
0
      visibleRegion.MoveBy(-pos);
3601
0
      if (aRenderingContext) {
3602
0
        gfxPoint devPixelOffset =
3603
0
          nsLayoutUtils::PointToGfxPoint(pos,
3604
0
                                         presContext->AppUnitsPerDevPixel());
3605
0
        aRenderingContext->SetMatrixDouble(
3606
0
          aRenderingContext->CurrentMatrixDouble().PreTranslate(devPixelOffset));
3607
0
      }
3608
0
    }
3609
0
    builder.SetIgnoreScrollFrame(rootScrollFrame);
3610
0
3611
0
    nsCanvasFrame* canvasFrame =
3612
0
      do_QueryFrame(rootScrollableFrame->GetScrolledFrame());
3613
0
    if (canvasFrame) {
3614
0
      // Use UnionRect here to ensure that areas where the scrollbars
3615
0
      // were are still filled with the background color.
3616
0
      canvasArea.UnionRect(canvasArea,
3617
0
        canvasFrame->CanvasArea() + builder.ToReferenceFrame(canvasFrame));
3618
0
    }
3619
0
  }
3620
0
3621
0
  builder.ClearHaveScrollableDisplayPort();
3622
0
  if (builder.IsPaintingToWindow()) {
3623
0
    MaybeCreateDisplayPortInFirstScrollFrameEncountered(aFrame, builder);
3624
0
  }
3625
0
3626
0
  nsRect visibleRect = visibleRegion.GetBounds();
3627
0
  PartialUpdateResult updateState = PartialUpdateResult::Failed;
3628
0
3629
0
  {
3630
0
    AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame:BuildDisplayList",
3631
0
                        GRAPHICS);
3632
0
    AUTO_PROFILER_TRACING("Paint", "DisplayList");
3633
0
3634
0
    PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::DisplayList);
3635
0
    TimeStamp dlStart = TimeStamp::Now();
3636
0
3637
0
    {
3638
0
      // If a scrollable container layer is created in nsDisplayList::PaintForFrame,
3639
0
      // it will be the scroll parent for display items that are built in the
3640
0
      // BuildDisplayListForStackingContext call below. We need to set the scroll
3641
0
      // parent on the display list builder while we build those items, so that they
3642
0
      // can pick up their scroll parent's id.
3643
0
      ViewID id = FrameMetrics::NULL_SCROLL_ID;
3644
0
      if (ignoreViewportScrolling && presContext->IsRootContentDocument()) {
3645
0
        if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
3646
0
          if (nsIContent* content = rootScrollFrame->GetContent()) {
3647
0
            id = nsLayoutUtils::FindOrCreateIDFor(content);
3648
0
          }
3649
0
        }
3650
0
      }
3651
0
      else if (presShell->GetDocument() && presShell->GetDocument()->IsRootDisplayDocument()
3652
0
          && !presShell->GetRootScrollFrame()) {
3653
0
        // In cases where the root document is a XUL document, we want to take
3654
0
        // the ViewID from the root element, as that will be the ViewID of the
3655
0
        // root APZC in the tree. Skip doing this in cases where we know
3656
0
        // nsGfxScrollFrame::BuilDisplayList will do it instead.
3657
0
        if (dom::Element* element = presShell->GetDocument()->GetDocumentElement()) {
3658
0
          id = nsLayoutUtils::FindOrCreateIDFor(element);
3659
0
        }
3660
0
      }
3661
0
3662
0
      nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
3663
0
3664
0
      builder.SetVisibleRect(visibleRect);
3665
0
      builder.SetIsBuilding(true);
3666
0
      builder.SetAncestorHasApzAwareEventHandler(
3667
0
          gfxPlatform::AsyncPanZoomEnabled() &&
3668
0
          nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell));
3669
0
3670
0
      DisplayListChecker beforeMergeChecker;
3671
0
      DisplayListChecker toBeMergedChecker;
3672
0
      DisplayListChecker afterMergeChecker;
3673
0
3674
0
      // Attempt to do a partial build and merge into the existing list.
3675
0
      // This calls BuildDisplayListForStacking context on a subset of the
3676
0
      // viewport.
3677
0
      if (useRetainedBuilder) {
3678
0
        if (gfxPrefs::LayoutVerifyRetainDisplayList()) {
3679
0
          beforeMergeChecker.Set(&list, "BM");
3680
0
        }
3681
0
        updateState = retainedBuilder->AttemptPartialUpdate(
3682
0
          aBackstop, beforeMergeChecker ? &toBeMergedChecker : nullptr);
3683
0
        if ((updateState != PartialUpdateResult::Failed) && beforeMergeChecker) {
3684
0
          afterMergeChecker.Set(&list, "AM");
3685
0
        }
3686
0
      }
3687
0
3688
0
      if ((updateState != PartialUpdateResult::Failed) &&
3689
0
          (gfxPrefs::LayoutDisplayListBuildTwice() || afterMergeChecker)) {
3690
0
        updateState = PartialUpdateResult::Failed;
3691
0
        if (gfxPrefs::LayersDrawFPS()) {
3692
0
          if (RefPtr<LayerManager> lm = builder.GetWidgetLayerManager()) {
3693
0
            if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm)) {
3694
0
              pt->dl2Ms() = (TimeStamp::Now() - dlStart).ToMilliseconds();
3695
0
            }
3696
0
          }
3697
0
        }
3698
0
        dlStart = TimeStamp::Now();
3699
0
      }
3700
0
3701
0
      if (updateState == PartialUpdateResult::Failed) {
3702
0
        list.DeleteAll(&builder);
3703
0
3704
0
        builder.ClearRetainedWindowRegions();
3705
0
        builder.ClearWillChangeBudget();
3706
0
3707
0
        builder.EnterPresShell(aFrame);
3708
0
        builder.SetDirtyRect(visibleRect);
3709
0
        aFrame->BuildDisplayListForStackingContext(&builder, &list);
3710
0
        AddExtraBackgroundItems(
3711
0
          builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
3712
0
3713
0
        builder.LeavePresShell(aFrame, &list);
3714
0
        updateState = PartialUpdateResult::Updated;
3715
0
3716
0
        if (afterMergeChecker) {
3717
0
          DisplayListChecker nonRetainedChecker(&list, "NR");
3718
0
          std::stringstream ss;
3719
0
          ss << "**** Differences between retained-after-merged (AM) and "
3720
0
             << "non-retained (NR) display lists:";
3721
0
          if (!nonRetainedChecker.CompareList(afterMergeChecker, ss)) {
3722
0
            ss << "\n\n*** non-retained display items:";
3723
0
            nonRetainedChecker.Dump(ss);
3724
0
            ss << "\n\n*** before-merge retained display items:";
3725
0
            beforeMergeChecker.Dump(ss);
3726
0
            ss << "\n\n*** to-be-merged retained display items:";
3727
0
            toBeMergedChecker.Dump(ss);
3728
0
            ss << "\n\n*** after-merge retained display items:";
3729
0
            afterMergeChecker.Dump(ss);
3730
0
            fprintf(stderr, "%s\n\n", ss.str().c_str());
3731
#ifdef DEBUG_FRAME_DUMP
3732
            fprintf(stderr, "*** Frame tree:\n");
3733
            aFrame->DumpFrameTree();
3734
#endif
3735
          }
3736
0
        }
3737
0
      }
3738
0
    }
3739
0
3740
0
    builder.SetIsBuilding(false);
3741
0
    builder.IncrementPresShellPaintCount(presShell);
3742
0
3743
0
    if (gfxPrefs::LayersDrawFPS()) {
3744
0
      if (RefPtr<LayerManager> lm = builder.GetWidgetLayerManager()) {
3745
0
        if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm)) {
3746
0
          pt->dlMs() = (TimeStamp::Now() - dlStart).ToMilliseconds();
3747
0
        }
3748
0
      }
3749
0
    }
3750
0
  }
3751
0
3752
0
  MOZ_ASSERT(updateState != PartialUpdateResult::Failed);
3753
0
  builder.Check();
3754
0
3755
0
  Telemetry::AccumulateTimeDelta(Telemetry::PAINT_BUILD_DISPLAYLIST_TIME,
3756
0
                                 startBuildDisplayList);
3757
0
3758
0
  bool consoleNeedsDisplayList = gfxUtils::DumpDisplayList() || gfxEnv::DumpPaint();
3759
#ifdef MOZ_DUMP_PAINTING
3760
  FILE* savedDumpFile = gfxUtils::sDumpPaintFile;
3761
#endif
3762
3763
0
  UniquePtr<std::stringstream> ss;
3764
0
  if (consoleNeedsDisplayList) {
3765
0
    ss = MakeUnique<std::stringstream>();
3766
#ifdef MOZ_DUMP_PAINTING
3767
    if (gfxEnv::DumpPaintToFile()) {
3768
      nsCString string("dump-");
3769
      // Include the process ID in the dump file name, to make sure that in an
3770
      // e10s setup different processes don't clobber each other's dump files.
3771
      string.AppendInt(getpid());
3772
      for (int paintCount : *gPaintCountStack) {
3773
        string.AppendLiteral("-");
3774
        string.AppendInt(paintCount);
3775
      }
3776
      string.AppendLiteral(".html");
3777
      gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w");
3778
    } else {
3779
      gfxUtils::sDumpPaintFile = stderr;
3780
    }
3781
    if (gfxEnv::DumpPaintToFile()) {
3782
      *ss << "<html><head><script>\n"
3783
             "var array = {};\n"
3784
             "function ViewImage(index) { \n"
3785
             "  var image = document.getElementById(index);\n"
3786
             "  if (image.src) {\n"
3787
             "    image.removeAttribute('src');\n"
3788
             "  } else {\n"
3789
             "    image.src = array[index];\n"
3790
             "  }\n"
3791
             "}</script></head><body>";
3792
    }
3793
#endif
3794
    *ss << nsPrintfCString("Painting --- before optimization (dirty %d,%d,%d,%d):\n",
3795
0
            visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height).get();
3796
0
    nsFrame::PrintDisplayList(&builder, list, *ss, gfxEnv::DumpPaintToFile());
3797
0
3798
0
    if (gfxEnv::DumpPaint() || gfxEnv::DumpPaintItems()) {
3799
0
      // Flush stream now to avoid reordering dump output relative to
3800
0
      // messages dumped by PaintRoot below.
3801
0
      fprint_stderr(gfxUtils::sDumpPaintFile, *ss);
3802
0
      ss = MakeUnique<std::stringstream>();
3803
0
    }
3804
0
  }
3805
0
3806
0
  uint32_t flags = nsDisplayList::PAINT_DEFAULT;
3807
0
  if (aFlags & PaintFrameFlags::PAINT_WIDGET_LAYERS) {
3808
0
    flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS;
3809
0
    if (!(aFlags & PaintFrameFlags::PAINT_DOCUMENT_RELATIVE)) {
3810
0
      nsIWidget *widget = aFrame->GetNearestWidget();
3811
0
      if (widget) {
3812
0
        // If we're finished building display list items for painting of the outermost
3813
0
        // pres shell, notify the widget about any toolbars we've encountered.
3814
0
        widget->UpdateThemeGeometries(builder.GetThemeGeometries());
3815
0
      }
3816
0
    }
3817
0
  }
3818
0
  if (aFlags & PaintFrameFlags::PAINT_EXISTING_TRANSACTION) {
3819
0
    flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
3820
0
  }
3821
0
  if (aFlags & PaintFrameFlags::PAINT_NO_COMPOSITE) {
3822
0
    flags |= nsDisplayList::PAINT_NO_COMPOSITE;
3823
0
  }
3824
0
  if (aFlags & PaintFrameFlags::PAINT_COMPRESSED) {
3825
0
    flags |= nsDisplayList::PAINT_COMPRESSED;
3826
0
  }
3827
0
  if (updateState == PartialUpdateResult::NoChange &&
3828
0
      !aRenderingContext) {
3829
0
    flags |= nsDisplayList::PAINT_IDENTICAL_DISPLAY_LIST;
3830
0
  }
3831
0
3832
0
  TimeStamp paintStart = TimeStamp::Now();
3833
0
  RefPtr<LayerManager> layerManager
3834
0
    = list.PaintRoot(&builder, aRenderingContext, flags);
3835
0
  Telemetry::AccumulateTimeDelta(Telemetry::PAINT_RASTERIZE_TIME,
3836
0
                                 paintStart);
3837
0
3838
0
  builder.Check();
3839
0
3840
0
  if (gfxPrefs::GfxLoggingPaintedPixelCountEnabled()) {
3841
0
    TimeStamp now = TimeStamp::Now();
3842
0
    float rasterizeTime = (now - paintStart).ToMilliseconds();
3843
0
    uint32_t pixelCount = layerManager->GetAndClearPaintedPixelCount();
3844
0
    static std::vector<std::pair<TimeStamp, uint32_t>> history;
3845
0
    if (pixelCount) {
3846
0
      history.push_back(std::make_pair(now, pixelCount));
3847
0
    }
3848
0
    uint32_t paintedInLastSecond = 0;
3849
0
    for (auto i = history.begin(); i != history.end(); i++) {
3850
0
      if ((now - i->first).ToMilliseconds() > 1000.0f) {
3851
0
        // more than 1000ms ago, don't count it
3852
0
        continue;
3853
0
      }
3854
0
      if (paintedInLastSecond == 0) {
3855
0
        // This is the first one in the last 1000ms, so drop everything earlier
3856
0
        history.erase(history.begin(), i);
3857
0
        i = history.begin();
3858
0
      }
3859
0
      paintedInLastSecond += i->second;
3860
0
      MOZ_ASSERT(paintedInLastSecond); // all historical pixel counts are > 0
3861
0
    }
3862
0
    printf_stderr("Painted %u pixels in %fms (%u in the last 1000ms)\n",
3863
0
        pixelCount, rasterizeTime, paintedInLastSecond);
3864
0
  }
3865
0
3866
0
  if (consoleNeedsDisplayList) {
3867
0
    *ss << "Painting --- after optimization:\n";
3868
0
    nsFrame::PrintDisplayList(&builder, list, *ss, gfxEnv::DumpPaintToFile());
3869
0
3870
0
    *ss << "Painting --- layer tree:\n";
3871
0
    if (layerManager) {
3872
0
      FrameLayerBuilder::DumpRetainedLayerTree(layerManager, *ss,
3873
0
                                               gfxEnv::DumpPaintToFile());
3874
0
    }
3875
0
3876
0
    fprint_stderr(gfxUtils::sDumpPaintFile, *ss);
3877
0
3878
#ifdef MOZ_DUMP_PAINTING
3879
    if (gfxEnv::DumpPaintToFile()) {
3880
      *ss << "</body></html>";
3881
    }
3882
    if (gfxEnv::DumpPaintToFile()) {
3883
      fclose(gfxUtils::sDumpPaintFile);
3884
    }
3885
    gfxUtils::sDumpPaintFile = savedDumpFile;
3886
#endif
3887
3888
0
    std::stringstream lsStream;
3889
0
    nsFrame::PrintDisplayList(&builder, list, lsStream);
3890
0
    if (layerManager->GetRoot()) {
3891
0
      layerManager->GetRoot()->SetDisplayListLog(lsStream.str().c_str());
3892
0
    }
3893
0
  }
3894
0
3895
#ifdef MOZ_DUMP_PAINTING
3896
  if (gfxPrefs::DumpClientLayers()) {
3897
    std::stringstream ss;
3898
    FrameLayerBuilder::DumpRetainedLayerTree(layerManager, ss, false);
3899
    print_stderr(ss);
3900
  }
3901
#endif
3902
3903
0
  // Update the widget's opaque region information. This sets
3904
0
  // glass boundaries on Windows. Also set up the window dragging region
3905
0
  // and plugin clip regions and bounds.
3906
0
  if ((aFlags & PaintFrameFlags::PAINT_WIDGET_LAYERS) &&
3907
0
      !(aFlags & PaintFrameFlags::PAINT_DOCUMENT_RELATIVE)) {
3908
0
    nsIWidget *widget = aFrame->GetNearestWidget();
3909
0
    if (widget) {
3910
0
      nsRegion opaqueRegion;
3911
0
      opaqueRegion.And(builder.GetWindowExcludeGlassRegion(), builder.GetWindowOpaqueRegion());
3912
0
      widget->UpdateOpaqueRegion(
3913
0
        LayoutDeviceIntRegion::FromUnknownRegion(
3914
0
          opaqueRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel())));
3915
0
3916
0
      widget->UpdateWindowDraggingRegion(builder.GetWindowDraggingRegion());
3917
0
    }
3918
0
  }
3919
0
3920
0
  if (builder.WillComputePluginGeometry()) {
3921
0
    // For single process compute and apply plugin geometry updates to plugin
3922
0
    // windows, then request composition. For content processes skip eveything
3923
0
    // except requesting composition. Geometry updates were calculated and
3924
0
    // shipped to the chrome process in nsDisplayList when the layer
3925
0
    // transaction completed.
3926
0
    if (XRE_IsParentProcess()) {
3927
0
      rootPresContext->ComputePluginGeometryUpdates(aFrame, &builder, &list);
3928
0
      // We're not going to get a WillPaintWindow event here if we didn't do
3929
0
      // widget invalidation, so just apply the plugin geometry update here
3930
0
      // instead. We could instead have the compositor send back an equivalent
3931
0
      // to WillPaintWindow, but it should be close enough to now not to matter.
3932
0
      if (layerManager && !layerManager->NeedsWidgetInvalidation()) {
3933
0
        rootPresContext->ApplyPluginGeometryUpdates();
3934
0
      }
3935
0
    }
3936
0
3937
0
    // We told the compositor thread not to composite when it received the
3938
0
    // transaction because we wanted to update plugins first. Schedule the
3939
0
    // composite now.
3940
0
    if (layerManager) {
3941
0
      layerManager->ScheduleComposite();
3942
0
    }
3943
0
3944
0
    // Disable partial updates for the following paint as well, as we now have
3945
0
    // a plugin-specific display list.
3946
0
    builder.SetDisablePartialUpdates(true);
3947
0
  }
3948
0
3949
0
  builder.Check();
3950
0
3951
0
  {
3952
0
    AUTO_PROFILER_TRACING("Paint", "DisplayListResources");
3953
0
3954
0
    // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
3955
0
    if (!useRetainedBuilder) {
3956
0
      list.DeleteAll(&builder);
3957
0
    }
3958
0
3959
0
    builder.EndFrame();
3960
0
  }
3961
0
  return NS_OK;
3962
0
}
3963
3964
/**
3965
 * Uses a binary search for find where the cursor falls in the line of text
3966
 * It also keeps track of the part of the string that has already been measured
3967
 * so it doesn't have to keep measuring the same text over and over
3968
 *
3969
 * @param "aBaseWidth" contains the width in twips of the portion
3970
 * of the text that has already been measured, and aBaseInx contains
3971
 * the index of the text that has already been measured.
3972
 *
3973
 * @param aTextWidth returns the (in twips) the length of the text that falls
3974
 * before the cursor aIndex contains the index of the text where the cursor falls
3975
 */
3976
bool
3977
nsLayoutUtils::BinarySearchForPosition(DrawTarget* aDrawTarget,
3978
                                       nsFontMetrics& aFontMetrics,
3979
                        const char16_t* aText,
3980
                        int32_t    aBaseWidth,
3981
                        int32_t    aBaseInx,
3982
                        int32_t    aStartInx,
3983
                        int32_t    aEndInx,
3984
                        int32_t    aCursorPos,
3985
                        int32_t&   aIndex,
3986
                        int32_t&   aTextWidth)
3987
0
{
3988
0
  int32_t range = aEndInx - aStartInx;
3989
0
  if ((range == 1) || (range == 2 && NS_IS_HIGH_SURROGATE(aText[aStartInx]))) {
3990
0
    aIndex   = aStartInx + aBaseInx;
3991
0
    aTextWidth = nsLayoutUtils::AppUnitWidthOfString(aText, aIndex,
3992
0
                                                     aFontMetrics, aDrawTarget);
3993
0
    return true;
3994
0
  }
3995
0
3996
0
  int32_t inx = aStartInx + (range / 2);
3997
0
3998
0
  // Make sure we don't leave a dangling low surrogate
3999
0
  if (NS_IS_HIGH_SURROGATE(aText[inx-1]))
4000
0
    inx++;
4001
0
4002
0
  int32_t textWidth = nsLayoutUtils::AppUnitWidthOfString(aText, inx,
4003
0
                                                          aFontMetrics,
4004
0
                                                          aDrawTarget);
4005
0
4006
0
  int32_t fullWidth = aBaseWidth + textWidth;
4007
0
  if (fullWidth == aCursorPos) {
4008
0
    aTextWidth = textWidth;
4009
0
    aIndex = inx;
4010
0
    return true;
4011
0
  } else if (aCursorPos < fullWidth) {
4012
0
    aTextWidth = aBaseWidth;
4013
0
    if (BinarySearchForPosition(aDrawTarget, aFontMetrics, aText, aBaseWidth,
4014
0
                                aBaseInx, aStartInx, inx, aCursorPos, aIndex,
4015
0
                                aTextWidth)) {
4016
0
      return true;
4017
0
    }
4018
0
  } else {
4019
0
    aTextWidth = fullWidth;
4020
0
    if (BinarySearchForPosition(aDrawTarget, aFontMetrics, aText, aBaseWidth,
4021
0
                                aBaseInx, inx, aEndInx, aCursorPos, aIndex,
4022
0
                                aTextWidth)) {
4023
0
      return true;
4024
0
    }
4025
0
  }
4026
0
  return false;
4027
0
}
4028
4029
void
4030
nsLayoutUtils::AddBoxesForFrame(nsIFrame* aFrame,
4031
                                nsLayoutUtils::BoxCallback* aCallback)
4032
0
{
4033
0
  nsAtom* pseudoType = aFrame->Style()->GetPseudo();
4034
0
4035
0
  if (pseudoType == nsCSSAnonBoxes::tableWrapper()) {
4036
0
    AddBoxesForFrame(aFrame->PrincipalChildList().FirstChild(), aCallback);
4037
0
    if (aCallback->mIncludeCaptionBoxForTable) {
4038
0
      nsIFrame* kid = aFrame->GetChildList(nsIFrame::kCaptionList).FirstChild();
4039
0
      if (kid) {
4040
0
        AddBoxesForFrame(kid, aCallback);
4041
0
      }
4042
0
    }
4043
0
  } else if (pseudoType == nsCSSAnonBoxes::mozBlockInsideInlineWrapper() ||
4044
0
             pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock() ||
4045
0
             pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock()) {
4046
0
    for (nsIFrame* kid : aFrame->PrincipalChildList()) {
4047
0
      AddBoxesForFrame(kid, aCallback);
4048
0
    }
4049
0
  } else {
4050
0
    aCallback->AddBox(aFrame);
4051
0
  }
4052
0
}
4053
4054
void
4055
nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback)
4056
0
{
4057
0
  while (aFrame) {
4058
0
    AddBoxesForFrame(aFrame, aCallback);
4059
0
    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
4060
0
  }
4061
0
}
4062
4063
nsIFrame*
4064
nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame)
4065
0
{
4066
0
  while (aFrame) {
4067
0
    nsAtom* pseudoType = aFrame->Style()->GetPseudo();
4068
0
4069
0
    if (pseudoType == nsCSSAnonBoxes::tableWrapper()) {
4070
0
      nsIFrame* f = GetFirstNonAnonymousFrame(aFrame->PrincipalChildList().FirstChild());
4071
0
      if (f) {
4072
0
        return f;
4073
0
      }
4074
0
      nsIFrame* kid = aFrame->GetChildList(nsIFrame::kCaptionList).FirstChild();
4075
0
      if (kid) {
4076
0
        f = GetFirstNonAnonymousFrame(kid);
4077
0
        if (f) {
4078
0
          return f;
4079
0
        }
4080
0
      }
4081
0
    } else if (pseudoType == nsCSSAnonBoxes::mozBlockInsideInlineWrapper() ||
4082
0
               pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock() ||
4083
0
               pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock()) {
4084
0
      for (nsIFrame* kid : aFrame->PrincipalChildList()) {
4085
0
        nsIFrame* f = GetFirstNonAnonymousFrame(kid);
4086
0
        if (f) {
4087
0
          return f;
4088
0
        }
4089
0
      }
4090
0
    } else {
4091
0
      return aFrame;
4092
0
    }
4093
0
4094
0
    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
4095
0
  }
4096
0
  return nullptr;
4097
0
}
4098
4099
struct BoxToRect : public nsLayoutUtils::BoxCallback {
4100
  const nsIFrame* mRelativeTo;
4101
  nsLayoutUtils::RectCallback* mCallback;
4102
  uint32_t mFlags;
4103
4104
  BoxToRect(const nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
4105
            uint32_t aFlags)
4106
0
    : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {}
4107
4108
0
  virtual void AddBox(nsIFrame* aFrame) override {
4109
0
    nsRect r;
4110
0
    nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r);
4111
0
    if (!outer) {
4112
0
      outer = aFrame;
4113
0
      switch (mFlags & nsLayoutUtils::RECTS_WHICH_BOX_MASK) {
4114
0
        case nsLayoutUtils::RECTS_USE_CONTENT_BOX:
4115
0
          r = aFrame->GetContentRectRelativeToSelf();
4116
0
          break;
4117
0
        case nsLayoutUtils::RECTS_USE_PADDING_BOX:
4118
0
          r = aFrame->GetPaddingRectRelativeToSelf();
4119
0
          break;
4120
0
        case nsLayoutUtils::RECTS_USE_MARGIN_BOX:
4121
0
          r = aFrame->GetMarginRectRelativeToSelf();
4122
0
          break;
4123
0
        default: // Use the border box
4124
0
          r = aFrame->GetRectRelativeToSelf();
4125
0
      }
4126
0
    }
4127
0
    if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) {
4128
0
      r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo);
4129
0
    } else {
4130
0
      r += outer->GetOffsetTo(mRelativeTo);
4131
0
    }
4132
0
    mCallback->AddRect(r);
4133
0
  }
4134
};
4135
4136
struct MOZ_RAII BoxToRectAndText : public BoxToRect {
4137
  Sequence<nsString>* mTextList;
4138
4139
  BoxToRectAndText(const nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
4140
                   Sequence<nsString>* aTextList, uint32_t aFlags)
4141
0
    : BoxToRect(aRelativeTo, aCallback, aFlags), mTextList(aTextList) {}
4142
4143
0
  static void AccumulateText(nsIFrame* aFrame, nsAString& aResult) {
4144
0
    MOZ_ASSERT(aFrame);
4145
0
4146
0
    // Get all the text in aFrame and child frames, while respecting
4147
0
    // the content offsets in each of the nsTextFrames.
4148
0
    if (aFrame->IsTextFrame()) {
4149
0
      nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
4150
0
4151
0
      nsIFrame::RenderedText renderedText = textFrame->GetRenderedText(
4152
0
        textFrame->GetContentOffset(),
4153
0
        textFrame->GetContentOffset() + textFrame->GetContentLength(),
4154
0
        nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
4155
0
        nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
4156
0
4157
0
      aResult.Append(renderedText.mString);
4158
0
    }
4159
0
4160
0
    for (nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
4161
0
         child;
4162
0
         child = child->GetNextSibling()) {
4163
0
      AccumulateText(child, aResult);
4164
0
    }
4165
0
  }
4166
4167
0
  virtual void AddBox(nsIFrame* aFrame) override {
4168
0
    BoxToRect::AddBox(aFrame);
4169
0
    if (mTextList) {
4170
0
      nsString* textForFrame = mTextList->AppendElement(fallible);
4171
0
      if (textForFrame) {
4172
0
        AccumulateText(aFrame, *textForFrame);
4173
0
      }
4174
0
    }
4175
0
  }
4176
};
4177
4178
void
4179
nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, const nsIFrame* aRelativeTo,
4180
                                 RectCallback* aCallback, uint32_t aFlags)
4181
0
{
4182
0
  BoxToRect converter(aRelativeTo, aCallback, aFlags);
4183
0
  GetAllInFlowBoxes(aFrame, &converter);
4184
0
}
4185
4186
void
4187
nsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame* aFrame,
4188
                                         const nsIFrame* aRelativeTo,
4189
                                         RectCallback* aCallback,
4190
                                         Sequence<nsString>* aTextList,
4191
                                         uint32_t aFlags)
4192
0
{
4193
0
  BoxToRectAndText converter(aRelativeTo, aCallback, aTextList, aFlags);
4194
0
  GetAllInFlowBoxes(aFrame, &converter);
4195
0
}
4196
4197
0
nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
4198
4199
0
void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) {
4200
0
  mResultRect.UnionRect(mResultRect, aRect);
4201
0
  if (!mSeenFirstRect) {
4202
0
    mSeenFirstRect = true;
4203
0
    mFirstRect = aRect;
4204
0
  }
4205
0
}
4206
4207
nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList* aList)
4208
  : mRectList(aList)
4209
0
{
4210
0
}
4211
4212
0
void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) {
4213
0
  RefPtr<DOMRect> rect = new DOMRect(mRectList);
4214
0
4215
0
  rect->SetLayoutRect(aRect);
4216
0
  mRectList->Append(rect);
4217
0
}
4218
4219
nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame)
4220
0
{
4221
0
  return aFrame->PresShell()->GetRootFrame();
4222
0
}
4223
4224
nsRect
4225
nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, const nsIFrame* aRelativeTo,
4226
0
                                      uint32_t aFlags) {
4227
0
  RectAccumulator accumulator;
4228
0
  GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags);
4229
0
  return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
4230
0
          : accumulator.mResultRect;
4231
0
}
4232
4233
nsRect
4234
nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
4235
                                       nsIFrame* aFrame,
4236
                                       uint32_t aFlags)
4237
0
{
4238
0
  const nsStyleText* textStyle = aFrame->StyleText();
4239
0
  if (!textStyle->HasTextShadow())
4240
0
    return aTextAndDecorationsRect;
4241
0
4242
0
  nsRect resultRect = aTextAndDecorationsRect;
4243
0
  int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
4244
0
  for (uint32_t i = 0; i < textStyle->mTextShadow->Length(); ++i) {
4245
0
    nsCSSShadowItem* shadow = textStyle->mTextShadow->ShadowAt(i);
4246
0
    nsMargin blur = nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D);
4247
0
    if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0))
4248
0
      continue;
4249
0
4250
0
    nsRect tmpRect(aTextAndDecorationsRect);
4251
0
4252
0
    tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
4253
0
    tmpRect.Inflate(blur);
4254
0
4255
0
    resultRect.UnionRect(resultRect, tmpRect);
4256
0
  }
4257
0
  return resultRect;
4258
0
}
4259
4260
enum ObjectDimensionType { eWidth, eHeight };
4261
static nscoord
4262
ComputeMissingDimension(const nsSize& aDefaultObjectSize,
4263
                        const nsSize& aIntrinsicRatio,
4264
                        const Maybe<nscoord>& aSpecifiedWidth,
4265
                        const Maybe<nscoord>& aSpecifiedHeight,
4266
                        ObjectDimensionType aDimensionToCompute)
4267
0
{
4268
0
  // The "default sizing algorithm" computes the missing dimension as follows:
4269
0
  // (source: http://dev.w3.org/csswg/css-images-3/#default-sizing )
4270
0
4271
0
  // 1. "If the object has an intrinsic aspect ratio, the missing dimension of
4272
0
  //     the concrete object size is calculated using the intrinsic aspect
4273
0
  //     ratio and the present dimension."
4274
0
  if (aIntrinsicRatio.width > 0 && aIntrinsicRatio.height > 0) {
4275
0
    // Fill in the missing dimension using the intrinsic aspect ratio.
4276
0
    nscoord knownDimensionSize;
4277
0
    float ratio;
4278
0
    if (aDimensionToCompute == eWidth) {
4279
0
      knownDimensionSize = *aSpecifiedHeight;
4280
0
      ratio = aIntrinsicRatio.width / aIntrinsicRatio.height;
4281
0
    } else {
4282
0
      knownDimensionSize = *aSpecifiedWidth;
4283
0
      ratio = aIntrinsicRatio.height / aIntrinsicRatio.width;
4284
0
    }
4285
0
    return NSCoordSaturatingNonnegativeMultiply(knownDimensionSize, ratio);
4286
0
  }
4287
0
4288
0
  // 2. "Otherwise, if the missing dimension is present in the object’s
4289
0
  //     intrinsic dimensions, [...]"
4290
0
  // NOTE: *Skipping* this case, because we already know it's not true -- we're
4291
0
  // in this function because the missing dimension is *not* present in
4292
0
  // the object's intrinsic dimensions.
4293
0
4294
0
  // 3. "Otherwise, the missing dimension of the concrete object size is taken
4295
0
  //     from the default object size. "
4296
0
  return (aDimensionToCompute == eWidth) ?
4297
0
    aDefaultObjectSize.width : aDefaultObjectSize.height;
4298
0
}
4299
4300
/*
4301
 * This computes & returns the concrete object size of replaced content, if
4302
 * that content were to be rendered with "object-fit: none".  (Or, if the
4303
 * element has neither an intrinsic height nor width, this method returns an
4304
 * empty Maybe<> object.)
4305
 *
4306
 * As specced...
4307
 *   http://dev.w3.org/csswg/css-images-3/#valdef-object-fit-none
4308
 * ..we use "the default sizing algorithm with no specified size,
4309
 * and a default object size equal to the replaced element's used width and
4310
 * height."
4311
 *
4312
 * The default sizing algorithm is described here:
4313
 *   http://dev.w3.org/csswg/css-images-3/#default-sizing
4314
 * Quotes in the function-impl are taken from that ^ spec-text.
4315
 *
4316
 * Per its final bulleted section: since there's no specified size,
4317
 * we run the default sizing algorithm using the object's intrinsic size in
4318
 * place of the specified size. But if the object has neither an intrinsic
4319
 * height nor an intrinsic width, then we instead return without populating our
4320
 * outparam, and we let the caller figure out the size (using a contain
4321
 * constraint).
4322
 */
4323
static Maybe<nsSize>
4324
MaybeComputeObjectFitNoneSize(const nsSize& aDefaultObjectSize,
4325
                              const IntrinsicSize& aIntrinsicSize,
4326
                              const nsSize& aIntrinsicRatio)
4327
0
{
4328
0
  // "If the object has an intrinsic height or width, its size is resolved as
4329
0
  // if its intrinsic dimensions were given as the specified size."
4330
0
  //
4331
0
  // So, first we check if we have an intrinsic height and/or width:
4332
0
  Maybe<nscoord> specifiedWidth;
4333
0
  if (aIntrinsicSize.width.GetUnit() == eStyleUnit_Coord) {
4334
0
    specifiedWidth.emplace(aIntrinsicSize.width.GetCoordValue());
4335
0
  }
4336
0
4337
0
  Maybe<nscoord> specifiedHeight;
4338
0
  if (aIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
4339
0
    specifiedHeight.emplace(aIntrinsicSize.height.GetCoordValue());
4340
0
  }
4341
0
4342
0
  Maybe<nsSize> noneSize; // (the value we'll return)
4343
0
  if (specifiedWidth || specifiedHeight) {
4344
0
    // We have at least one specified dimension; use whichever dimension is
4345
0
    // specified, and compute the other one using our intrinsic ratio, or (if
4346
0
    // no valid ratio) using the default object size.
4347
0
    noneSize.emplace();
4348
0
4349
0
    noneSize->width = specifiedWidth ?
4350
0
      *specifiedWidth :
4351
0
      ComputeMissingDimension(aDefaultObjectSize, aIntrinsicRatio,
4352
0
                              specifiedWidth, specifiedHeight,
4353
0
                              eWidth);
4354
0
4355
0
    noneSize->height = specifiedHeight ?
4356
0
      *specifiedHeight :
4357
0
      ComputeMissingDimension(aDefaultObjectSize, aIntrinsicRatio,
4358
0
                              specifiedWidth, specifiedHeight,
4359
0
                              eHeight);
4360
0
  }
4361
0
  // [else:] "Otherwise [if there's neither an intrinsic height nor width], its
4362
0
  // size is resolved as a contain constraint against the default object size."
4363
0
  // We'll let our caller do that, to share code & avoid redundant
4364
0
  // computations; so, we return w/out populating noneSize.
4365
0
  return noneSize;
4366
0
}
4367
4368
// Computes the concrete object size to render into, as described at
4369
// http://dev.w3.org/csswg/css-images-3/#concrete-size-resolution
4370
static nsSize
4371
ComputeConcreteObjectSize(const nsSize& aConstraintSize,
4372
                          const IntrinsicSize& aIntrinsicSize,
4373
                          const nsSize& aIntrinsicRatio,
4374
                          uint8_t aObjectFit)
4375
0
{
4376
0
  // Handle default behavior (filling the container) w/ fast early return.
4377
0
  // (Also: if there's no valid intrinsic ratio, then we have the "fill"
4378
0
  // behavior & just use the constraint size.)
4379
0
  if (MOZ_LIKELY(aObjectFit == NS_STYLE_OBJECT_FIT_FILL) ||
4380
0
      aIntrinsicRatio.width == 0 ||
4381
0
      aIntrinsicRatio.height == 0) {
4382
0
    return aConstraintSize;
4383
0
  }
4384
0
4385
0
  // The type of constraint to compute (cover/contain), if needed:
4386
0
  Maybe<nsImageRenderer::FitType> fitType;
4387
0
4388
0
  Maybe<nsSize> noneSize;
4389
0
  if (aObjectFit == NS_STYLE_OBJECT_FIT_NONE ||
4390
0
      aObjectFit == NS_STYLE_OBJECT_FIT_SCALE_DOWN) {
4391
0
    noneSize = MaybeComputeObjectFitNoneSize(aConstraintSize, aIntrinsicSize,
4392
0
                                             aIntrinsicRatio);
4393
0
    if (!noneSize || aObjectFit == NS_STYLE_OBJECT_FIT_SCALE_DOWN) {
4394
0
      // Need to compute a 'CONTAIN' constraint (either for the 'none' size
4395
0
      // itself, or for comparison w/ the 'none' size to resolve 'scale-down'.)
4396
0
      fitType.emplace(nsImageRenderer::CONTAIN);
4397
0
    }
4398
0
  } else if (aObjectFit == NS_STYLE_OBJECT_FIT_COVER) {
4399
0
    fitType.emplace(nsImageRenderer::COVER);
4400
0
  } else if (aObjectFit == NS_STYLE_OBJECT_FIT_CONTAIN) {
4401
0
    fitType.emplace(nsImageRenderer::CONTAIN);
4402
0
  }
4403
0
4404
0
  Maybe<nsSize> constrainedSize;
4405
0
  if (fitType) {
4406
0
    constrainedSize.emplace(
4407
0
      nsImageRenderer::ComputeConstrainedSize(aConstraintSize,
4408
0
                                              aIntrinsicRatio,
4409
0
                                              *fitType));
4410
0
  }
4411
0
4412
0
  // Now, we should have all the sizing information that we need.
4413
0
  switch (aObjectFit) {
4414
0
    // skipping NS_STYLE_OBJECT_FIT_FILL; we handled it w/ early-return.
4415
0
    case NS_STYLE_OBJECT_FIT_CONTAIN:
4416
0
    case NS_STYLE_OBJECT_FIT_COVER:
4417
0
      MOZ_ASSERT(constrainedSize);
4418
0
      return *constrainedSize;
4419
0
4420
0
    case NS_STYLE_OBJECT_FIT_NONE:
4421
0
      if (noneSize) {
4422
0
        return *noneSize;
4423
0
      }
4424
0
      MOZ_ASSERT(constrainedSize);
4425
0
      return *constrainedSize;
4426
0
4427
0
    case NS_STYLE_OBJECT_FIT_SCALE_DOWN:
4428
0
      MOZ_ASSERT(constrainedSize);
4429
0
      if (noneSize) {
4430
0
        constrainedSize->width =
4431
0
          std::min(constrainedSize->width, noneSize->width);
4432
0
        constrainedSize->height =
4433
0
          std::min(constrainedSize->height, noneSize->height);
4434
0
      }
4435
0
      return *constrainedSize;
4436
0
4437
0
    default:
4438
0
      MOZ_ASSERT_UNREACHABLE("Unexpected enum value for 'object-fit'");
4439
0
      return aConstraintSize; // fall back to (default) 'fill' behavior
4440
0
  }
4441
0
}
4442
4443
// (Helper for HasInitialObjectFitAndPosition, to check
4444
// each "object-position" coord.)
4445
static bool
4446
IsCoord50Pct(const mozilla::Position::Coord& aCoord)
4447
0
{
4448
0
  return (aCoord.mLength == 0 &&
4449
0
          aCoord.mHasPercent &&
4450
0
          aCoord.mPercent == 0.5f);
4451
0
}
4452
4453
// Indicates whether the given nsStylePosition has the initial values
4454
// for the "object-fit" and "object-position" properties.
4455
static bool
4456
HasInitialObjectFitAndPosition(const nsStylePosition* aStylePos)
4457
0
{
4458
0
  const mozilla::Position& objectPos = aStylePos->mObjectPosition;
4459
0
4460
0
  return aStylePos->mObjectFit == NS_STYLE_OBJECT_FIT_FILL &&
4461
0
    IsCoord50Pct(objectPos.mXPosition) &&
4462
0
    IsCoord50Pct(objectPos.mYPosition);
4463
0
}
4464
4465
/* static */ nsRect
4466
nsLayoutUtils::ComputeObjectDestRect(const nsRect& aConstraintRect,
4467
                                     const IntrinsicSize& aIntrinsicSize,
4468
                                     const nsSize& aIntrinsicRatio,
4469
                                     const nsStylePosition* aStylePos,
4470
                                     nsPoint* aAnchorPoint)
4471
0
{
4472
0
  // Step 1: Figure out our "concrete object size"
4473
0
  // (the size of the region we'll actually draw our image's pixels into).
4474
0
  nsSize concreteObjectSize =
4475
0
    ComputeConcreteObjectSize(aConstraintRect.Size(), aIntrinsicSize,
4476
0
                              aIntrinsicRatio, aStylePos->mObjectFit);
4477
0
4478
0
  // Step 2: Figure out how to align that region in the element's content-box.
4479
0
  nsPoint imageTopLeftPt, imageAnchorPt;
4480
0
  nsImageRenderer::ComputeObjectAnchorPoint(aStylePos->mObjectPosition,
4481
0
                                            aConstraintRect.Size(),
4482
0
                                            concreteObjectSize,
4483
0
                                            &imageTopLeftPt, &imageAnchorPt);
4484
0
  // Right now, we're with respect to aConstraintRect's top-left point.  We add
4485
0
  // that point here, to convert to the same broader coordinate space that
4486
0
  // aConstraintRect is in.
4487
0
  imageTopLeftPt += aConstraintRect.TopLeft();
4488
0
  imageAnchorPt += aConstraintRect.TopLeft();
4489
0
4490
0
  if (aAnchorPoint) {
4491
0
    // Special-case: if our "object-fit" and "object-position" properties have
4492
0
    // their default values ("object-fit: fill; object-position:50% 50%"), then
4493
0
    // we'll override the calculated imageAnchorPt, and instead use the
4494
0
    // object's top-left corner.
4495
0
    //
4496
0
    // This special case is partly for backwards compatibility (since
4497
0
    // traditionally we've pixel-aligned the top-left corner of e.g. <img>
4498
0
    // elements), and partly because ComputeSnappedDrawingParameters produces
4499
0
    // less error if the anchor point is at the top-left corner. So, all other
4500
0
    // things being equal, we prefer that code path with less error.
4501
0
    if (HasInitialObjectFitAndPosition(aStylePos)) {
4502
0
      *aAnchorPoint = imageTopLeftPt;
4503
0
    } else {
4504
0
      *aAnchorPoint = imageAnchorPt;
4505
0
    }
4506
0
  }
4507
0
  return nsRect(imageTopLeftPt, concreteObjectSize);
4508
0
}
4509
4510
already_AddRefed<nsFontMetrics>
4511
nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame, float aInflation)
4512
0
{
4513
0
  ComputedStyle* computedStyle = aFrame->Style();
4514
0
  uint8_t variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL;
4515
0
  if (computedStyle->IsTextCombined()) {
4516
0
    MOZ_ASSERT(aFrame->IsTextFrame());
4517
0
    auto textFrame = static_cast<const nsTextFrame*>(aFrame);
4518
0
    auto clusters = textFrame->CountGraphemeClusters();
4519
0
    if (clusters == 2) {
4520
0
      variantWidth = NS_FONT_VARIANT_WIDTH_HALF;
4521
0
    } else if (clusters == 3) {
4522
0
      variantWidth = NS_FONT_VARIANT_WIDTH_THIRD;
4523
0
    } else if (clusters == 4) {
4524
0
      variantWidth = NS_FONT_VARIANT_WIDTH_QUARTER;
4525
0
    }
4526
0
  }
4527
0
  return GetFontMetricsForComputedStyle(computedStyle, aFrame->PresContext(),
4528
0
                                        aInflation, variantWidth);
4529
0
}
4530
4531
already_AddRefed<nsFontMetrics>
4532
nsLayoutUtils::GetFontMetricsForComputedStyle(ComputedStyle* aComputedStyle,
4533
                                              nsPresContext* aPresContext,
4534
                                              float aInflation,
4535
                                              uint8_t aVariantWidth)
4536
0
{
4537
0
  WritingMode wm(aComputedStyle);
4538
0
  const nsStyleFont* styleFont = aComputedStyle->StyleFont();
4539
0
  nsFontMetrics::Params params;
4540
0
  params.language = styleFont->mLanguage;
4541
0
  params.explicitLanguage = styleFont->mExplicitLanguage;
4542
0
  params.orientation =
4543
0
    wm.IsVertical() && !wm.IsSideways() ? gfxFont::eVertical
4544
0
                                        : gfxFont::eHorizontal;
4545
0
  // pass the user font set object into the device context to
4546
0
  // pass along to CreateFontGroup
4547
0
  params.userFontSet = aPresContext->GetUserFontSet();
4548
0
  params.textPerf = aPresContext->GetTextPerfMetrics();
4549
0
4550
0
  // When aInflation is 1.0 and we don't require width variant, avoid
4551
0
  // making a local copy of the nsFont.
4552
0
  // This also avoids running font.size through floats when it is large,
4553
0
  // which would be lossy.  Fortunately, in such cases, aInflation is
4554
0
  // guaranteed to be 1.0f.
4555
0
  if (aInflation == 1.0f && aVariantWidth == NS_FONT_VARIANT_WIDTH_NORMAL) {
4556
0
    return aPresContext->DeviceContext()->GetMetricsFor(styleFont->mFont, params);
4557
0
  }
4558
0
4559
0
  nsFont font = styleFont->mFont;
4560
0
  font.size = NSToCoordRound(font.size * aInflation);
4561
0
  font.variantWidth = aVariantWidth;
4562
0
  return aPresContext->DeviceContext()->GetMetricsFor(font, params);
4563
0
}
4564
4565
nsIFrame*
4566
nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame)
4567
0
{
4568
0
  nsIFrame* result = aDescendantFrame;
4569
0
4570
0
  while (result) {
4571
0
    nsIFrame* parent = result->GetParent();
4572
0
    if (parent == aParent) {
4573
0
      break;
4574
0
    }
4575
0
4576
0
    // The frame is not an immediate child of aParent so walk up another level
4577
0
    result = parent;
4578
0
  }
4579
0
4580
0
  return result;
4581
0
}
4582
4583
nsBlockFrame*
4584
nsLayoutUtils::GetAsBlock(nsIFrame* aFrame)
4585
0
{
4586
0
  nsBlockFrame* block = do_QueryFrame(aFrame);
4587
0
  return block;
4588
0
}
4589
4590
nsBlockFrame*
4591
nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame)
4592
0
{
4593
0
  nsIFrame* nextAncestor;
4594
0
  for (nextAncestor = aFrame->GetParent(); nextAncestor;
4595
0
       nextAncestor = nextAncestor->GetParent()) {
4596
0
    nsBlockFrame* block = GetAsBlock(nextAncestor);
4597
0
    if (block)
4598
0
      return block;
4599
0
  }
4600
0
  return nullptr;
4601
0
}
4602
4603
nsIFrame*
4604
nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame* aFrame)
4605
0
{
4606
0
  if (!(aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT))
4607
0
    return aFrame;
4608
0
4609
0
  nsIFrame* f = aFrame;
4610
0
  do {
4611
0
    f = GetParentOrPlaceholderFor(f);
4612
0
  } while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT);
4613
0
  return f;
4614
0
}
4615
4616
nsIFrame*
4617
nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame)
4618
0
{
4619
0
  if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
4620
0
      && !aFrame->GetPrevInFlow()) {
4621
0
    return aFrame->GetProperty(nsIFrame::PlaceholderFrameProperty());
4622
0
  }
4623
0
  return aFrame->GetParent();
4624
0
}
4625
4626
nsIFrame*
4627
nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(nsIFrame* aFrame)
4628
0
{
4629
0
  nsIFrame* f = GetParentOrPlaceholderFor(aFrame);
4630
0
  if (f)
4631
0
    return f;
4632
0
  return GetCrossDocParentFrame(aFrame);
4633
0
}
4634
4635
nsIFrame*
4636
nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame *aFrame)
4637
0
{
4638
0
  nsIFrame *result = aFrame->GetNextContinuation();
4639
0
  if (result)
4640
0
    return result;
4641
0
4642
0
  if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0) {
4643
0
    // We only store the ib-split sibling annotation with the first
4644
0
    // frame in the continuation chain. Walk back to find that frame now.
4645
0
    aFrame = aFrame->FirstContinuation();
4646
0
4647
0
    return aFrame->GetProperty(nsIFrame::IBSplitSibling());
4648
0
  }
4649
0
4650
0
  return nullptr;
4651
0
}
4652
4653
nsIFrame*
4654
nsLayoutUtils::FirstContinuationOrIBSplitSibling(const nsIFrame* aFrame)
4655
0
{
4656
0
  nsIFrame* result = aFrame->FirstContinuation();
4657
0
4658
0
  if (result->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
4659
0
    while (auto* f = result->GetProperty(nsIFrame::IBSplitPrevSibling())) {
4660
0
      result = f;
4661
0
    }
4662
0
  }
4663
0
4664
0
  return result;
4665
0
}
4666
4667
nsIFrame*
4668
nsLayoutUtils::LastContinuationOrIBSplitSibling(const nsIFrame* aFrame)
4669
0
{
4670
0
  nsIFrame* result = aFrame->FirstContinuation();
4671
0
4672
0
  if (result->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
4673
0
    while (auto* f = result->GetProperty(nsIFrame::IBSplitSibling())) {
4674
0
      result = f;
4675
0
    }
4676
0
  }
4677
0
4678
0
  return result->LastContinuation();
4679
0
}
4680
4681
bool
4682
nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
4683
0
{
4684
0
  if (aFrame->GetPrevContinuation()) {
4685
0
    return false;
4686
0
  }
4687
0
  if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
4688
0
      aFrame->GetProperty(nsIFrame::IBSplitPrevSibling())) {
4689
0
    return false;
4690
0
  }
4691
0
4692
0
  return true;
4693
0
}
4694
4695
bool
4696
nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame)
4697
0
{
4698
0
  if (!aFrame)
4699
0
    return false;
4700
0
4701
0
  nsIFrame* rootScrollFrame = aFrame->PresShell()->GetRootScrollFrame();
4702
0
  if (!rootScrollFrame)
4703
0
    return false;
4704
0
4705
0
  nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame);
4706
0
  NS_ASSERTION(rootScrollableFrame, "The root scorollable frame is null");
4707
0
4708
0
  if (!IsProperAncestorFrame(rootScrollFrame, aFrame))
4709
0
    return false;
4710
0
4711
0
  nsIFrame* rootScrolledFrame = rootScrollableFrame->GetScrolledFrame();
4712
0
  return !(rootScrolledFrame == aFrame ||
4713
0
           IsProperAncestorFrame(rootScrolledFrame, aFrame));
4714
0
}
4715
4716
// Use only for widths/heights (or their min/max), since it clamps
4717
// negative calc() results to 0.
4718
static bool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult)
4719
0
{
4720
0
  if (aStyle.IsCalcUnit()) {
4721
0
    if (aStyle.CalcHasPercent()) {
4722
0
      return false;
4723
0
    }
4724
0
    // If it has no percents, we can pass 0 for the percentage basis.
4725
0
    aResult = aStyle.ComputeComputedCalc(0);
4726
0
    if (aResult < 0)
4727
0
      aResult = 0;
4728
0
    return true;
4729
0
  }
4730
0
4731
0
  if (eStyleUnit_Coord != aStyle.GetUnit())
4732
0
    return false;
4733
0
4734
0
  aResult = aStyle.GetCoordValue();
4735
0
  NS_ASSERTION(aResult >= 0, "negative widths not allowed");
4736
0
  return true;
4737
0
}
4738
4739
static nscoord
4740
GetBSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing,
4741
                         nsIFrame* aFrame,
4742
                         bool aHorizontalAxis,
4743
                         bool aIgnorePadding);
4744
4745
// Only call on style coords for which GetAbsoluteCoord returned false.
4746
static bool
4747
GetPercentBSize(const nsStyleCoord& aStyle,
4748
                nsIFrame* aFrame,
4749
                bool aHorizontalAxis,
4750
                nscoord& aResult)
4751
0
{
4752
0
  if (eStyleUnit_Percent != aStyle.GetUnit() &&
4753
0
      !aStyle.IsCalcUnit())
4754
0
    return false;
4755
0
4756
0
  MOZ_ASSERT(!aStyle.IsCalcUnit() || aStyle.CalcHasPercent(),
4757
0
             "GetAbsoluteCoord should have handled this");
4758
0
4759
0
  // During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses
4760
0
  // SetComputedHeight on the reflow state for its child to propagate its
4761
0
  // computed height to the scrolled content. So here we skip to the scroll
4762
0
  // frame that contains this scrolled content in order to get the same
4763
0
  // behavior as layout when computing percentage heights.
4764
0
  nsIFrame *f = aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);
4765
0
  if (!f) {
4766
0
    MOZ_ASSERT_UNREACHABLE("top of frame tree not a containing block");
4767
0
    return false;
4768
0
  }
4769
0
4770
0
  WritingMode wm = f->GetWritingMode();
4771
0
4772
0
  const nsStylePosition *pos = f->StylePosition();
4773
0
  const nsStyleCoord& bSizeCoord = pos->BSize(wm);
4774
0
  nscoord h;
4775
0
  if (!GetAbsoluteCoord(bSizeCoord, h) &&
4776
0
      !GetPercentBSize(bSizeCoord, f, aHorizontalAxis, h)) {
4777
0
    NS_ASSERTION(bSizeCoord.GetUnit() == eStyleUnit_Auto ||
4778
0
                 bSizeCoord.HasPercent(),
4779
0
                 "unknown block-size unit");
4780
0
    LayoutFrameType fType = f->Type();
4781
0
    if (fType != LayoutFrameType::Viewport &&
4782
0
        fType != LayoutFrameType::Canvas &&
4783
0
        fType != LayoutFrameType::PageContent) {
4784
0
      // There's no basis for the percentage height, so it acts like auto.
4785
0
      // Should we consider a max-height < min-height pair a basis for
4786
0
      // percentage heights?  The spec is somewhat unclear, and not doing
4787
0
      // so is simpler and avoids troubling discontinuities in behavior,
4788
0
      // so I'll choose not to. -LDB
4789
0
      return false;
4790
0
    }
4791
0
4792
0
    NS_ASSERTION(bSizeCoord.GetUnit() == eStyleUnit_Auto,
4793
0
                 "Unexpected block-size unit for viewport or canvas or page-content");
4794
0
    // For the viewport, canvas, and page-content kids, the percentage
4795
0
    // basis is just the parent block-size.
4796
0
    h = f->BSize(wm);
4797
0
    if (h == NS_UNCONSTRAINEDSIZE) {
4798
0
      // We don't have a percentage basis after all
4799
0
      return false;
4800
0
    }
4801
0
  }
4802
0
4803
0
  const nsStyleCoord& maxBSizeCoord = pos->MaxBSize(wm);
4804
0
4805
0
  nscoord maxh;
4806
0
  if (GetAbsoluteCoord(maxBSizeCoord, maxh) ||
4807
0
      GetPercentBSize(maxBSizeCoord, f, aHorizontalAxis, maxh)) {
4808
0
    if (maxh < h)
4809
0
      h = maxh;
4810
0
  } else {
4811
0
    NS_ASSERTION(maxBSizeCoord.GetUnit() == eStyleUnit_None ||
4812
0
                 maxBSizeCoord.HasPercent(),
4813
0
                 "unknown max block-size unit");
4814
0
  }
4815
0
4816
0
  const nsStyleCoord& minBSizeCoord = pos->MinBSize(wm);
4817
0
4818
0
  nscoord minh;
4819
0
  if (GetAbsoluteCoord(minBSizeCoord, minh) ||
4820
0
      GetPercentBSize(minBSizeCoord, f, aHorizontalAxis, minh)) {
4821
0
    if (minh > h)
4822
0
      h = minh;
4823
0
  } else {
4824
0
    NS_ASSERTION(minBSizeCoord.HasPercent() ||
4825
0
                 minBSizeCoord.GetUnit() == eStyleUnit_Auto,
4826
0
                 "unknown min block-size unit");
4827
0
  }
4828
0
4829
0
  // Now adjust h for box-sizing styles on the parent.  We never ignore padding
4830
0
  // here.  That could conceivably cause some problems with fieldsets (which are
4831
0
  // the one place that wants to ignore padding), but solving that here without
4832
0
  // hardcoding a check for f being a fieldset-content frame is a bit of a pain.
4833
0
  nscoord bSizeTakenByBoxSizing =
4834
0
    GetBSizeTakenByBoxSizing(pos->mBoxSizing, f, aHorizontalAxis, false);
4835
0
  h = std::max(0, h - bSizeTakenByBoxSizing);
4836
0
4837
0
  if (aStyle.IsCalcUnit()) {
4838
0
    aResult = std::max(aStyle.ComputeComputedCalc(h), 0);
4839
0
    return true;
4840
0
  }
4841
0
4842
0
  aResult = NSToCoordRound(aStyle.GetPercentValue() * h);
4843
0
  return true;
4844
0
}
4845
4846
// Return true if aStyle can be resolved to a definite value and if so
4847
// return that value in aResult.
4848
static bool
4849
GetDefiniteSize(const nsStyleCoord&       aStyle,
4850
                nsIFrame*                 aFrame,
4851
                bool                      aIsInlineAxis,
4852
                const Maybe<LogicalSize>& aPercentageBasis,
4853
                nscoord*                  aResult)
4854
0
{
4855
0
  switch (aStyle.GetUnit()) {
4856
0
    case eStyleUnit_Coord:
4857
0
      *aResult = aStyle.GetCoordValue();
4858
0
      return true;
4859
0
    case eStyleUnit_Percent: {
4860
0
      if (aPercentageBasis.isNothing()) {
4861
0
        return false;
4862
0
      }
4863
0
      auto wm = aFrame->GetWritingMode();
4864
0
      nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm)
4865
0
                                 : aPercentageBasis.value().BSize(wm);
4866
0
      if (pb != NS_UNCONSTRAINEDSIZE) {
4867
0
        nscoord p = NSToCoordFloorClamped(pb * aStyle.GetPercentValue());
4868
0
        *aResult = std::max(nscoord(0), p);
4869
0
        return true;
4870
0
      }
4871
0
      return false;
4872
0
    }
4873
0
    case eStyleUnit_Calc: {
4874
0
      nsStyleCoord::Calc* calc = aStyle.GetCalcValue();
4875
0
      if (calc->mPercent != 0.0f) {
4876
0
        if (aPercentageBasis.isNothing()) {
4877
0
          return false;
4878
0
        }
4879
0
        auto wm = aFrame->GetWritingMode();
4880
0
        nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm)
4881
0
                                   : aPercentageBasis.value().BSize(wm);
4882
0
        if (pb == NS_UNCONSTRAINEDSIZE) {
4883
0
          return false;
4884
0
        }
4885
0
        *aResult = std::max(0, calc->mLength +
4886
0
                               NSToCoordFloorClamped(pb * calc->mPercent));
4887
0
      } else {
4888
0
        *aResult = std::max(0, calc->mLength);
4889
0
      }
4890
0
      return true;
4891
0
    }
4892
0
    default:
4893
0
      return false;
4894
0
  }
4895
0
}
4896
4897
//
4898
// NOTE: this function will be replaced by GetDefiniteSizeTakenByBoxSizing (bug 1363918).
4899
// Please do not add new uses of this function.
4900
//
4901
// Get the amount of vertical space taken out of aFrame's content area due to
4902
// its borders and paddings given the box-sizing value in aBoxSizing.  We don't
4903
// get aBoxSizing from the frame because some callers want to compute this for
4904
// specific box-sizing values.  aHorizontalAxis is true if our inline direction
4905
// is horisontal and our block direction is vertical.  aIgnorePadding is true if
4906
// padding should be ignored.
4907
static nscoord
4908
GetBSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing,
4909
                         nsIFrame* aFrame,
4910
                         bool aHorizontalAxis,
4911
                         bool aIgnorePadding)
4912
0
{
4913
0
  nscoord bSizeTakenByBoxSizing = 0;
4914
0
  if (aBoxSizing == StyleBoxSizing::Border) {
4915
0
    const nsStyleBorder* styleBorder = aFrame->StyleBorder();
4916
0
    bSizeTakenByBoxSizing +=
4917
0
      aHorizontalAxis ? styleBorder->GetComputedBorder().TopBottom()
4918
0
                      : styleBorder->GetComputedBorder().LeftRight();
4919
0
    if (!aIgnorePadding) {
4920
0
      const nsStyleSides& stylePadding =
4921
0
        aFrame->StylePadding()->mPadding;
4922
0
      const nsStyleCoord& paddingStart =
4923
0
        stylePadding.Get(aHorizontalAxis ? eSideTop : eSideLeft);
4924
0
      const nsStyleCoord& paddingEnd =
4925
0
        stylePadding.Get(aHorizontalAxis ? eSideBottom : eSideRight);
4926
0
      nscoord pad;
4927
0
      // XXXbz Calling GetPercentBSize on padding values looks bogus, since
4928
0
      // percent padding is always a percentage of the inline-size of the
4929
0
      // containing block.  We should perhaps just treat non-absolute paddings
4930
0
      // here as 0 instead, except that in some cases the width may in fact be
4931
0
      // known.  See bug 1231059.
4932
0
      if (GetAbsoluteCoord(paddingStart, pad) ||
4933
0
          GetPercentBSize(paddingStart, aFrame, aHorizontalAxis, pad)) {
4934
0
        bSizeTakenByBoxSizing += pad;
4935
0
      }
4936
0
      if (GetAbsoluteCoord(paddingEnd, pad) ||
4937
0
          GetPercentBSize(paddingEnd, aFrame, aHorizontalAxis, pad)) {
4938
0
        bSizeTakenByBoxSizing += pad;
4939
0
      }
4940
0
    }
4941
0
  }
4942
0
  return bSizeTakenByBoxSizing;
4943
0
}
4944
4945
// Get the amount of space taken out of aFrame's content area due to its
4946
// borders and paddings given the box-sizing value in aBoxSizing.  We don't
4947
// get aBoxSizing from the frame because some callers want to compute this for
4948
// specific box-sizing values.
4949
// aIsInlineAxis is true if we're computing for aFrame's inline axis.
4950
// aIgnorePadding is true if padding should be ignored.
4951
static nscoord
4952
GetDefiniteSizeTakenByBoxSizing(StyleBoxSizing aBoxSizing,
4953
                                nsIFrame* aFrame,
4954
                                bool aIsInlineAxis,
4955
                                bool aIgnorePadding,
4956
                                const Maybe<LogicalSize>& aPercentageBasis)
4957
0
{
4958
0
  nscoord sizeTakenByBoxSizing = 0;
4959
0
  if (MOZ_UNLIKELY(aBoxSizing == StyleBoxSizing::Border)) {
4960
0
    const bool isHorizontalAxis =
4961
0
      aIsInlineAxis == !aFrame->GetWritingMode().IsVertical();
4962
0
    const nsStyleBorder* styleBorder = aFrame->StyleBorder();
4963
0
    sizeTakenByBoxSizing =
4964
0
      isHorizontalAxis ? styleBorder->GetComputedBorder().LeftRight()
4965
0
                       : styleBorder->GetComputedBorder().TopBottom();
4966
0
    if (!aIgnorePadding) {
4967
0
      const nsStyleSides& stylePadding = aFrame->StylePadding()->mPadding;
4968
0
      const nsStyleCoord& pStart =
4969
0
        stylePadding.Get(isHorizontalAxis ? eSideLeft : eSideTop);
4970
0
      const nsStyleCoord& pEnd =
4971
0
        stylePadding.Get(isHorizontalAxis ? eSideRight : eSideBottom);
4972
0
      nscoord pad;
4973
0
      // XXXbz Calling GetPercentBSize on padding values looks bogus, since
4974
0
      // percent padding is always a percentage of the inline-size of the
4975
0
      // containing block.  We should perhaps just treat non-absolute paddings
4976
0
      // here as 0 instead, except that in some cases the width may in fact be
4977
0
      // known.  See bug 1231059.
4978
0
      if (GetDefiniteSize(pStart, aFrame, aIsInlineAxis, aPercentageBasis, &pad) ||
4979
0
          (aPercentageBasis.isNothing() &&
4980
0
           GetPercentBSize(pStart, aFrame, isHorizontalAxis, pad))) {
4981
0
        sizeTakenByBoxSizing += pad;
4982
0
      }
4983
0
      if (GetDefiniteSize(pEnd, aFrame, aIsInlineAxis, aPercentageBasis, &pad) ||
4984
0
          (aPercentageBasis.isNothing() &&
4985
0
           GetPercentBSize(pEnd, aFrame, isHorizontalAxis, pad))) {
4986
0
        sizeTakenByBoxSizing += pad;
4987
0
      }
4988
0
    }
4989
0
  }
4990
0
  return sizeTakenByBoxSizing;
4991
0
}
4992
4993
// Handles only -moz-max-content and -moz-min-content, and
4994
// -moz-fit-content for min-width and max-width, since the others
4995
// (-moz-fit-content for width, and -moz-available) have no effect on
4996
// intrinsic widths.
4997
enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH };
4998
static bool
4999
GetIntrinsicCoord(const nsStyleCoord& aStyle,
5000
                  gfxContext* aRenderingContext,
5001
                  nsIFrame* aFrame,
5002
                  eWidthProperty aProperty,
5003
                  nscoord& aResult)
5004
0
{
5005
0
  MOZ_ASSERT(aProperty == PROP_WIDTH || aProperty == PROP_MAX_WIDTH ||
5006
0
             aProperty == PROP_MIN_WIDTH, "unexpected property");
5007
0
5008
0
  if (aStyle.GetUnit() != eStyleUnit_Enumerated)
5009
0
    return false;
5010
0
  int32_t val = aStyle.GetIntValue();
5011
0
  NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT ||
5012
0
               val == NS_STYLE_WIDTH_MIN_CONTENT ||
5013
0
               val == NS_STYLE_WIDTH_FIT_CONTENT ||
5014
0
               val == NS_STYLE_WIDTH_AVAILABLE,
5015
0
               "unexpected enumerated value for width property");
5016
0
  if (val == NS_STYLE_WIDTH_AVAILABLE)
5017
0
    return false;
5018
0
  if (val == NS_STYLE_WIDTH_FIT_CONTENT) {
5019
0
    if (aProperty == PROP_WIDTH)
5020
0
      return false; // handle like 'width: auto'
5021
0
    if (aProperty == PROP_MAX_WIDTH)
5022
0
      // constrain large 'width' values down to -moz-max-content
5023
0
      val = NS_STYLE_WIDTH_MAX_CONTENT;
5024
0
    else
5025
0
      // constrain small 'width' or 'max-width' values up to -moz-min-content
5026
0
      val = NS_STYLE_WIDTH_MIN_CONTENT;
5027
0
  }
5028
0
5029
0
  NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT ||
5030
0
               val == NS_STYLE_WIDTH_MIN_CONTENT,
5031
0
               "should have reduced everything remaining to one of these");
5032
0
5033
0
  // If aFrame is a container for font size inflation, then shrink
5034
0
  // wrapping inside of it should not apply font size inflation.
5035
0
  AutoMaybeDisableFontInflation an(aFrame);
5036
0
5037
0
  if (val == NS_STYLE_WIDTH_MAX_CONTENT)
5038
0
    aResult = aFrame->GetPrefISize(aRenderingContext);
5039
0
  else
5040
0
    aResult = aFrame->GetMinISize(aRenderingContext);
5041
0
  return true;
5042
0
}
5043
5044
#undef  DEBUG_INTRINSIC_WIDTH
5045
5046
#ifdef DEBUG_INTRINSIC_WIDTH
5047
static int32_t gNoiseIndent = 0;
5048
#endif
5049
5050
// Return true for form controls whose minimum intrinsic inline-size
5051
// shrinks to 0 when they have a percentage inline-size (but not
5052
// percentage max-inline-size).  (Proper replaced elements, whose
5053
// intrinsic minimium inline-size shrinks to 0 for both percentage
5054
// inline-size and percentage max-inline-size, are handled elsewhere.)
5055
inline static bool
5056
FormControlShrinksForPercentISize(nsIFrame* aFrame)
5057
0
{
5058
0
  if (!aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
5059
0
    // Quick test to reject most frames.
5060
0
    return false;
5061
0
  }
5062
0
5063
0
  LayoutFrameType fType = aFrame->Type();
5064
0
  if (fType == LayoutFrameType::Meter || fType == LayoutFrameType::Progress) {
5065
0
    // progress and meter do have this shrinking behavior
5066
0
    // FIXME: Maybe these should be nsIFormControlFrame?
5067
0
    return true;
5068
0
  }
5069
0
5070
0
  if (!static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
5071
0
    // Not a form control.  This includes fieldsets, which do not
5072
0
    // shrink.
5073
0
    return false;
5074
0
  }
5075
0
5076
0
  if (fType == LayoutFrameType::GfxButtonControl ||
5077
0
      fType == LayoutFrameType::HTMLButtonControl) {
5078
0
    // Buttons don't have this shrinking behavior.  (Note that color
5079
0
    // inputs do, even though they inherit from button, so we can't use
5080
0
    // do_QueryFrame here.)
5081
0
    return false;
5082
0
  }
5083
0
5084
0
  return true;
5085
0
}
5086
5087
// https://drafts.csswg.org/css-sizing-3/#percentage-sizing
5088
// Return true if the above spec's rule for replaced boxes applies.
5089
// XXX bug 1463700 will make this match the spec...
5090
static bool
5091
IsReplacedBoxResolvedAgainstZero(nsIFrame* aFrame,
5092
                                 const nsStyleCoord& aStyleSize,
5093
                                 const nsStyleCoord& aStyleMaxSize)
5094
0
{
5095
0
  const bool sizeHasPercent = aStyleSize.HasPercent();
5096
0
  return ((sizeHasPercent || aStyleMaxSize.HasPercent()) &&
5097
0
          aFrame->IsFrameOfType(nsIFrame::eReplacedSizing)) ||
5098
0
         (sizeHasPercent &&
5099
0
          FormControlShrinksForPercentISize(aFrame));
5100
0
}
5101
5102
/**
5103
 * Add aOffsets which describes what to add on outside of the content box
5104
 * aContentSize (controlled by 'box-sizing') and apply min/max properties.
5105
 * We have to account for these properties after getting all the offsets
5106
 * (margin, border, padding) because percentages do not operate linearly.
5107
 * Doing this is ok because although percentages aren't handled linearly,
5108
 * they are handled monotonically.
5109
 *
5110
 * @param aContentSize the content size calculated so far
5111
                       (@see IntrinsicForContainer)
5112
 * @param aContentMinSize ditto min content size
5113
 * @param aStyleSize a 'width' or 'height' property value
5114
 * @param aFixedMinSize if aStyleMinSize is a definite size then this points to
5115
 *                      the value, otherwise nullptr
5116
 * @param aStyleMinSize a 'min-width' or 'min-height' property value
5117
 * @param aFixedMaxSize if aStyleMaxSize is a definite size then this points to
5118
 *                      the value, otherwise nullptr
5119
 * @param aStyleMaxSize a 'max-width' or 'max-height' property value
5120
 * @param aFlags same as for IntrinsicForContainer
5121
 * @param aContainerWM the container's WM
5122
 */
5123
static nscoord
5124
AddIntrinsicSizeOffset(gfxContext* aRenderingContext,
5125
                       nsIFrame* aFrame,
5126
                       const nsIFrame::IntrinsicISizeOffsetData& aOffsets,
5127
                       nsLayoutUtils::IntrinsicISizeType aType,
5128
                       StyleBoxSizing aBoxSizing,
5129
                       nscoord aContentSize,
5130
                       nscoord aContentMinSize,
5131
                       const nsStyleCoord& aStyleSize,
5132
                       const nscoord* aFixedMinSize,
5133
                       const nsStyleCoord& aStyleMinSize,
5134
                       const nscoord* aFixedMaxSize,
5135
                       const nsStyleCoord& aStyleMaxSize,
5136
                       uint32_t aFlags,
5137
                       PhysicalAxis aAxis)
5138
0
{
5139
0
  nscoord result = aContentSize;
5140
0
  nscoord min = aContentMinSize;
5141
0
  nscoord coordOutsideSize = 0;
5142
0
5143
0
  if (!(aFlags & nsLayoutUtils::IGNORE_PADDING)) {
5144
0
    coordOutsideSize += aOffsets.hPadding;
5145
0
  }
5146
0
5147
0
  coordOutsideSize += aOffsets.hBorder;
5148
0
5149
0
  if (aBoxSizing == StyleBoxSizing::Border) {
5150
0
    min += coordOutsideSize;
5151
0
    result = NSCoordSaturatingAdd(result, coordOutsideSize);
5152
0
5153
0
    coordOutsideSize = 0;
5154
0
  }
5155
0
5156
0
  coordOutsideSize += aOffsets.hMargin;
5157
0
5158
0
  min += coordOutsideSize;
5159
0
  result = NSCoordSaturatingAdd(result, coordOutsideSize);
5160
0
5161
0
  nscoord size;
5162
0
  if (aType == nsLayoutUtils::MIN_ISIZE &&
5163
0
      ::IsReplacedBoxResolvedAgainstZero(aFrame, aStyleSize, aStyleMaxSize)) {
5164
0
    // XXX bug 1463700: this doesn't handle calc() according to spec
5165
0
    result = 0; // let |min| handle padding/border/margin
5166
0
  } else if (GetAbsoluteCoord(aStyleSize, size) ||
5167
0
             GetIntrinsicCoord(aStyleSize, aRenderingContext, aFrame,
5168
0
                               PROP_WIDTH, size)) {
5169
0
    result = size + coordOutsideSize;
5170
0
  }
5171
0
5172
0
  nscoord maxSize = aFixedMaxSize ? *aFixedMaxSize : 0;
5173
0
  if (aFixedMaxSize ||
5174
0
      GetIntrinsicCoord(aStyleMaxSize, aRenderingContext, aFrame,
5175
0
                        PROP_MAX_WIDTH, maxSize)) {
5176
0
    maxSize += coordOutsideSize;
5177
0
    if (result > maxSize) {
5178
0
      result = maxSize;
5179
0
    }
5180
0
  }
5181
0
5182
0
  nscoord minSize = aFixedMinSize ? *aFixedMinSize : 0;
5183
0
  if (aFixedMinSize ||
5184
0
      GetIntrinsicCoord(aStyleMinSize, aRenderingContext, aFrame,
5185
0
                        PROP_MIN_WIDTH, minSize)) {
5186
0
    minSize += coordOutsideSize;
5187
0
    if (result < minSize) {
5188
0
      result = minSize;
5189
0
    }
5190
0
  }
5191
0
5192
0
  if (result < min) {
5193
0
    result = min;
5194
0
  }
5195
0
5196
0
  const nsStyleDisplay* disp = aFrame->StyleDisplay();
5197
0
  if (aFrame->IsThemed(disp)) {
5198
0
    LayoutDeviceIntSize devSize;
5199
0
    bool canOverride = true;
5200
0
    nsPresContext* pc = aFrame->PresContext();
5201
0
    pc->GetTheme()->GetMinimumWidgetSize(pc, aFrame, disp->mAppearance,
5202
0
                                         &devSize, &canOverride);
5203
0
    nscoord themeSize =
5204
0
      pc->DevPixelsToAppUnits(aAxis == eAxisVertical ? devSize.height
5205
0
                                                     : devSize.width);
5206
0
    // GetMinimumWidgetSize() returns a border-box width.
5207
0
    themeSize += aOffsets.hMargin;
5208
0
    if (themeSize > result || !canOverride) {
5209
0
      result = themeSize;
5210
0
    }
5211
0
  }
5212
0
  return result;
5213
0
}
5214
5215
static void
5216
AddStateBitToAncestors(nsIFrame* aFrame, nsFrameState aBit)
5217
0
{
5218
0
  for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
5219
0
    if (f->HasAnyStateBits(aBit)) {
5220
0
      break;
5221
0
    }
5222
0
    f->AddStateBits(aBit);
5223
0
  }
5224
0
}
5225
5226
/* static */ nscoord
5227
nsLayoutUtils::IntrinsicForAxis(PhysicalAxis              aAxis,
5228
                                gfxContext*               aRenderingContext,
5229
                                nsIFrame*                 aFrame,
5230
                                IntrinsicISizeType        aType,
5231
                                const Maybe<LogicalSize>& aPercentageBasis,
5232
                                uint32_t                  aFlags,
5233
                                nscoord                   aMarginBoxMinSizeClamp)
5234
0
{
5235
0
  MOZ_ASSERT(aFrame, "null frame");
5236
0
  MOZ_ASSERT(aFrame->GetParent(),
5237
0
             "IntrinsicForAxis called on frame not in tree");
5238
0
  MOZ_ASSERT(aType == MIN_ISIZE || aType == PREF_ISIZE, "bad type");
5239
0
  MOZ_ASSERT(aFrame->GetParent()->Type() != LayoutFrameType::GridContainer ||
5240
0
             aPercentageBasis.isSome(),
5241
0
             "grid layout should always pass a percentage basis");
5242
0
5243
0
  const bool horizontalAxis = MOZ_LIKELY(aAxis == eAxisHorizontal);
5244
#ifdef DEBUG_INTRINSIC_WIDTH
5245
  nsFrame::IndentBy(stderr, gNoiseIndent);
5246
  static_cast<nsFrame*>(aFrame)->ListTag(stderr);
5247
  printf_stderr(" %s %s intrinsic size for container:\n",
5248
                aType == MIN_ISIZE ? "min" : "pref",
5249
                horizontalAxis ? "horizontal" : "vertical");
5250
#endif
5251
5252
0
  // If aFrame is a container for font size inflation, then shrink
5253
0
  // wrapping inside of it should not apply font size inflation.
5254
0
  AutoMaybeDisableFontInflation an(aFrame);
5255
0
5256
0
  // We want the size this frame will contribute to the parent's inline-size,
5257
0
  // so we work in the parent's writing mode; but if aFrame is orthogonal to
5258
0
  // its parent, we'll need to look at its BSize instead of min/pref-ISize.
5259
0
  const nsStylePosition* stylePos = aFrame->StylePosition();
5260
0
  StyleBoxSizing boxSizing = stylePos->mBoxSizing;
5261
0
5262
0
  const nsStyleCoord& styleMinISize =
5263
0
    horizontalAxis ? stylePos->mMinWidth : stylePos->mMinHeight;
5264
0
  const nsStyleCoord& styleISize =
5265
0
    (aFlags & MIN_INTRINSIC_ISIZE) ? styleMinISize :
5266
0
    (horizontalAxis ? stylePos->mWidth : stylePos->mHeight);
5267
0
  MOZ_ASSERT(!(aFlags & MIN_INTRINSIC_ISIZE) ||
5268
0
             styleISize.GetUnit() == eStyleUnit_Auto ||
5269
0
             styleISize.GetUnit() == eStyleUnit_Enumerated,
5270
0
             "should only use MIN_INTRINSIC_ISIZE for intrinsic values");
5271
0
  const nsStyleCoord& styleMaxISize =
5272
0
    horizontalAxis ? stylePos->mMaxWidth : stylePos->mMaxHeight;
5273
0
5274
0
  // We build up two values starting with the content box, and then
5275
0
  // adding padding, border and margin.  The result is normally
5276
0
  // |result|.  Then, when we handle 'width', 'min-width', and
5277
0
  // 'max-width', we use the results we've been building in |min| as a
5278
0
  // minimum, overriding 'min-width'.  This ensures two things:
5279
0
  //   * that we don't let a value of 'box-sizing' specifying a width
5280
0
  //     smaller than the padding/border inside the box-sizing box give
5281
0
  //     a content width less than zero
5282
0
  //   * that we prevent tables from becoming smaller than their
5283
0
  //     intrinsic minimum width
5284
0
  nscoord result = 0, min = 0;
5285
0
5286
0
  nscoord maxISize;
5287
0
  bool haveFixedMaxISize = GetAbsoluteCoord(styleMaxISize, maxISize);
5288
0
  nscoord minISize;
5289
0
5290
0
  // Treat "min-width: auto" as 0.
5291
0
  bool haveFixedMinISize;
5292
0
  if (eStyleUnit_Auto == styleMinISize.GetUnit()) {
5293
0
    // NOTE: Technically, "auto" is supposed to behave like "min-content" on
5294
0
    // flex items. However, we don't need to worry about that here, because
5295
0
    // flex items' min-sizes are intentionally ignored until the flex
5296
0
    // container explicitly considers them during space distribution.
5297
0
    minISize = 0;
5298
0
    haveFixedMinISize = true;
5299
0
  } else {
5300
0
    haveFixedMinISize = GetAbsoluteCoord(styleMinISize, minISize);
5301
0
  }
5302
0
5303
0
  PhysicalAxis ourInlineAxis =
5304
0
    aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
5305
0
  const bool isInlineAxis = aAxis == ourInlineAxis;
5306
0
  // If we have a specified width (or a specified 'min-width' greater
5307
0
  // than the specified 'max-width', which works out to the same thing),
5308
0
  // don't even bother getting the frame's intrinsic width, because in
5309
0
  // this case GetAbsoluteCoord(styleISize, w) will always succeed, so
5310
0
  // we'll never need the intrinsic dimensions.
5311
0
  if (styleISize.GetUnit() == eStyleUnit_Enumerated &&
5312
0
      (styleISize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
5313
0
       styleISize.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) {
5314
0
    // -moz-fit-content and -moz-available enumerated widths compute intrinsic
5315
0
    // widths just like auto.
5316
0
    // For -moz-max-content and -moz-min-content, we handle them like
5317
0
    // specified widths, but ignore box-sizing.
5318
0
    boxSizing = StyleBoxSizing::Content;
5319
0
  } else if (!styleISize.ConvertsToLength() &&
5320
0
             !(haveFixedMinISize && haveFixedMaxISize && maxISize <= minISize)) {
5321
#ifdef DEBUG_INTRINSIC_WIDTH
5322
    ++gNoiseIndent;
5323
#endif
5324
0
    if (MOZ_UNLIKELY(!isInlineAxis)) {
5325
0
      IntrinsicSize intrinsicSize = aFrame->GetIntrinsicSize();
5326
0
      const nsStyleCoord intrinsicBCoord =
5327
0
        horizontalAxis ? intrinsicSize.width : intrinsicSize.height;
5328
0
      if (intrinsicBCoord.GetUnit() == eStyleUnit_Coord) {
5329
0
        result = intrinsicBCoord.GetCoordValue();
5330
0
      } else {
5331
0
        // We don't have an intrinsic bsize and we need aFrame's block-dir size.
5332
0
        if (aFlags & BAIL_IF_REFLOW_NEEDED) {
5333
0
          return NS_INTRINSIC_WIDTH_UNKNOWN;
5334
0
        }
5335
0
        // XXX Unfortunately, we probably don't know this yet, so this is wrong...
5336
0
        // but it's not clear what we should do. If aFrame's inline size hasn't
5337
0
        // been determined yet, we can't necessarily figure out its block size
5338
0
        // either. For now, authors who put orthogonal elements into things like
5339
0
        // buttons or table cells may have to explicitly provide sizes rather
5340
0
        // than expecting intrinsic sizing to work "perfectly" in underspecified
5341
0
        // cases.
5342
0
        result = aFrame->BSize();
5343
0
      }
5344
0
    } else {
5345
0
      result = aType == MIN_ISIZE
5346
0
               ? aFrame->GetMinISize(aRenderingContext)
5347
0
               : aFrame->GetPrefISize(aRenderingContext);
5348
0
    }
5349
#ifdef DEBUG_INTRINSIC_WIDTH
5350
    --gNoiseIndent;
5351
    nsFrame::IndentBy(stderr, gNoiseIndent);
5352
    static_cast<nsFrame*>(aFrame)->ListTag(stderr);
5353
    printf_stderr(" %s %s intrinsic size from frame is %d.\n",
5354
                  aType == MIN_ISIZE ? "min" : "pref",
5355
                  horizontalAxis ? "horizontal" : "vertical",
5356
                  result);
5357
#endif
5358
5359
0
    // Handle elements with an intrinsic ratio (or size) and a specified
5360
0
    // height, min-height, or max-height.
5361
0
    // NOTE: We treat "min-height:auto" as "0" for the purpose of this code,
5362
0
    // since that's what it means in all cases except for on flex items -- and
5363
0
    // even there, we're supposed to ignore it (i.e. treat it as 0) until the
5364
0
    // flex container explicitly considers it.
5365
0
    const nsStyleCoord& styleBSize =
5366
0
      horizontalAxis ? stylePos->mHeight : stylePos->mWidth;
5367
0
    const nsStyleCoord& styleMinBSize =
5368
0
      horizontalAxis ? stylePos->mMinHeight : stylePos->mMinWidth;
5369
0
    const nsStyleCoord& styleMaxBSize =
5370
0
      horizontalAxis ? stylePos->mMaxHeight : stylePos->mMaxWidth;
5371
0
5372
0
    if (styleBSize.GetUnit() != eStyleUnit_Auto ||
5373
0
        !(styleMinBSize.GetUnit() == eStyleUnit_Auto ||
5374
0
          (styleMinBSize.GetUnit() == eStyleUnit_Coord &&
5375
0
           styleMinBSize.GetCoordValue() == 0)) ||
5376
0
        styleMaxBSize.GetUnit() != eStyleUnit_None) {
5377
0
5378
0
      nsSize ratio(aFrame->GetIntrinsicRatio());
5379
0
      nscoord ratioISize = (horizontalAxis ? ratio.width  : ratio.height);
5380
0
      nscoord ratioBSize = (horizontalAxis ? ratio.height : ratio.width);
5381
0
      if (ratioBSize != 0) {
5382
0
        AddStateBitToAncestors(aFrame,
5383
0
            NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
5384
0
5385
0
        nscoord bSizeTakenByBoxSizing =
5386
0
          GetDefiniteSizeTakenByBoxSizing(boxSizing, aFrame, !isInlineAxis,
5387
0
                                          aFlags & IGNORE_PADDING,
5388
0
                                          aPercentageBasis);
5389
0
        // NOTE: This is only the minContentSize if we've been passed MIN_INTRINSIC_ISIZE
5390
0
        // (which is fine, because this should only be used inside a check for that flag).
5391
0
        nscoord minContentSize = result;
5392
0
        nscoord h;
5393
0
        if (GetDefiniteSize(styleBSize, aFrame, !isInlineAxis, aPercentageBasis, &h) ||
5394
0
            (aPercentageBasis.isNothing() &&
5395
0
             GetPercentBSize(styleBSize, aFrame, horizontalAxis, h))) {
5396
0
          h = std::max(0, h - bSizeTakenByBoxSizing);
5397
0
          result = NSCoordMulDiv(h, ratioISize, ratioBSize);
5398
0
        }
5399
0
5400
0
        if (GetDefiniteSize(styleMaxBSize, aFrame, !isInlineAxis, aPercentageBasis, &h) ||
5401
0
            (aPercentageBasis.isNothing() &&
5402
0
             GetPercentBSize(styleMaxBSize, aFrame, horizontalAxis, h))) {
5403
0
          h = std::max(0, h - bSizeTakenByBoxSizing);
5404
0
          nscoord maxISize = NSCoordMulDiv(h, ratioISize, ratioBSize);
5405
0
          if (maxISize < result) {
5406
0
            result = maxISize;
5407
0
          }
5408
0
          if (maxISize < minContentSize) {
5409
0
            minContentSize = maxISize;
5410
0
          }
5411
0
        }
5412
0
5413
0
        if (GetDefiniteSize(styleMinBSize, aFrame, !isInlineAxis, aPercentageBasis, &h) ||
5414
0
            (aPercentageBasis.isNothing() &&
5415
0
             GetPercentBSize(styleMinBSize, aFrame, horizontalAxis, h))) {
5416
0
          h = std::max(0, h - bSizeTakenByBoxSizing);
5417
0
          nscoord minISize = NSCoordMulDiv(h, ratioISize, ratioBSize);
5418
0
          if (minISize > result) {
5419
0
            result = minISize;
5420
0
          }
5421
0
          if (minISize > minContentSize) {
5422
0
            minContentSize = minISize;
5423
0
          }
5424
0
        }
5425
0
        if (MOZ_UNLIKELY(aFlags & nsLayoutUtils::MIN_INTRINSIC_ISIZE)) {
5426
0
          // This is the 'min-width/height:auto' "transferred size" piece of:
5427
0
          // https://www.w3.org/TR/css-flexbox-1/#min-width-automatic-minimum-size
5428
0
          // https://drafts.csswg.org/css-grid/#min-size-auto
5429
0
          result = std::min(result, minContentSize);
5430
0
        }
5431
0
      }
5432
0
    }
5433
0
  }
5434
0
5435
0
  if (aFrame->IsTableFrame()) {
5436
0
    // Tables can't shrink smaller than their intrinsic minimum width,
5437
0
    // no matter what.
5438
0
    min = aFrame->GetMinISize(aRenderingContext);
5439
0
  }
5440
0
5441
0
  nscoord pmPercentageBasis = NS_UNCONSTRAINEDSIZE;
5442
0
  if (aPercentageBasis.isSome()) {
5443
0
    // The padding/margin percentage basis is the inline-size in the parent's
5444
0
    // writing-mode.
5445
0
    auto childWM = aFrame->GetWritingMode();
5446
0
    pmPercentageBasis =
5447
0
      aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM) ?
5448
0
        aPercentageBasis->BSize(childWM) :
5449
0
        aPercentageBasis->ISize(childWM);
5450
0
  }
5451
0
  nsIFrame::IntrinsicISizeOffsetData offsets =
5452
0
    MOZ_LIKELY(isInlineAxis) ? aFrame->IntrinsicISizeOffsets(pmPercentageBasis)
5453
0
                             : aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
5454
0
  nscoord contentBoxSize = result;
5455
0
  result = AddIntrinsicSizeOffset(aRenderingContext, aFrame, offsets, aType,
5456
0
                                  boxSizing, result, min, styleISize,
5457
0
                                  haveFixedMinISize ? &minISize : nullptr,
5458
0
                                  styleMinISize,
5459
0
                                  haveFixedMaxISize ? &maxISize : nullptr,
5460
0
                                  styleMaxISize,
5461
0
                                  aFlags, aAxis);
5462
0
  nscoord overflow = result - aMarginBoxMinSizeClamp;
5463
0
  if (MOZ_UNLIKELY(overflow > 0)) {
5464
0
    nscoord newContentBoxSize = std::max(nscoord(0), contentBoxSize - overflow);
5465
0
    result -= contentBoxSize - newContentBoxSize;
5466
0
  }
5467
0
5468
#ifdef DEBUG_INTRINSIC_WIDTH
5469
  nsFrame::IndentBy(stderr, gNoiseIndent);
5470
  static_cast<nsFrame*>(aFrame)->ListTag(stderr);
5471
  printf_stderr(" %s %s intrinsic size for container is %d twips.\n",
5472
                aType == MIN_ISIZE ? "min" : "pref",
5473
                horizontalAxis ? "horizontal" : "vertical",
5474
                result);
5475
#endif
5476
5477
0
  return result;
5478
0
}
5479
5480
/* static */ nscoord
5481
nsLayoutUtils::IntrinsicForContainer(gfxContext* aRenderingContext,
5482
                                     nsIFrame* aFrame,
5483
                                     IntrinsicISizeType aType,
5484
                                     uint32_t aFlags)
5485
0
{
5486
0
  MOZ_ASSERT(aFrame && aFrame->GetParent());
5487
0
  // We want the size aFrame will contribute to its parent's inline-size.
5488
0
  PhysicalAxis axis =
5489
0
    aFrame->GetParent()->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
5490
0
  return IntrinsicForAxis(axis, aRenderingContext, aFrame, aType, Nothing(), aFlags);
5491
0
}
5492
5493
/* static */ nscoord
5494
nsLayoutUtils::MinSizeContributionForAxis(PhysicalAxis       aAxis,
5495
                                          gfxContext*        aRC,
5496
                                          nsIFrame*          aFrame,
5497
                                          IntrinsicISizeType aType,
5498
                                          const LogicalSize& aPercentageBasis,
5499
                                          uint32_t           aFlags)
5500
0
{
5501
0
  MOZ_ASSERT(aFrame);
5502
0
  MOZ_ASSERT(aFrame->IsFlexOrGridItem(),
5503
0
             "only grid/flex items have this behavior currently");
5504
0
5505
#ifdef DEBUG_INTRINSIC_WIDTH
5506
  nsFrame::IndentBy(stderr, gNoiseIndent);
5507
  static_cast<nsFrame*>(aFrame)->ListTag(stderr);
5508
  printf_stderr(" %s min-isize for %s WM:\n",
5509
                aType == MIN_ISIZE ? "min" : "pref",
5510
                aWM.IsVertical() ? "vertical" : "horizontal");
5511
#endif
5512
5513
0
  // Note: this method is only meant for grid/flex items.
5514
0
  const nsStylePosition* const stylePos = aFrame->StylePosition();
5515
0
  const nsStyleCoord* style = aAxis == eAxisHorizontal ? &stylePos->mMinWidth
5516
0
                                                       : &stylePos->mMinHeight;
5517
0
  nscoord minSize;
5518
0
  nscoord* fixedMinSize = nullptr;
5519
0
  auto minSizeUnit = style->GetUnit();
5520
0
  if (minSizeUnit == eStyleUnit_Auto) {
5521
0
    if (aFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) {
5522
0
      style = aAxis == eAxisHorizontal ? &stylePos->mWidth
5523
0
                                       : &stylePos->mHeight;
5524
0
      if (GetAbsoluteCoord(*style, minSize)) {
5525
0
        // We have a definite width/height.  This is the "specified size" in:
5526
0
        // https://drafts.csswg.org/css-grid/#min-size-auto
5527
0
        fixedMinSize = &minSize;
5528
0
      } else if (::IsReplacedBoxResolvedAgainstZero(aFrame, *style,
5529
0
                     aAxis == eAxisHorizontal ? stylePos->mMaxWidth
5530
0
                                              : stylePos->mMaxHeight)) {
5531
0
        // XXX bug 1463700: this doesn't handle calc() according to spec
5532
0
        minSize = 0;
5533
0
        fixedMinSize = &minSize;
5534
0
      }
5535
0
      // fall through - the caller will have to deal with "transferred size"
5536
0
    } else {
5537
0
      // min-[width|height]:auto with overflow != visible computes to zero.
5538
0
      minSize = 0;
5539
0
      fixedMinSize = &minSize;
5540
0
    }
5541
0
  } else if (GetAbsoluteCoord(*style, minSize)) {
5542
0
    fixedMinSize = &minSize;
5543
0
  } else if (minSizeUnit != eStyleUnit_Enumerated) {
5544
0
    MOZ_ASSERT(style->HasPercent());
5545
0
    minSize = 0;
5546
0
    fixedMinSize = &minSize;
5547
0
  }
5548
0
5549
0
  if (!fixedMinSize) {
5550
0
    // Let the caller deal with the "content size" cases.
5551
#ifdef DEBUG_INTRINSIC_WIDTH
5552
    nsFrame::IndentBy(stderr, gNoiseIndent);
5553
    static_cast<nsFrame*>(aFrame)->ListTag(stderr);
5554
    printf_stderr(" %s min-isize is indefinite.\n",
5555
                  aType == MIN_ISIZE ? "min" : "pref");
5556
#endif
5557
0
    return NS_UNCONSTRAINEDSIZE;
5558
0
  }
5559
0
5560
0
  // If aFrame is a container for font size inflation, then shrink
5561
0
  // wrapping inside of it should not apply font size inflation.
5562
0
  AutoMaybeDisableFontInflation an(aFrame);
5563
0
5564
0
  // The padding/margin percentage basis is the inline-size in the parent's
5565
0
  // writing-mode.
5566
0
  auto childWM = aFrame->GetWritingMode();
5567
0
  nscoord pmPercentageBasis =
5568
0
    aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM) ?
5569
0
      aPercentageBasis.BSize(childWM) :
5570
0
      aPercentageBasis.ISize(childWM);
5571
0
  PhysicalAxis ourInlineAxis = childWM.PhysicalAxis(eLogicalAxisInline);
5572
0
  nsIFrame::IntrinsicISizeOffsetData offsets =
5573
0
    ourInlineAxis == aAxis ? aFrame->IntrinsicISizeOffsets(pmPercentageBasis)
5574
0
                           : aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
5575
0
  nscoord result = 0;
5576
0
  nscoord min = 0;
5577
0
  const nsStyleCoord& maxISize =
5578
0
    aAxis == eAxisHorizontal ? stylePos->mMaxWidth : stylePos->mMaxHeight;
5579
0
  result = AddIntrinsicSizeOffset(aRC, aFrame, offsets, aType,
5580
0
                                  stylePos->mBoxSizing,
5581
0
                                  result, min, *style, fixedMinSize,
5582
0
                                  *style, nullptr, maxISize, aFlags, aAxis);
5583
0
5584
#ifdef DEBUG_INTRINSIC_WIDTH
5585
  nsFrame::IndentBy(stderr, gNoiseIndent);
5586
  static_cast<nsFrame*>(aFrame)->ListTag(stderr);
5587
  printf_stderr(" %s min-isize is %d twips.\n",
5588
         aType == MIN_ISIZE ? "min" : "pref", result);
5589
#endif
5590
5591
0
  return result;
5592
0
}
5593
5594
/* static */ nscoord
5595
nsLayoutUtils::ComputeCBDependentValue(nscoord aPercentBasis,
5596
                                       const nsStyleCoord& aCoord)
5597
0
{
5598
0
  NS_WARNING_ASSERTION(
5599
0
    aPercentBasis != NS_UNCONSTRAINEDSIZE,
5600
0
    "have unconstrained width or height; this should only result from very "
5601
0
    "large sizes, not attempts at intrinsic size calculation");
5602
0
5603
0
  if (aCoord.IsCoordPercentCalcUnit()) {
5604
0
    return aCoord.ComputeCoordPercentCalc(aPercentBasis);
5605
0
  }
5606
0
  NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
5607
0
               aCoord.GetUnit() == eStyleUnit_Auto,
5608
0
               "unexpected width value");
5609
0
  return 0;
5610
0
}
5611
5612
/* static */ nscoord
5613
nsLayoutUtils::ComputeBSizeDependentValue(
5614
                 nscoord              aContainingBlockBSize,
5615
                 const nsStyleCoord&  aCoord)
5616
0
{
5617
0
  // XXXldb Some callers explicitly check aContainingBlockBSize
5618
0
  // against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or
5619
0
  // calc()s containing percents before calling this function.
5620
0
  // However, it would be much more likely to catch problems without
5621
0
  // the unit conditions.
5622
0
  // XXXldb Many callers pass a non-'auto' containing block height when
5623
0
  // according to CSS2.1 they should be passing 'auto'.
5624
0
  MOZ_ASSERT(NS_AUTOHEIGHT != aContainingBlockBSize ||
5625
0
             !aCoord.HasPercent(),
5626
0
             "unexpected containing block block-size");
5627
0
5628
0
  if (aCoord.IsCoordPercentCalcUnit()) {
5629
0
    return aCoord.ComputeCoordPercentCalc(aContainingBlockBSize);
5630
0
  }
5631
0
5632
0
  NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
5633
0
               aCoord.GetUnit() == eStyleUnit_Auto,
5634
0
               "unexpected block-size value");
5635
0
  return 0;
5636
0
}
5637
5638
/* static */ void
5639
nsLayoutUtils::MarkDescendantsDirty(nsIFrame *aSubtreeRoot)
5640
0
{
5641
0
  AutoTArray<nsIFrame*, 4> subtrees;
5642
0
  subtrees.AppendElement(aSubtreeRoot);
5643
0
5644
0
  // dirty descendants, iterating over subtrees that may include
5645
0
  // additional subtrees associated with placeholders
5646
0
  do {
5647
0
    nsIFrame *subtreeRoot = subtrees.PopLastElement();
5648
0
5649
0
    // Mark all descendants dirty (using an nsTArray stack rather than
5650
0
    // recursion).
5651
0
    // Note that ReflowInput::InitResizeFlags has some similar
5652
0
    // code; see comments there for how and why it differs.
5653
0
    AutoTArray<nsIFrame*, 32> stack;
5654
0
    stack.AppendElement(subtreeRoot);
5655
0
5656
0
    do {
5657
0
      nsIFrame *f = stack.PopLastElement();
5658
0
5659
0
      f->MarkIntrinsicISizesDirty();
5660
0
5661
0
      if (f->IsPlaceholderFrame()) {
5662
0
        nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
5663
0
        if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
5664
0
          // We have another distinct subtree we need to mark.
5665
0
          subtrees.AppendElement(oof);
5666
0
        }
5667
0
      }
5668
0
5669
0
      nsIFrame::ChildListIterator lists(f);
5670
0
      for (; !lists.IsDone(); lists.Next()) {
5671
0
        nsFrameList::Enumerator childFrames(lists.CurrentList());
5672
0
        for (; !childFrames.AtEnd(); childFrames.Next()) {
5673
0
          nsIFrame* kid = childFrames.get();
5674
0
          stack.AppendElement(kid);
5675
0
        }
5676
0
      }
5677
0
    } while (stack.Length() != 0);
5678
0
  } while (subtrees.Length() != 0);
5679
0
}
5680
5681
/* static */
5682
void
5683
nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame* aFrame)
5684
0
{
5685
0
  AutoTArray<nsIFrame*, 32> stack;
5686
0
  stack.AppendElement(aFrame);
5687
0
5688
0
  do {
5689
0
    nsIFrame* f = stack.PopLastElement();
5690
0
5691
0
    if (!f->HasAnyStateBits(
5692
0
        NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
5693
0
      continue;
5694
0
    }
5695
0
    f->MarkIntrinsicISizesDirty();
5696
0
5697
0
    for (nsIFrame::ChildListIterator lists(f); !lists.IsDone(); lists.Next()) {
5698
0
      for (nsIFrame* kid : lists.CurrentList()) {
5699
0
        stack.AppendElement(kid);
5700
0
      }
5701
0
    }
5702
0
  } while (stack.Length() != 0);
5703
0
}
5704
5705
nsSize
5706
nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth, nscoord minHeight,
5707
                                                      nscoord maxWidth, nscoord maxHeight,
5708
                                                      nscoord tentWidth, nscoord tentHeight)
5709
0
{
5710
0
  // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:
5711
0
5712
0
  if (minWidth > maxWidth)
5713
0
    maxWidth = minWidth;
5714
0
  if (minHeight > maxHeight)
5715
0
    maxHeight = minHeight;
5716
0
5717
0
  nscoord heightAtMaxWidth, heightAtMinWidth,
5718
0
          widthAtMaxHeight, widthAtMinHeight;
5719
0
5720
0
  if (tentWidth > 0) {
5721
0
    heightAtMaxWidth = NSCoordMulDiv(maxWidth, tentHeight, tentWidth);
5722
0
    if (heightAtMaxWidth < minHeight)
5723
0
      heightAtMaxWidth = minHeight;
5724
0
    heightAtMinWidth = NSCoordMulDiv(minWidth, tentHeight, tentWidth);
5725
0
    if (heightAtMinWidth > maxHeight)
5726
0
      heightAtMinWidth = maxHeight;
5727
0
  } else {
5728
0
    heightAtMaxWidth = heightAtMinWidth = NS_CSS_MINMAX(tentHeight, minHeight, maxHeight);
5729
0
  }
5730
0
5731
0
  if (tentHeight > 0) {
5732
0
    widthAtMaxHeight = NSCoordMulDiv(maxHeight, tentWidth, tentHeight);
5733
0
    if (widthAtMaxHeight < minWidth)
5734
0
      widthAtMaxHeight = minWidth;
5735
0
    widthAtMinHeight = NSCoordMulDiv(minHeight, tentWidth, tentHeight);
5736
0
    if (widthAtMinHeight > maxWidth)
5737
0
      widthAtMinHeight = maxWidth;
5738
0
  } else {
5739
0
    widthAtMaxHeight = widthAtMinHeight = NS_CSS_MINMAX(tentWidth, minWidth, maxWidth);
5740
0
  }
5741
0
5742
0
  // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :
5743
0
5744
0
  nscoord width, height;
5745
0
5746
0
  if (tentWidth > maxWidth) {
5747
0
    if (tentHeight > maxHeight) {
5748
0
      if (int64_t(maxWidth) * int64_t(tentHeight) <=
5749
0
          int64_t(maxHeight) * int64_t(tentWidth)) {
5750
0
        width = maxWidth;
5751
0
        height = heightAtMaxWidth;
5752
0
      } else {
5753
0
        width = widthAtMaxHeight;
5754
0
        height = maxHeight;
5755
0
      }
5756
0
    } else {
5757
0
      // This also covers "(w > max-width) and (h < min-height)" since in
5758
0
      // that case (max-width/w < 1), and with (h < min-height):
5759
0
      //   max(max-width * h/w, min-height) == min-height
5760
0
      width = maxWidth;
5761
0
      height = heightAtMaxWidth;
5762
0
    }
5763
0
  } else if (tentWidth < minWidth) {
5764
0
    if (tentHeight < minHeight) {
5765
0
      if (int64_t(minWidth) * int64_t(tentHeight) <=
5766
0
          int64_t(minHeight) * int64_t(tentWidth)) {
5767
0
        width = widthAtMinHeight;
5768
0
        height = minHeight;
5769
0
      } else {
5770
0
        width = minWidth;
5771
0
        height = heightAtMinWidth;
5772
0
      }
5773
0
    } else {
5774
0
      // This also covers "(w < min-width) and (h > max-height)" since in
5775
0
      // that case (min-width/w > 1), and with (h > max-height):
5776
0
      //   min(min-width * h/w, max-height) == max-height
5777
0
      width = minWidth;
5778
0
      height = heightAtMinWidth;
5779
0
    }
5780
0
  } else {
5781
0
    if (tentHeight > maxHeight) {
5782
0
      width = widthAtMaxHeight;
5783
0
      height = maxHeight;
5784
0
    } else if (tentHeight < minHeight) {
5785
0
      width = widthAtMinHeight;
5786
0
      height = minHeight;
5787
0
    } else {
5788
0
      width = tentWidth;
5789
0
      height = tentHeight;
5790
0
    }
5791
0
  }
5792
0
5793
0
  return nsSize(width, height);
5794
0
}
5795
5796
/* static */ nscoord
5797
nsLayoutUtils::MinISizeFromInline(nsIFrame* aFrame,
5798
                                  gfxContext* aRenderingContext)
5799
0
{
5800
0
  NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),
5801
0
               "should not be container for font size inflation");
5802
0
5803
0
  nsIFrame::InlineMinISizeData data;
5804
0
  DISPLAY_MIN_INLINE_SIZE(aFrame, data.mPrevLines);
5805
0
  aFrame->AddInlineMinISize(aRenderingContext, &data);
5806
0
  data.ForceBreak();
5807
0
  return data.mPrevLines;
5808
0
}
5809
5810
/* static */ nscoord
5811
nsLayoutUtils::PrefISizeFromInline(nsIFrame* aFrame,
5812
                                   gfxContext* aRenderingContext)
5813
0
{
5814
0
  NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),
5815
0
               "should not be container for font size inflation");
5816
0
5817
0
  nsIFrame::InlinePrefISizeData data;
5818
0
  DISPLAY_PREF_INLINE_SIZE(aFrame, data.mPrevLines);
5819
0
  aFrame->AddInlinePrefISize(aRenderingContext, &data);
5820
0
  data.ForceBreak();
5821
0
  return data.mPrevLines;
5822
0
}
5823
5824
static nscolor
5825
DarkenColor(nscolor aColor)
5826
0
{
5827
0
  uint16_t  hue, sat, value;
5828
0
  uint8_t alpha;
5829
0
5830
0
  // convert the RBG to HSV so we can get the lightness (which is the v)
5831
0
  NS_RGB2HSV(aColor, hue, sat, value, alpha);
5832
0
5833
0
  // The goal here is to send white to black while letting colored
5834
0
  // stuff stay colored... So we adopt the following approach.
5835
0
  // Something with sat = 0 should end up with value = 0.  Something
5836
0
  // with a high sat can end up with a high value and it's ok.... At
5837
0
  // the same time, we don't want to make things lighter.  Do
5838
0
  // something simple, since it seems to work.
5839
0
  if (value > sat) {
5840
0
    value = sat;
5841
0
    // convert this color back into the RGB color space.
5842
0
    NS_HSV2RGB(aColor, hue, sat, value, alpha);
5843
0
  }
5844
0
  return aColor;
5845
0
}
5846
5847
// Check whether we should darken text/decoration colors. We need to do this if
5848
// background images and colors are being suppressed, because that means
5849
// light text will not be visible against the (presumed light-colored) background.
5850
static bool
5851
ShouldDarkenColors(nsPresContext* aPresContext)
5852
0
{
5853
0
  return !aPresContext->GetBackgroundColorDraw() &&
5854
0
         !aPresContext->GetBackgroundImageDraw();
5855
0
}
5856
5857
nscolor
5858
nsLayoutUtils::DarkenColorIfNeeded(nsIFrame* aFrame, nscolor aColor)
5859
0
{
5860
0
  if (ShouldDarkenColors(aFrame->PresContext())) {
5861
0
    return DarkenColor(aColor);
5862
0
  }
5863
0
  return aColor;
5864
0
}
5865
5866
gfxFloat
5867
nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
5868
                                   nscoord aY, nscoord aAscent)
5869
0
{
5870
0
  gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
5871
0
  gfxFloat baseline = gfxFloat(aY) + aAscent;
5872
0
  gfxRect putativeRect(0, baseline/appUnitsPerDevUnit, 1, 1);
5873
0
  if (!aContext->UserToDevicePixelSnapped(putativeRect, true))
5874
0
    return baseline;
5875
0
  return aContext->DeviceToUser(putativeRect.TopLeft()).y * appUnitsPerDevUnit;
5876
0
}
5877
5878
gfxFloat
5879
nsLayoutUtils::GetSnappedBaselineX(nsIFrame* aFrame, gfxContext* aContext,
5880
                                   nscoord aX, nscoord aAscent)
5881
0
{
5882
0
  gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
5883
0
  gfxFloat baseline = gfxFloat(aX) + aAscent;
5884
0
  gfxRect putativeRect(baseline / appUnitsPerDevUnit, 0, 1, 1);
5885
0
  if (!aContext->UserToDevicePixelSnapped(putativeRect, true)) {
5886
0
    return baseline;
5887
0
  }
5888
0
  return aContext->DeviceToUser(putativeRect.TopLeft()).x * appUnitsPerDevUnit;
5889
0
}
5890
5891
// Hard limit substring lengths to 8000 characters ... this lets us statically
5892
// size the cluster buffer array in FindSafeLength
5893
0
#define MAX_GFX_TEXT_BUF_SIZE 8000
5894
5895
static int32_t FindSafeLength(const char16_t *aString, uint32_t aLength,
5896
                              uint32_t aMaxChunkLength)
5897
0
{
5898
0
  if (aLength <= aMaxChunkLength)
5899
0
    return aLength;
5900
0
5901
0
  int32_t len = aMaxChunkLength;
5902
0
5903
0
  // Ensure that we don't break inside a surrogate pair
5904
0
  while (len > 0 && NS_IS_LOW_SURROGATE(aString[len])) {
5905
0
    len--;
5906
0
  }
5907
0
  if (len == 0) {
5908
0
    // We don't want our caller to go into an infinite loop, so don't
5909
0
    // return zero. It's hard to imagine how we could actually get here
5910
0
    // unless there are languages that allow clusters of arbitrary size.
5911
0
    // If there are and someone feeds us a 500+ character cluster, too
5912
0
    // bad.
5913
0
    return aMaxChunkLength;
5914
0
  }
5915
0
  return len;
5916
0
}
5917
5918
static int32_t GetMaxChunkLength(nsFontMetrics& aFontMetrics)
5919
0
{
5920
0
  return std::min(aFontMetrics.GetMaxStringLength(), MAX_GFX_TEXT_BUF_SIZE);
5921
0
}
5922
5923
nscoord
5924
nsLayoutUtils::AppUnitWidthOfString(const char16_t *aString,
5925
                                    uint32_t aLength,
5926
                                    nsFontMetrics& aFontMetrics,
5927
                                    DrawTarget* aDrawTarget)
5928
0
{
5929
0
  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
5930
0
  nscoord width = 0;
5931
0
  while (aLength > 0) {
5932
0
    int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
5933
0
    width += aFontMetrics.GetWidth(aString, len, aDrawTarget);
5934
0
    aLength -= len;
5935
0
    aString += len;
5936
0
  }
5937
0
  return width;
5938
0
}
5939
5940
nscoord
5941
nsLayoutUtils::AppUnitWidthOfStringBidi(const char16_t* aString,
5942
                                        uint32_t aLength,
5943
                                        const nsIFrame* aFrame,
5944
                                        nsFontMetrics& aFontMetrics,
5945
                                        gfxContext& aContext)
5946
0
{
5947
0
  nsPresContext* presContext = aFrame->PresContext();
5948
0
  if (presContext->BidiEnabled()) {
5949
0
    nsBidiLevel level =
5950
0
      nsBidiPresUtils::BidiLevelFromStyle(aFrame->Style());
5951
0
    return nsBidiPresUtils::MeasureTextWidth(aString, aLength, level,
5952
0
                                             presContext, aContext,
5953
0
                                             aFontMetrics);
5954
0
  }
5955
0
  aFontMetrics.SetTextRunRTL(false);
5956
0
  aFontMetrics.SetVertical(aFrame->GetWritingMode().IsVertical());
5957
0
  aFontMetrics.SetTextOrientation(aFrame->StyleVisibility()->mTextOrientation);
5958
0
  return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
5959
0
                                             aContext.GetDrawTarget());
5960
0
}
5961
5962
bool
5963
nsLayoutUtils::StringWidthIsGreaterThan(const nsString& aString,
5964
                                        nsFontMetrics& aFontMetrics,
5965
                                        DrawTarget* aDrawTarget,
5966
                                        nscoord aWidth)
5967
0
{
5968
0
  const char16_t *string = aString.get();
5969
0
  uint32_t length = aString.Length();
5970
0
  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
5971
0
  nscoord width = 0;
5972
0
  while (length > 0) {
5973
0
    int32_t len = FindSafeLength(string, length, maxChunkLength);
5974
0
    width += aFontMetrics.GetWidth(string, len, aDrawTarget);
5975
0
    if (width > aWidth) {
5976
0
      return true;
5977
0
    }
5978
0
    length -= len;
5979
0
    string += len;
5980
0
  }
5981
0
  return false;
5982
0
}
5983
5984
nsBoundingMetrics
5985
nsLayoutUtils::AppUnitBoundsOfString(const char16_t* aString,
5986
                                     uint32_t aLength,
5987
                                     nsFontMetrics& aFontMetrics,
5988
                                     DrawTarget* aDrawTarget)
5989
0
{
5990
0
  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
5991
0
  int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
5992
0
  // Assign directly in the first iteration. This ensures that
5993
0
  // negative ascent/descent can be returned and the left bearing
5994
0
  // is properly initialized.
5995
0
  nsBoundingMetrics totalMetrics =
5996
0
    aFontMetrics.GetBoundingMetrics(aString, len, aDrawTarget);
5997
0
  aLength -= len;
5998
0
  aString += len;
5999
0
6000
0
  while (aLength > 0) {
6001
0
    len = FindSafeLength(aString, aLength, maxChunkLength);
6002
0
    nsBoundingMetrics metrics =
6003
0
      aFontMetrics.GetBoundingMetrics(aString, len, aDrawTarget);
6004
0
    totalMetrics += metrics;
6005
0
    aLength -= len;
6006
0
    aString += len;
6007
0
  }
6008
0
  return totalMetrics;
6009
0
}
6010
6011
void
6012
nsLayoutUtils::DrawString(const nsIFrame*     aFrame,
6013
                          nsFontMetrics&      aFontMetrics,
6014
                          gfxContext*         aContext,
6015
                          const char16_t*     aString,
6016
                          int32_t             aLength,
6017
                          nsPoint             aPoint,
6018
                          ComputedStyle*      aComputedStyle,
6019
                          DrawStringFlags     aFlags)
6020
0
{
6021
0
  nsresult rv = NS_ERROR_FAILURE;
6022
0
6023
0
  // If caller didn't pass a style, use the frame's.
6024
0
  if (!aComputedStyle) {
6025
0
    aComputedStyle = aFrame->Style();
6026
0
  }
6027
0
6028
0
  if (aFlags & DrawStringFlags::eForceHorizontal) {
6029
0
    aFontMetrics.SetVertical(false);
6030
0
  } else {
6031
0
    aFontMetrics.SetVertical(WritingMode(aComputedStyle).IsVertical());
6032
0
  }
6033
0
6034
0
  aFontMetrics.SetTextOrientation(
6035
0
    aComputedStyle->StyleVisibility()->mTextOrientation);
6036
0
6037
0
  nsPresContext* presContext = aFrame->PresContext();
6038
0
  if (presContext->BidiEnabled()) {
6039
0
    nsBidiLevel level =
6040
0
      nsBidiPresUtils::BidiLevelFromStyle(aComputedStyle);
6041
0
    rv = nsBidiPresUtils::RenderText(aString, aLength, level,
6042
0
                                     presContext, *aContext,
6043
0
                                     aContext->GetDrawTarget(), aFontMetrics,
6044
0
                                     aPoint.x, aPoint.y);
6045
0
  }
6046
0
  if (NS_FAILED(rv))
6047
0
  {
6048
0
    aFontMetrics.SetTextRunRTL(false);
6049
0
    DrawUniDirString(aString, aLength, aPoint, aFontMetrics, *aContext);
6050
0
  }
6051
0
}
6052
6053
void
6054
nsLayoutUtils::DrawUniDirString(const char16_t* aString,
6055
                                uint32_t aLength,
6056
                                const nsPoint& aPoint,
6057
                                nsFontMetrics& aFontMetrics,
6058
                                gfxContext& aContext)
6059
0
{
6060
0
  nscoord x = aPoint.x;
6061
0
  nscoord y = aPoint.y;
6062
0
6063
0
  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
6064
0
  if (aLength <= maxChunkLength) {
6065
0
    aFontMetrics.DrawString(aString, aLength, x, y, &aContext,
6066
0
                            aContext.GetDrawTarget());
6067
0
    return;
6068
0
  }
6069
0
6070
0
  bool isRTL = aFontMetrics.GetTextRunRTL();
6071
0
6072
0
  // If we're drawing right to left, we must start at the end.
6073
0
  if (isRTL) {
6074
0
    x += nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
6075
0
                                             aContext.GetDrawTarget());
6076
0
  }
6077
0
6078
0
  while (aLength > 0) {
6079
0
    int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
6080
0
    nscoord width = aFontMetrics.GetWidth(aString, len, aContext.GetDrawTarget());
6081
0
    if (isRTL) {
6082
0
      x -= width;
6083
0
    }
6084
0
    aFontMetrics.DrawString(aString, len, x, y, &aContext,
6085
0
                            aContext.GetDrawTarget());
6086
0
    if (!isRTL) {
6087
0
      x += width;
6088
0
    }
6089
0
    aLength -= len;
6090
0
    aString += len;
6091
0
  }
6092
0
}
6093
6094
/* static */ void
6095
nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
6096
                               gfxContext* aContext,
6097
                               const nsRect& aTextRect,
6098
                               const nsRect& aDirtyRect,
6099
                               const nscolor& aForegroundColor,
6100
                               TextShadowCallback aCallback,
6101
                               void* aCallbackData)
6102
0
{
6103
0
  const nsStyleText* textStyle = aFrame->StyleText();
6104
0
  if (!textStyle->HasTextShadow())
6105
0
    return;
6106
0
6107
0
  // Text shadow happens with the last value being painted at the back,
6108
0
  // ie. it is painted first.
6109
0
  gfxContext* aDestCtx = aContext;
6110
0
  for (uint32_t i = textStyle->mTextShadow->Length(); i > 0; --i) {
6111
0
    nsCSSShadowItem* shadowDetails = textStyle->mTextShadow->ShadowAt(i - 1);
6112
0
    nsPoint shadowOffset(shadowDetails->mXOffset,
6113
0
                         shadowDetails->mYOffset);
6114
0
    nscoord blurRadius = std::max(shadowDetails->mRadius, 0);
6115
0
6116
0
    nsRect shadowRect(aTextRect);
6117
0
    shadowRect.MoveBy(shadowOffset);
6118
0
6119
0
    nsPresContext* presCtx = aFrame->PresContext();
6120
0
    nsContextBoxBlur contextBoxBlur;
6121
0
6122
0
    nscolor shadowColor = shadowDetails->mColor.CalcColor(aForegroundColor);
6123
0
6124
0
    // Webrender just needs the shadow details
6125
0
    if (auto* textDrawer = aContext->GetTextDrawer()) {
6126
0
      wr::Shadow wrShadow;
6127
0
6128
0
      wrShadow.offset = {
6129
0
        presCtx->AppUnitsToFloatDevPixels(shadowDetails->mXOffset),
6130
0
        presCtx->AppUnitsToFloatDevPixels(shadowDetails->mYOffset)
6131
0
      };
6132
0
6133
0
      wrShadow.blur_radius = presCtx->AppUnitsToFloatDevPixels(shadowDetails->mRadius);
6134
0
      wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
6135
0
6136
0
      textDrawer->AppendShadow(wrShadow);
6137
0
      continue;
6138
0
    }
6139
0
6140
0
    gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
6141
0
                                                    presCtx->AppUnitsPerDevPixel(),
6142
0
                                                    aDestCtx, aDirtyRect, nullptr,
6143
0
                                                    nsContextBoxBlur::DISABLE_HARDWARE_ACCELERATION_BLUR);
6144
0
    if (!shadowContext)
6145
0
      continue;
6146
0
6147
0
6148
0
6149
0
    aDestCtx->Save();
6150
0
    aDestCtx->NewPath();
6151
0
    aDestCtx->SetColor(Color::FromABGR(shadowColor));
6152
0
6153
0
    // The callback will draw whatever we want to blur as a shadow.
6154
0
    aCallback(shadowContext, shadowOffset, shadowColor, aCallbackData);
6155
0
6156
0
    contextBoxBlur.DoPaint();
6157
0
    aDestCtx->Restore();
6158
0
  }
6159
0
}
6160
6161
/* static */ nscoord
6162
nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
6163
                                       nscoord        aLineHeight,
6164
                                       bool           aIsInverted)
6165
0
{
6166
0
  nscoord fontAscent = aIsInverted ? aFontMetrics->MaxDescent()
6167
0
                                   : aFontMetrics->MaxAscent();
6168
0
  nscoord fontHeight = aFontMetrics->MaxHeight();
6169
0
6170
0
  nscoord leading = aLineHeight - fontHeight;
6171
0
  return fontAscent + leading/2;
6172
0
}
6173
6174
6175
/* static */ bool
6176
nsLayoutUtils::GetFirstLineBaseline(WritingMode aWritingMode,
6177
                                    const nsIFrame* aFrame, nscoord* aResult)
6178
0
{
6179
0
  LinePosition position;
6180
0
  if (!GetFirstLinePosition(aWritingMode, aFrame, &position))
6181
0
    return false;
6182
0
  *aResult = position.mBaseline;
6183
0
  return true;
6184
0
}
6185
6186
/* static */ bool
6187
nsLayoutUtils::GetFirstLinePosition(WritingMode aWM,
6188
                                    const nsIFrame* aFrame,
6189
                                    LinePosition* aResult)
6190
0
{
6191
0
  if (aFrame->StyleDisplay()->IsContainSize()) {
6192
0
    return false;
6193
0
  }
6194
0
  const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
6195
0
  if (!block) {
6196
0
    // For the first-line baseline we also have to check for a table, and if
6197
0
    // so, use the baseline of its first row.
6198
0
    LayoutFrameType fType = aFrame->Type();
6199
0
    if (fType == LayoutFrameType::TableWrapper ||
6200
0
        fType == LayoutFrameType::FlexContainer ||
6201
0
        fType == LayoutFrameType::GridContainer) {
6202
0
      if ((fType == LayoutFrameType::GridContainer &&
6203
0
           aFrame->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) ||
6204
0
          (fType == LayoutFrameType::FlexContainer &&
6205
0
           aFrame->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) ||
6206
0
          (fType == LayoutFrameType::TableWrapper &&
6207
0
           static_cast<const nsTableWrapperFrame*>(aFrame)->GetRowCount() == 0)) {
6208
0
        // empty grid/flex/table container
6209
0
        aResult->mBStart = 0;
6210
0
        aResult->mBaseline = aFrame->SynthesizeBaselineBOffsetFromBorderBox(aWM,
6211
0
                                       BaselineSharingGroup::eFirst);
6212
0
        aResult->mBEnd = aFrame->BSize(aWM);
6213
0
        return true;
6214
0
      }
6215
0
      aResult->mBStart = 0;
6216
0
      aResult->mBaseline = aFrame->GetLogicalBaseline(aWM);
6217
0
      // This is what we want for the list bullet caller; not sure if
6218
0
      // other future callers will want the same.
6219
0
      aResult->mBEnd = aFrame->BSize(aWM);
6220
0
      return true;
6221
0
    }
6222
0
6223
0
    // For first-line baselines, we have to consider scroll frames.
6224
0
    if (fType == LayoutFrameType::Scroll) {
6225
0
      nsIScrollableFrame *sFrame = do_QueryFrame(const_cast<nsIFrame*>(aFrame));
6226
0
      if (!sFrame) {
6227
0
        MOZ_ASSERT_UNREACHABLE("not scroll frame");
6228
0
      }
6229
0
      LinePosition kidPosition;
6230
0
      if (GetFirstLinePosition(aWM,
6231
0
                               sFrame->GetScrolledFrame(), &kidPosition)) {
6232
0
        // Consider only the border and padding that contributes to the
6233
0
        // kid's position, not the scrolling, so we get the initial
6234
0
        // position.
6235
0
        *aResult = kidPosition +
6236
0
          aFrame->GetLogicalUsedBorderAndPadding(aWM).BStart(aWM);
6237
0
        return true;
6238
0
      }
6239
0
      return false;
6240
0
    }
6241
0
6242
0
    if (fType == LayoutFrameType::FieldSet) {
6243
0
      LinePosition kidPosition;
6244
0
      nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
6245
0
      // kid might be a legend frame here, but that's ok.
6246
0
      if (GetFirstLinePosition(aWM, kid, &kidPosition)) {
6247
0
        *aResult = kidPosition +
6248
0
          kid->GetLogicalNormalPosition(aWM, aFrame->GetSize()).B(aWM);
6249
0
        return true;
6250
0
      }
6251
0
      return false;
6252
0
    }
6253
0
6254
0
    // No baseline.
6255
0
    return false;
6256
0
  }
6257
0
6258
0
  for (nsBlockFrame::ConstLineIterator line = block->LinesBegin(),
6259
0
                                       line_end = block->LinesEnd();
6260
0
       line != line_end; ++line) {
6261
0
    if (line->IsBlock()) {
6262
0
      nsIFrame *kid = line->mFirstChild;
6263
0
      LinePosition kidPosition;
6264
0
      if (GetFirstLinePosition(aWM, kid, &kidPosition)) {
6265
0
        //XXX Not sure if this is the correct value to use for container
6266
0
        //    width here. It will only be used in vertical-rl layout,
6267
0
        //    which we don't have full support and testing for yet.
6268
0
        const nsSize& containerSize = line->mContainerSize;
6269
0
        *aResult = kidPosition +
6270
0
                   kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
6271
0
        return true;
6272
0
      }
6273
0
    } else {
6274
0
      // XXX Is this the right test?  We have some bogus empty lines
6275
0
      // floating around, but IsEmpty is perhaps too weak.
6276
0
      if (line->BSize() != 0 || !line->IsEmpty()) {
6277
0
        nscoord bStart = line->BStart();
6278
0
        aResult->mBStart = bStart;
6279
0
        aResult->mBaseline = bStart + line->GetLogicalAscent();
6280
0
        aResult->mBEnd = bStart + line->BSize();
6281
0
        return true;
6282
0
      }
6283
0
    }
6284
0
  }
6285
0
  return false;
6286
0
}
6287
6288
/* static */ bool
6289
nsLayoutUtils::GetLastLineBaseline(WritingMode aWM,
6290
                                   const nsIFrame* aFrame, nscoord* aResult)
6291
0
{
6292
0
  if (aFrame->StyleDisplay()->IsContainSize()) {
6293
0
    return false;
6294
0
  }
6295
0
6296
0
  const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
6297
0
  if (!block)
6298
0
    // No baseline.  (We intentionally don't descend into scroll frames.)
6299
0
    return false;
6300
0
6301
0
  for (nsBlockFrame::ConstReverseLineIterator line = block->LinesRBegin(),
6302
0
                                              line_end = block->LinesREnd();
6303
0
       line != line_end; ++line) {
6304
0
    if (line->IsBlock()) {
6305
0
      nsIFrame *kid = line->mFirstChild;
6306
0
      nscoord kidBaseline;
6307
0
      const nsSize& containerSize = line->mContainerSize;
6308
0
      if (GetLastLineBaseline(aWM, kid, &kidBaseline)) {
6309
0
        // Ignore relative positioning for baseline calculations
6310
0
        *aResult = kidBaseline +
6311
0
          kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
6312
0
        return true;
6313
0
      } else if (kid->IsScrollFrame()) {
6314
0
        // Defer to nsFrame::GetLogicalBaseline (which synthesizes a baseline
6315
0
        // from the margin-box).
6316
0
        kidBaseline = kid->GetLogicalBaseline(aWM);
6317
0
        *aResult = kidBaseline +
6318
0
          kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
6319
0
        return true;
6320
0
      }
6321
0
    } else {
6322
0
      // XXX Is this the right test?  We have some bogus empty lines
6323
0
      // floating around, but IsEmpty is perhaps too weak.
6324
0
      if (line->BSize() != 0 || !line->IsEmpty()) {
6325
0
        *aResult = line->BStart() + line->GetLogicalAscent();
6326
0
        return true;
6327
0
      }
6328
0
    }
6329
0
  }
6330
0
  return false;
6331
0
}
6332
6333
static nscoord
6334
CalculateBlockContentBEnd(WritingMode aWM, nsBlockFrame* aFrame)
6335
0
{
6336
0
  MOZ_ASSERT(aFrame, "null ptr");
6337
0
6338
0
  nscoord contentBEnd = 0;
6339
0
6340
0
  for (nsBlockFrame::LineIterator line = aFrame->LinesBegin(),
6341
0
                                  line_end = aFrame->LinesEnd();
6342
0
       line != line_end; ++line) {
6343
0
    if (line->IsBlock()) {
6344
0
      nsIFrame* child = line->mFirstChild;
6345
0
      const nsSize& containerSize = line->mContainerSize;
6346
0
      nscoord offset =
6347
0
        child->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
6348
0
      contentBEnd =
6349
0
        std::max(contentBEnd,
6350
0
                 nsLayoutUtils::CalculateContentBEnd(aWM, child) + offset);
6351
0
    }
6352
0
    else {
6353
0
      contentBEnd = std::max(contentBEnd, line->BEnd());
6354
0
    }
6355
0
  }
6356
0
  return contentBEnd;
6357
0
}
6358
6359
/* static */ nscoord
6360
nsLayoutUtils::CalculateContentBEnd(WritingMode aWM, nsIFrame* aFrame)
6361
0
{
6362
0
  MOZ_ASSERT(aFrame, "null ptr");
6363
0
6364
0
  nscoord contentBEnd = aFrame->BSize(aWM);
6365
0
6366
0
  // We want scrollable overflow rather than visual because this
6367
0
  // calculation is intended to affect layout.
6368
0
  LogicalSize overflowSize(aWM, aFrame->GetScrollableOverflowRect().Size());
6369
0
  if (overflowSize.BSize(aWM) > contentBEnd) {
6370
0
    nsIFrame::ChildListIDs skip(nsIFrame::kOverflowList |
6371
0
                                nsIFrame::kExcessOverflowContainersList |
6372
0
                                nsIFrame::kOverflowOutOfFlowList);
6373
0
    nsBlockFrame* blockFrame = GetAsBlock(aFrame);
6374
0
    if (blockFrame) {
6375
0
      contentBEnd =
6376
0
        std::max(contentBEnd, CalculateBlockContentBEnd(aWM, blockFrame));
6377
0
      skip |= nsIFrame::kPrincipalList;
6378
0
    }
6379
0
    nsIFrame::ChildListIterator lists(aFrame);
6380
0
    for (; !lists.IsDone(); lists.Next()) {
6381
0
      if (!skip.Contains(lists.CurrentID())) {
6382
0
        nsFrameList::Enumerator childFrames(lists.CurrentList());
6383
0
        for (; !childFrames.AtEnd(); childFrames.Next()) {
6384
0
          nsIFrame* child = childFrames.get();
6385
0
          nscoord offset =
6386
0
            child->GetLogicalNormalPosition(aWM,
6387
0
                                            aFrame->GetSize()).B(aWM);
6388
0
          contentBEnd = std::max(contentBEnd,
6389
0
                                 CalculateContentBEnd(aWM, child) + offset);
6390
0
        }
6391
0
      }
6392
0
    }
6393
0
  }
6394
0
  return contentBEnd;
6395
0
}
6396
6397
/* static */ nsIFrame*
6398
nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame)
6399
0
{
6400
0
  nsIFrame* layer;
6401
0
  for (layer = aFrame; layer; layer = layer->GetParent()) {
6402
0
    if (layer->IsAbsPosContainingBlock() ||
6403
0
        (layer->GetParent() && layer->GetParent()->IsScrollFrame()))
6404
0
      break;
6405
0
  }
6406
0
  if (layer)
6407
0
    return layer;
6408
0
  return aFrame->PresShell()->GetRootFrame();
6409
0
}
6410
6411
SamplingFilter
6412
nsLayoutUtils::GetSamplingFilterForFrame(nsIFrame* aForFrame)
6413
0
{
6414
0
  SamplingFilter defaultFilter = SamplingFilter::GOOD;
6415
0
  ComputedStyle *sc;
6416
0
  if (nsCSSRendering::IsCanvasFrame(aForFrame)) {
6417
0
    nsCSSRendering::FindBackground(aForFrame, &sc);
6418
0
  } else {
6419
0
    sc = aForFrame->Style();
6420
0
  }
6421
0
6422
0
  switch (sc->StyleVisibility()->mImageRendering) {
6423
0
  case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED:
6424
0
    return SamplingFilter::POINT;
6425
0
  case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY:
6426
0
    return SamplingFilter::LINEAR;
6427
0
  case NS_STYLE_IMAGE_RENDERING_CRISPEDGES:
6428
0
    return SamplingFilter::POINT;
6429
0
  default:
6430
0
    return defaultFilter;
6431
0
  }
6432
0
}
6433
6434
/**
6435
 * Given an image being drawn into an appunit coordinate system, and
6436
 * a point in that coordinate system, map the point back into image
6437
 * pixel space.
6438
 * @param aSize the size of the image, in pixels
6439
 * @param aDest the rectangle that the image is being mapped into
6440
 * @param aPt a point in the same coordinate system as the rectangle
6441
 */
6442
static gfxPoint
6443
MapToFloatImagePixels(const gfxSize& aSize,
6444
                      const gfxRect& aDest, const gfxPoint& aPt)
6445
0
{
6446
0
  return gfxPoint(((aPt.x - aDest.X())*aSize.width)/aDest.Width(),
6447
0
                  ((aPt.y - aDest.Y())*aSize.height)/aDest.Height());
6448
0
}
6449
6450
/**
6451
 * Given an image being drawn into an pixel-based coordinate system, and
6452
 * a point in image space, map the point into the pixel-based coordinate
6453
 * system.
6454
 * @param aSize the size of the image, in pixels
6455
 * @param aDest the rectangle that the image is being mapped into
6456
 * @param aPt a point in image space
6457
 */
6458
static gfxPoint
6459
MapToFloatUserPixels(const gfxSize& aSize,
6460
                     const gfxRect& aDest, const gfxPoint& aPt)
6461
0
{
6462
0
  return gfxPoint(aPt.x*aDest.Width()/aSize.width + aDest.X(),
6463
0
                  aPt.y*aDest.Height()/aSize.height + aDest.Y());
6464
0
}
6465
6466
/* static */ gfxRect
6467
nsLayoutUtils::RectToGfxRect(const nsRect& aRect, int32_t aAppUnitsPerDevPixel)
6468
0
{
6469
0
  return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel,
6470
0
                 gfxFloat(aRect.y) / aAppUnitsPerDevPixel,
6471
0
                 gfxFloat(aRect.width) / aAppUnitsPerDevPixel,
6472
0
                 gfxFloat(aRect.height) / aAppUnitsPerDevPixel);
6473
0
}
6474
6475
struct SnappedImageDrawingParameters {
6476
  // A transform from image space to device space.
6477
  gfxMatrix imageSpaceToDeviceSpace;
6478
  // The size at which the image should be drawn (which may not be its
6479
  // intrinsic size due to, for example, HQ scaling).
6480
  nsIntSize size;
6481
  // The region in tiled image space which will be drawn, with an associated
6482
  // region to which sampling should be restricted.
6483
  ImageRegion region;
6484
  // The default viewport size for SVG images, which we use unless a different
6485
  // one has been explicitly specified. This is the same as |size| except that
6486
  // it does not take into account any transformation on the gfxContext we're
6487
  // drawing to - for example, CSS transforms are not taken into account.
6488
  CSSIntSize svgViewportSize;
6489
  // Whether there's anything to draw at all.
6490
  bool shouldDraw;
6491
6492
  SnappedImageDrawingParameters()
6493
   : region(ImageRegion::Empty())
6494
   , shouldDraw(false)
6495
0
  {}
6496
6497
  SnappedImageDrawingParameters(const gfxMatrix&   aImageSpaceToDeviceSpace,
6498
                                const nsIntSize&   aSize,
6499
                                const ImageRegion& aRegion,
6500
                                const CSSIntSize&  aSVGViewportSize)
6501
   : imageSpaceToDeviceSpace(aImageSpaceToDeviceSpace)
6502
   , size(aSize)
6503
   , region(aRegion)
6504
   , svgViewportSize(aSVGViewportSize)
6505
   , shouldDraw(true)
6506
0
  {}
6507
};
6508
6509
/**
6510
 * Given two axis-aligned rectangles, returns the transformation that maps the
6511
 * first onto the second.
6512
 *
6513
 * @param aFrom The rect to be transformed.
6514
 * @param aTo The rect that aFrom should be mapped onto by the transformation.
6515
 */
6516
static gfxMatrix
6517
TransformBetweenRects(const gfxRect& aFrom, const gfxRect& aTo)
6518
0
{
6519
0
  gfxSize scale(aTo.width / aFrom.width,
6520
0
                aTo.height / aFrom.height);
6521
0
  gfxPoint translation(aTo.x - aFrom.x * scale.width,
6522
0
                       aTo.y - aFrom.y * scale.height);
6523
0
  return gfxMatrix(scale.width, 0, 0, scale.height,
6524
0
                   translation.x, translation.y);
6525
0
}
6526
6527
static nsRect
6528
TileNearRect(const nsRect& aAnyTile, const nsRect& aTargetRect)
6529
0
{
6530
0
  nsPoint distance = aTargetRect.TopLeft() - aAnyTile.TopLeft();
6531
0
  return aAnyTile + nsPoint(distance.x / aAnyTile.width * aAnyTile.width,
6532
0
                            distance.y / aAnyTile.height * aAnyTile.height);
6533
0
}
6534
6535
static gfxFloat
6536
StableRound(gfxFloat aValue)
6537
0
{
6538
0
  // Values slightly less than 0.5 should round up like 0.5 would; we're
6539
0
  // assuming they were meant to be 0.5.
6540
0
  return floor(aValue + 0.5001);
6541
0
}
6542
6543
static gfxPoint
6544
StableRound(const gfxPoint& aPoint)
6545
0
{
6546
0
  return gfxPoint(StableRound(aPoint.x), StableRound(aPoint.y));
6547
0
}
6548
6549
/**
6550
 * Given a set of input parameters, compute certain output parameters
6551
 * for drawing an image with the image snapping algorithm.
6552
 * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
6553
 *
6554
 *  @see nsLayoutUtils::DrawImage() for the descriptions of input parameters
6555
 */
6556
static SnappedImageDrawingParameters
6557
ComputeSnappedImageDrawingParameters(gfxContext*     aCtx,
6558
                                     int32_t         aAppUnitsPerDevPixel,
6559
                                     const nsRect    aDest,
6560
                                     const nsRect    aFill,
6561
                                     const nsPoint   aAnchor,
6562
                                     const nsRect    aDirty,
6563
                                     imgIContainer*  aImage,
6564
                                     const SamplingFilter aSamplingFilter,
6565
                                     uint32_t        aImageFlags,
6566
                                     ExtendMode      aExtendMode)
6567
0
{
6568
0
  if (aDest.IsEmpty() || aFill.IsEmpty())
6569
0
    return SnappedImageDrawingParameters();
6570
0
6571
0
  // Avoid unnecessarily large offsets.
6572
0
  bool doTile = !aDest.Contains(aFill);
6573
0
  nsRect appUnitDest = doTile ? TileNearRect(aDest, aFill.Intersect(aDirty))
6574
0
                              : aDest;
6575
0
  nsPoint anchor = aAnchor + (appUnitDest.TopLeft() - aDest.TopLeft());
6576
0
6577
0
  gfxRect devPixelDest =
6578
0
    nsLayoutUtils::RectToGfxRect(appUnitDest, aAppUnitsPerDevPixel);
6579
0
  gfxRect devPixelFill =
6580
0
    nsLayoutUtils::RectToGfxRect(aFill, aAppUnitsPerDevPixel);
6581
0
  gfxRect devPixelDirty =
6582
0
    nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel);
6583
0
6584
0
  gfxMatrix currentMatrix = aCtx->CurrentMatrixDouble();
6585
0
  gfxRect fill = devPixelFill;
6586
0
  gfxRect dest = devPixelDest;
6587
0
  bool didSnap;
6588
0
  // Snap even if we have a scale in the context. But don't snap if
6589
0
  // we have something that's not translation+scale, or if the scale flips in
6590
0
  // the X or Y direction, because snapped image drawing can't handle that yet.
6591
0
  // Any changes to this algorithm will need to be reflected in
6592
0
  // ComputeImageContainerDrawingParameters.
6593
0
  if (!currentMatrix.HasNonAxisAlignedTransform() &&
6594
0
      currentMatrix._11 > 0.0 && currentMatrix._22 > 0.0 &&
6595
0
      aCtx->UserToDevicePixelSnapped(fill, true) &&
6596
0
      aCtx->UserToDevicePixelSnapped(dest, true)) {
6597
0
    // We snapped. On this code path, |fill| and |dest| take into account
6598
0
    // currentMatrix's transform.
6599
0
    didSnap = true;
6600
0
  } else {
6601
0
    // We didn't snap. On this code path, |fill| and |dest| do not take into
6602
0
    // account currentMatrix's transform.
6603
0
    didSnap = false;
6604
0
    fill = devPixelFill;
6605
0
    dest = devPixelDest;
6606
0
  }
6607
0
6608
0
  // If we snapped above, |dest| already takes into account |currentMatrix|'s scale
6609
0
  // and has integer coordinates. If not, we need these properties to compute
6610
0
  // the optimal drawn image size, so compute |snappedDestSize| here.
6611
0
  gfxSize snappedDestSize = dest.Size();
6612
0
  gfxSize scaleFactors = currentMatrix.ScaleFactors(true);
6613
0
  if (!didSnap) {
6614
0
    snappedDestSize.Scale(scaleFactors.width, scaleFactors.height);
6615
0
    snappedDestSize.width = NS_round(snappedDestSize.width);
6616
0
    snappedDestSize.height = NS_round(snappedDestSize.height);
6617
0
  }
6618
0
6619
0
  // We need to be sure that this is at least one pixel in width and height,
6620
0
  // or we'll end up drawing nothing even if we have a nonempty fill.
6621
0
  snappedDestSize.width = std::max(snappedDestSize.width, 1.0);
6622
0
  snappedDestSize.height = std::max(snappedDestSize.height, 1.0);
6623
0
6624
0
  // Bail if we're not going to end up drawing anything.
6625
0
  if (fill.IsEmpty()) {
6626
0
    return SnappedImageDrawingParameters();
6627
0
  }
6628
0
6629
0
  nsIntSize intImageSize =
6630
0
    aImage->OptimalImageSizeForDest(snappedDestSize,
6631
0
                                    imgIContainer::FRAME_CURRENT,
6632
0
                                    aSamplingFilter, aImageFlags);
6633
0
6634
0
  nsIntSize svgViewportSize;
6635
0
  if (scaleFactors.width == 1.0 && scaleFactors.height == 1.0) {
6636
0
    // intImageSize is scaled by currentMatrix. But since there are no scale
6637
0
    // factors in currentMatrix, it is safe to assign intImageSize to
6638
0
    // svgViewportSize directly.
6639
0
    svgViewportSize = intImageSize;
6640
0
  } else {
6641
0
    // We should not take into account any transformation of currentMatrix
6642
0
    // when computing svg viewport size. Since currentMatrix contains scale
6643
0
    // factors, we need to recompute SVG viewport by unscaled devPixelDest.
6644
0
    svgViewportSize = aImage->OptimalImageSizeForDest(devPixelDest.Size(),
6645
0
                                                      imgIContainer::FRAME_CURRENT,
6646
0
                                                      aSamplingFilter,
6647
0
                                                      aImageFlags);
6648
0
  }
6649
0
6650
0
  gfxSize imageSize(intImageSize.width, intImageSize.height);
6651
0
6652
0
  // Compute the set of pixels that would be sampled by an ideal rendering
6653
0
  gfxPoint subimageTopLeft =
6654
0
    MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft());
6655
0
  gfxPoint subimageBottomRight =
6656
0
    MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight());
6657
0
  gfxRect subimage;
6658
0
  subimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
6659
0
                  NSToIntFloor(subimageTopLeft.y));
6660
0
  subimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - subimage.x,
6661
0
                  NSToIntCeil(subimageBottomRight.y) - subimage.y);
6662
0
6663
0
  if (subimage.IsEmpty()) {
6664
0
    // Bail if the subimage is empty (we're not going to be drawing anything).
6665
0
    return SnappedImageDrawingParameters();
6666
0
  }
6667
0
6668
0
  gfxMatrix transform;
6669
0
  gfxMatrix invTransform;
6670
0
6671
0
  bool anchorAtUpperLeft = anchor.x == appUnitDest.x &&
6672
0
                           anchor.y == appUnitDest.y;
6673
0
  bool exactlyOneImageCopy = aFill.IsEqualEdges(appUnitDest);
6674
0
  if (anchorAtUpperLeft && exactlyOneImageCopy) {
6675
0
    // The simple case: we can ignore the anchor point and compute the
6676
0
    // transformation from the sampled region (the subimage) to the fill rect.
6677
0
    // This approach is preferable when it works since it tends to produce
6678
0
    // less numerical error.
6679
0
    transform = TransformBetweenRects(subimage, fill);
6680
0
    invTransform = TransformBetweenRects(fill, subimage);
6681
0
  } else {
6682
0
    // The more complicated case: we compute the transformation from the
6683
0
    // image rect positioned at the image space anchor point to the dest rect
6684
0
    // positioned at the device space anchor point.
6685
0
6686
0
    // Compute the anchor point in both device space and image space.  This
6687
0
    // code assumes that pixel-based devices have one pixel per device unit!
6688
0
    gfxPoint anchorPoint(gfxFloat(anchor.x)/aAppUnitsPerDevPixel,
6689
0
                         gfxFloat(anchor.y)/aAppUnitsPerDevPixel);
6690
0
    gfxPoint imageSpaceAnchorPoint =
6691
0
      MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint);
6692
0
6693
0
    if (didSnap) {
6694
0
      imageSpaceAnchorPoint = StableRound(imageSpaceAnchorPoint);
6695
0
      anchorPoint = imageSpaceAnchorPoint;
6696
0
      anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint);
6697
0
      anchorPoint = currentMatrix.TransformPoint(anchorPoint);
6698
0
      anchorPoint = StableRound(anchorPoint);
6699
0
    }
6700
0
6701
0
    // Compute an unsnapped version of the dest rect's size. We continue to
6702
0
    // follow the pattern that we take |currentMatrix| into account only if
6703
0
    // |didSnap| is true.
6704
0
    gfxSize unsnappedDestSize
6705
0
      = didSnap ? devPixelDest.Size() * currentMatrix.ScaleFactors(true)
6706
0
                : devPixelDest.Size();
6707
0
6708
0
    gfxRect anchoredDestRect(anchorPoint, unsnappedDestSize);
6709
0
    gfxRect anchoredImageRect(imageSpaceAnchorPoint, imageSize);
6710
0
6711
0
    // Calculate anchoredDestRect with snapped fill rect when the devPixelFill rect
6712
0
    // corresponds to just a single tile in that direction
6713
0
    if (fill.Width() != devPixelFill.Width() &&
6714
0
        devPixelDest.x == devPixelFill.x &&
6715
0
        devPixelDest.XMost() == devPixelFill.XMost()) {
6716
0
      anchoredDestRect.width = fill.width;
6717
0
    }
6718
0
    if (fill.Height() != devPixelFill.Height() &&
6719
0
        devPixelDest.y == devPixelFill.y &&
6720
0
        devPixelDest.YMost() == devPixelFill.YMost()) {
6721
0
      anchoredDestRect.height = fill.height;
6722
0
    }
6723
0
6724
0
    transform = TransformBetweenRects(anchoredImageRect, anchoredDestRect);
6725
0
    invTransform = TransformBetweenRects(anchoredDestRect, anchoredImageRect);
6726
0
  }
6727
0
6728
0
  // If the transform is not a straight translation by integers, then
6729
0
  // filtering will occur, and restricting the fill rect to the dirty rect
6730
0
  // would change the values computed for edge pixels, which we can't allow.
6731
0
  // Also, if 'didSnap' is false then rounding out 'devPixelDirty' might not
6732
0
  // produce pixel-aligned coordinates, which would also break the values
6733
0
  // computed for edge pixels.
6734
0
  if (didSnap && !invTransform.HasNonIntegerTranslation()) {
6735
0
    // This form of Transform is safe to call since non-axis-aligned
6736
0
    // transforms wouldn't be snapped.
6737
0
    devPixelDirty = currentMatrix.TransformRect(devPixelDirty);
6738
0
    devPixelDirty.RoundOut();
6739
0
    fill = fill.Intersect(devPixelDirty);
6740
0
  }
6741
0
  if (fill.IsEmpty())
6742
0
    return SnappedImageDrawingParameters();
6743
0
6744
0
  gfxRect imageSpaceFill(didSnap ? invTransform.TransformRect(fill)
6745
0
                                 : invTransform.TransformBounds(fill));
6746
0
6747
0
  // If we didn't snap, we need to post-multiply the matrix on the context to
6748
0
  // get the final matrix we'll draw with, because we didn't take it into
6749
0
  // account when computing the matrices above.
6750
0
  if (!didSnap) {
6751
0
    transform = transform * currentMatrix;
6752
0
  }
6753
0
6754
0
  ExtendMode extendMode = (aImageFlags & imgIContainer::FLAG_CLAMP)
6755
0
                          ? ExtendMode::CLAMP
6756
0
                          : aExtendMode;
6757
0
  // We were passed in the default extend mode but need to tile.
6758
0
  if (extendMode == ExtendMode::CLAMP && doTile) {
6759
0
    MOZ_ASSERT(!(aImageFlags & imgIContainer::FLAG_CLAMP));
6760
0
    extendMode = ExtendMode::REPEAT;
6761
0
  }
6762
0
6763
0
  ImageRegion region =
6764
0
    ImageRegion::CreateWithSamplingRestriction(imageSpaceFill, subimage, extendMode);
6765
0
6766
0
  return SnappedImageDrawingParameters(transform, intImageSize,
6767
0
                                       region,
6768
0
                                       CSSIntSize(svgViewportSize.width,
6769
0
                                                  svgViewportSize.height));
6770
0
}
6771
6772
static ImgDrawResult
6773
DrawImageInternal(gfxContext&            aContext,
6774
                  nsPresContext*         aPresContext,
6775
                  imgIContainer*         aImage,
6776
                  const SamplingFilter   aSamplingFilter,
6777
                  const nsRect&          aDest,
6778
                  const nsRect&          aFill,
6779
                  const nsPoint&         aAnchor,
6780
                  const nsRect&          aDirty,
6781
                  const Maybe<SVGImageContext>& aSVGContext,
6782
                  uint32_t               aImageFlags,
6783
                  ExtendMode             aExtendMode = ExtendMode::CLAMP,
6784
                  float                  aOpacity = 1.0)
6785
0
{
6786
0
  ImgDrawResult result = ImgDrawResult::SUCCESS;
6787
0
6788
0
  aImageFlags |= imgIContainer::FLAG_ASYNC_NOTIFY;
6789
0
6790
0
  if (aPresContext->Type() == nsPresContext::eContext_Print) {
6791
0
    // We want vector images to be passed on as vector commands, not a raster
6792
0
    // image.
6793
0
    aImageFlags |= imgIContainer::FLAG_BYPASS_SURFACE_CACHE;
6794
0
  }
6795
0
  if (aDest.Contains(aFill)) {
6796
0
    aImageFlags |= imgIContainer::FLAG_CLAMP;
6797
0
  }
6798
0
  int32_t appUnitsPerDevPixel =
6799
0
   aPresContext->AppUnitsPerDevPixel();
6800
0
6801
0
  SnappedImageDrawingParameters params =
6802
0
    ComputeSnappedImageDrawingParameters(&aContext, appUnitsPerDevPixel, aDest,
6803
0
                                         aFill, aAnchor, aDirty, aImage,
6804
0
                                         aSamplingFilter, aImageFlags, aExtendMode);
6805
0
6806
0
  if (!params.shouldDraw) {
6807
0
    return result;
6808
0
  }
6809
0
6810
0
  {
6811
0
    gfxContextMatrixAutoSaveRestore contextMatrixRestorer(&aContext);
6812
0
6813
0
    aContext.SetMatrixDouble(params.imageSpaceToDeviceSpace);
6814
0
6815
0
    Maybe<SVGImageContext> fallbackContext;
6816
0
    if (!aSVGContext) {
6817
0
      // Use the default viewport.
6818
0
      fallbackContext.emplace(Some(params.svgViewportSize));
6819
0
    }
6820
0
6821
0
    result = aImage->Draw(&aContext, params.size, params.region,
6822
0
                          imgIContainer::FRAME_CURRENT, aSamplingFilter,
6823
0
                          aSVGContext ? aSVGContext : fallbackContext,
6824
0
                          aImageFlags, aOpacity);
6825
0
6826
0
  }
6827
0
6828
0
  return result;
6829
0
}
6830
6831
/* static */ ImgDrawResult
6832
nsLayoutUtils::DrawSingleUnscaledImage(gfxContext&          aContext,
6833
                                       nsPresContext*       aPresContext,
6834
                                       imgIContainer*       aImage,
6835
                                       const SamplingFilter aSamplingFilter,
6836
                                       const nsPoint&       aDest,
6837
                                       const nsRect*        aDirty,
6838
                                       const Maybe<SVGImageContext>& aSVGContext,
6839
                                       uint32_t             aImageFlags,
6840
                                       const nsRect*        aSourceArea)
6841
0
{
6842
0
  CSSIntSize imageSize;
6843
0
  aImage->GetWidth(&imageSize.width);
6844
0
  aImage->GetHeight(&imageSize.height);
6845
0
  if (imageSize.width < 1 || imageSize.height < 1) {
6846
0
    NS_WARNING("Image width or height is non-positive");
6847
0
    return ImgDrawResult::TEMPORARY_ERROR;
6848
0
  }
6849
0
6850
0
  nsSize size(CSSPixel::ToAppUnits(imageSize));
6851
0
  nsRect source;
6852
0
  if (aSourceArea) {
6853
0
    source = *aSourceArea;
6854
0
  } else {
6855
0
    source.SizeTo(size);
6856
0
  }
6857
0
6858
0
  nsRect dest(aDest - source.TopLeft(), size);
6859
0
  nsRect fill(aDest, source.Size());
6860
0
  // Ensure that only a single image tile is drawn. If aSourceArea extends
6861
0
  // outside the image bounds, we want to honor the aSourceArea-to-aDest
6862
0
  // translation but we don't want to actually tile the image.
6863
0
  fill.IntersectRect(fill, dest);
6864
0
  return DrawImageInternal(aContext, aPresContext,
6865
0
                           aImage, aSamplingFilter,
6866
0
                           dest, fill, aDest, aDirty ? *aDirty : dest,
6867
0
                           aSVGContext, aImageFlags);
6868
0
}
6869
6870
/* static */ ImgDrawResult
6871
nsLayoutUtils::DrawSingleImage(gfxContext&            aContext,
6872
                               nsPresContext*         aPresContext,
6873
                               imgIContainer*         aImage,
6874
                               const SamplingFilter   aSamplingFilter,
6875
                               const nsRect&          aDest,
6876
                               const nsRect&          aDirty,
6877
                               const Maybe<SVGImageContext>& aSVGContext,
6878
                               uint32_t               aImageFlags,
6879
                               const nsPoint*         aAnchorPoint,
6880
                               const nsRect*          aSourceArea)
6881
0
{
6882
0
  nscoord appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
6883
0
  CSSIntSize pixelImageSize(ComputeSizeForDrawingWithFallback(aImage, aDest.Size()));
6884
0
  if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
6885
0
    NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
6886
0
                 "Image width or height is negative");
6887
0
    return ImgDrawResult::SUCCESS;  // no point in drawing a zero size image
6888
0
  }
6889
0
6890
0
  nsSize imageSize(CSSPixel::ToAppUnits(pixelImageSize));
6891
0
  nsRect source;
6892
0
  nsCOMPtr<imgIContainer> image;
6893
0
  if (aSourceArea) {
6894
0
    source = *aSourceArea;
6895
0
    nsIntRect subRect(source.x, source.y, source.width, source.height);
6896
0
    subRect.ScaleInverseRoundOut(appUnitsPerCSSPixel);
6897
0
    image = ImageOps::Clip(aImage, subRect);
6898
0
6899
0
    nsRect imageRect;
6900
0
    imageRect.SizeTo(imageSize);
6901
0
    nsRect clippedSource = imageRect.Intersect(source);
6902
0
6903
0
    source -= clippedSource.TopLeft();
6904
0
    imageSize = clippedSource.Size();
6905
0
  } else {
6906
0
    source.SizeTo(imageSize);
6907
0
    image = aImage;
6908
0
  }
6909
0
6910
0
  nsRect dest = GetWholeImageDestination(imageSize, source, aDest);
6911
0
6912
0
  // Ensure that only a single image tile is drawn. If aSourceArea extends
6913
0
  // outside the image bounds, we want to honor the aSourceArea-to-aDest
6914
0
  // transform but we don't want to actually tile the image.
6915
0
  nsRect fill;
6916
0
  fill.IntersectRect(aDest, dest);
6917
0
  return DrawImageInternal(aContext, aPresContext, image,
6918
0
                           aSamplingFilter, dest, fill,
6919
0
                           aAnchorPoint ? *aAnchorPoint : fill.TopLeft(),
6920
0
                           aDirty, aSVGContext, aImageFlags);
6921
0
}
6922
6923
/* static */ void
6924
nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage,
6925
                                     CSSIntSize&    aImageSize, /*outparam*/
6926
                                     nsSize&        aIntrinsicRatio, /*outparam*/
6927
                                     bool&          aGotWidth,  /*outparam*/
6928
                                     bool&          aGotHeight  /*outparam*/)
6929
0
{
6930
0
  aGotWidth  = NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width));
6931
0
  aGotHeight = NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height));
6932
0
  bool gotRatio = NS_SUCCEEDED(aImage->GetIntrinsicRatio(&aIntrinsicRatio));
6933
0
6934
0
  if (!(aGotWidth && aGotHeight) && !gotRatio) {
6935
0
    // We hit an error (say, because the image failed to load or couldn't be
6936
0
    // decoded) and should return zero size.
6937
0
    aGotWidth = aGotHeight = true;
6938
0
    aImageSize = CSSIntSize(0, 0);
6939
0
    aIntrinsicRatio = nsSize(0, 0);
6940
0
  }
6941
0
}
6942
6943
/* static */ CSSIntSize
6944
nsLayoutUtils::ComputeSizeForDrawingWithFallback(imgIContainer* aImage,
6945
                                                 const nsSize&  aFallbackSize)
6946
0
{
6947
0
  CSSIntSize imageSize;
6948
0
  nsSize imageRatio;
6949
0
  bool gotHeight, gotWidth;
6950
0
  ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight);
6951
0
6952
0
  // If we didn't get both width and height, try to compute them using the
6953
0
  // intrinsic ratio of the image.
6954
0
  if (gotWidth != gotHeight) {
6955
0
    if (!gotWidth) {
6956
0
      if (imageRatio.height != 0) {
6957
0
        imageSize.width =
6958
0
          NSCoordSaturatingNonnegativeMultiply(imageSize.height,
6959
0
                                               float(imageRatio.width) /
6960
0
                                               float(imageRatio.height));
6961
0
        gotWidth = true;
6962
0
      }
6963
0
    } else {
6964
0
      if (imageRatio.width != 0) {
6965
0
        imageSize.height =
6966
0
          NSCoordSaturatingNonnegativeMultiply(imageSize.width,
6967
0
                                               float(imageRatio.height) /
6968
0
                                               float(imageRatio.width));
6969
0
        gotHeight = true;
6970
0
      }
6971
0
    }
6972
0
  }
6973
0
6974
0
  // If we still don't have a width or height, just use the fallback size the
6975
0
  // caller provided.
6976
0
  if (!gotWidth) {
6977
0
    imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.width);
6978
0
  }
6979
0
  if (!gotHeight) {
6980
0
    imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.height);
6981
0
  }
6982
0
6983
0
  return imageSize;
6984
0
}
6985
6986
/* static */ IntSize
6987
nsLayoutUtils::ComputeImageContainerDrawingParameters(imgIContainer*            aImage,
6988
                                                      nsIFrame*                 aForFrame,
6989
                                                      const LayoutDeviceRect&   aDestRect,
6990
                                                      const StackingContextHelper& aSc,
6991
                                                      uint32_t                  aFlags,
6992
                                                      Maybe<SVGImageContext>&   aSVGContext)
6993
0
{
6994
0
  MOZ_ASSERT(aImage);
6995
0
  MOZ_ASSERT(aForFrame);
6996
0
6997
0
  gfx::Size scaleFactors = aSc.GetInheritedScale();
6998
0
  SamplingFilter samplingFilter =
6999
0
    nsLayoutUtils::GetSamplingFilterForFrame(aForFrame);
7000
0
7001
0
  // Compute our SVG context parameters, if any. Don't replace the viewport
7002
0
  // size if it was already set, prefer what the caller gave.
7003
0
  SVGImageContext::MaybeStoreContextPaint(aSVGContext, aForFrame, aImage);
7004
0
  if ((scaleFactors.width != 1.0 || scaleFactors.height != 1.0) &&
7005
0
      aImage->GetType() == imgIContainer::TYPE_VECTOR &&
7006
0
      (!aSVGContext || !aSVGContext->GetViewportSize())) {
7007
0
    gfxSize gfxDestSize(aDestRect.Width(), aDestRect.Height());
7008
0
    IntSize viewportSize =
7009
0
      aImage->OptimalImageSizeForDest(gfxDestSize,
7010
0
                                      imgIContainer::FRAME_CURRENT,
7011
0
                                      samplingFilter, aFlags);
7012
0
7013
0
    CSSIntSize cssViewportSize(viewportSize.width, viewportSize.height);
7014
0
    if (!aSVGContext) {
7015
0
      aSVGContext.emplace(Some(cssViewportSize));
7016
0
    } else {
7017
0
      aSVGContext->SetViewportSize(Some(cssViewportSize));
7018
0
    }
7019
0
  }
7020
0
7021
0
  // Attempt to snap pixels, the same as ComputeSnappedImageDrawingParameters.
7022
0
  // Any changes to the algorithm here will need to be reflected there.
7023
0
  bool snapped = false;
7024
0
  gfxSize gfxLayerSize;
7025
0
  const gfx::Matrix& itm = aSc.GetInheritedTransform();
7026
0
  if (!itm.HasNonAxisAlignedTransform() &&
7027
0
      itm._11 > 0.0 &&
7028
0
      itm._22 > 0.0) {
7029
0
    gfxRect rect(gfxPoint(aDestRect.X(), aDestRect.Y()),
7030
0
                 gfxSize(aDestRect.Width(), aDestRect.Height()));
7031
0
7032
0
    gfxPoint p1 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopLeft())));
7033
0
    gfxPoint p2 = ThebesPoint(itm.TransformPoint(ToPoint(rect.TopRight())));
7034
0
    gfxPoint p3 = ThebesPoint(itm.TransformPoint(ToPoint(rect.BottomRight())));
7035
0
7036
0
    if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
7037
0
      p1.Round();
7038
0
      p3.Round();
7039
0
7040
0
      rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
7041
0
      rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
7042
0
                          std::max(p1.y, p3.y) - rect.Y()));
7043
0
7044
0
      // An empty size is unacceptable so we ensure our suggested size is at
7045
0
      // least 1 pixel wide/tall.
7046
0
      gfxLayerSize = gfxSize(std::max(rect.Width(), 1.0),
7047
0
                             std::max(rect.Height(), 1.0));
7048
0
      snapped = true;
7049
0
    }
7050
0
  }
7051
0
7052
0
  if (!snapped) {
7053
0
    // Compute our size in layer pixels.
7054
0
    const LayerIntSize layerSize =
7055
0
      RoundedToInt(LayerSize(aDestRect.Width() * scaleFactors.width,
7056
0
                             aDestRect.Height() * scaleFactors.height));
7057
0
7058
0
    // An empty size is unacceptable so we ensure our suggested size is at least
7059
0
    // 1 pixel wide/tall.
7060
0
    gfxLayerSize = gfxSize(std::max(layerSize.width, 1),
7061
0
                           std::max(layerSize.height, 1));
7062
0
  }
7063
0
7064
0
  return aImage->OptimalImageSizeForDest(gfxLayerSize,
7065
0
                                         imgIContainer::FRAME_CURRENT,
7066
0
                                         samplingFilter, aFlags);
7067
0
}
7068
7069
/* static */ nsPoint
7070
nsLayoutUtils::GetBackgroundFirstTilePos(const nsPoint& aDest,
7071
                                         const nsPoint& aFill,
7072
                                         const nsSize& aRepeatSize)
7073
0
{
7074
0
  return nsPoint(NSToIntFloor(float(aFill.x - aDest.x) / aRepeatSize.width) * aRepeatSize.width,
7075
0
                 NSToIntFloor(float(aFill.y - aDest.y) / aRepeatSize.height) * aRepeatSize.height) +
7076
0
         aDest;
7077
0
}
7078
7079
/* static */ ImgDrawResult
7080
nsLayoutUtils::DrawBackgroundImage(gfxContext&         aContext,
7081
                                   nsIFrame*           aForFrame,
7082
                                   nsPresContext*      aPresContext,
7083
                                   imgIContainer*      aImage,
7084
                                   const CSSIntSize&   aImageSize,
7085
                                   SamplingFilter      aSamplingFilter,
7086
                                   const nsRect&       aDest,
7087
                                   const nsRect&       aFill,
7088
                                   const nsSize&       aRepeatSize,
7089
                                   const nsPoint&      aAnchor,
7090
                                   const nsRect&       aDirty,
7091
                                   uint32_t            aImageFlags,
7092
                                   ExtendMode          aExtendMode,
7093
                                   float               aOpacity)
7094
0
{
7095
0
  AUTO_PROFILER_LABEL("nsLayoutUtils::DrawBackgroundImage", GRAPHICS);
7096
0
7097
0
  Maybe<SVGImageContext> svgContext(Some(SVGImageContext(Some(aImageSize))));
7098
0
  SVGImageContext::MaybeStoreContextPaint(svgContext, aForFrame, aImage);
7099
0
7100
0
  /* Fast path when there is no need for image spacing */
7101
0
  if (aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height) {
7102
0
    return DrawImageInternal(aContext, aPresContext, aImage,
7103
0
                             aSamplingFilter, aDest, aFill, aAnchor,
7104
0
                             aDirty, svgContext, aImageFlags, aExtendMode,
7105
0
                             aOpacity);
7106
0
  }
7107
0
7108
0
  nsPoint firstTilePos = GetBackgroundFirstTilePos(aDest.TopLeft(), aFill.TopLeft(), aRepeatSize);
7109
0
  for (int32_t i = firstTilePos.x; i < aFill.XMost(); i += aRepeatSize.width) {
7110
0
    for (int32_t j = firstTilePos.y; j < aFill.YMost(); j += aRepeatSize.height) {
7111
0
      nsRect dest(i, j, aDest.width, aDest.height);
7112
0
      ImgDrawResult result = DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
7113
0
                                            dest, dest, aAnchor, aDirty, svgContext,
7114
0
                                            aImageFlags, ExtendMode::CLAMP,
7115
0
                                            aOpacity);
7116
0
      if (result != ImgDrawResult::SUCCESS) {
7117
0
        return result;
7118
0
      }
7119
0
    }
7120
0
  }
7121
0
7122
0
  return ImgDrawResult::SUCCESS;
7123
0
}
7124
7125
/* static */ ImgDrawResult
7126
nsLayoutUtils::DrawImage(gfxContext&         aContext,
7127
                         ComputedStyle*     aComputedStyle,
7128
                         nsPresContext*      aPresContext,
7129
                         imgIContainer*      aImage,
7130
                         const SamplingFilter aSamplingFilter,
7131
                         const nsRect&       aDest,
7132
                         const nsRect&       aFill,
7133
                         const nsPoint&      aAnchor,
7134
                         const nsRect&       aDirty,
7135
                         uint32_t            aImageFlags,
7136
                         float               aOpacity)
7137
0
{
7138
0
  Maybe<SVGImageContext> svgContext;
7139
0
  SVGImageContext::MaybeStoreContextPaint(svgContext, aComputedStyle, aImage);
7140
0
7141
0
  return DrawImageInternal(aContext, aPresContext, aImage,
7142
0
                           aSamplingFilter, aDest, aFill, aAnchor,
7143
0
                           aDirty,
7144
0
                           svgContext,
7145
0
                           aImageFlags, ExtendMode::CLAMP,
7146
0
                           aOpacity);
7147
0
}
7148
7149
/* static */ nsRect
7150
nsLayoutUtils::GetWholeImageDestination(const nsSize& aWholeImageSize,
7151
                                        const nsRect& aImageSourceArea,
7152
                                        const nsRect& aDestArea)
7153
0
{
7154
0
  double scaleX = double(aDestArea.width)/aImageSourceArea.width;
7155
0
  double scaleY = double(aDestArea.height)/aImageSourceArea.height;
7156
0
  nscoord destOffsetX = NSToCoordRound(aImageSourceArea.x*scaleX);
7157
0
  nscoord destOffsetY = NSToCoordRound(aImageSourceArea.y*scaleY);
7158
0
  nscoord wholeSizeX = NSToCoordRound(aWholeImageSize.width*scaleX);
7159
0
  nscoord wholeSizeY = NSToCoordRound(aWholeImageSize.height*scaleY);
7160
0
  return nsRect(aDestArea.TopLeft() - nsPoint(destOffsetX, destOffsetY),
7161
0
                nsSize(wholeSizeX, wholeSizeY));
7162
0
}
7163
7164
/* static */ already_AddRefed<imgIContainer>
7165
nsLayoutUtils::OrientImage(imgIContainer* aContainer,
7166
                           const StyleImageOrientation& aOrientation)
7167
0
{
7168
0
  MOZ_ASSERT(aContainer, "Should have an image container");
7169
0
  nsCOMPtr<imgIContainer> img(aContainer);
7170
0
7171
0
  if (aOrientation == StyleImageOrientation::FromImage) {
7172
0
    img = ImageOps::Orient(img, img->GetOrientation());
7173
0
  }
7174
0
7175
0
  return img.forget();
7176
0
}
7177
7178
static bool NonZeroStyleCoord(const nsStyleCoord& aCoord)
7179
0
{
7180
0
  if (aCoord.IsCoordPercentCalcUnit()) {
7181
0
    // Since negative results are clamped to 0, check > 0.
7182
0
    return aCoord.ComputeCoordPercentCalc(nscoord_MAX) > 0 ||
7183
0
           aCoord.ComputeCoordPercentCalc(0) > 0;
7184
0
  }
7185
0
7186
0
  return true;
7187
0
}
7188
7189
/* static */ bool
7190
nsLayoutUtils::HasNonZeroCorner(const nsStyleCorners& aCorners)
7191
0
{
7192
0
  NS_FOR_CSS_HALF_CORNERS(corner) {
7193
0
    if (NonZeroStyleCoord(aCorners.Get(corner)))
7194
0
      return true;
7195
0
  }
7196
0
  return false;
7197
0
}
7198
7199
// aCorner is a "full corner" value, i.e. eCornerTopLeft etc.
7200
static bool IsCornerAdjacentToSide(uint8_t aCorner, Side aSide)
7201
0
{
7202
0
  static_assert((int)eSideTop == eCornerTopLeft, "Check for Full Corner");
7203
0
  static_assert((int)eSideRight == eCornerTopRight, "Check for Full Corner");
7204
0
  static_assert((int)eSideBottom == eCornerBottomRight, "Check for Full Corner");
7205
0
  static_assert((int)eSideLeft == eCornerBottomLeft, "Check for Full Corner");
7206
0
  static_assert((int)eSideTop == ((eCornerTopRight - 1)&3), "Check for Full Corner");
7207
0
  static_assert((int)eSideRight == ((eCornerBottomRight - 1)&3), "Check for Full Corner");
7208
0
  static_assert((int)eSideBottom == ((eCornerBottomLeft - 1)&3), "Check for Full Corner");
7209
0
  static_assert((int)eSideLeft == ((eCornerTopLeft - 1)&3), "Check for Full Corner");
7210
0
7211
0
  return aSide == aCorner || aSide == ((aCorner - 1)&3);
7212
0
}
7213
7214
/* static */ bool
7215
nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners& aCorners,
7216
                                      Side aSide)
7217
0
{
7218
0
  static_assert(eCornerTopLeftX/2 == eCornerTopLeft, "Check for Non Zero on side");
7219
0
  static_assert(eCornerTopLeftY/2 == eCornerTopLeft, "Check for Non Zero on side");
7220
0
  static_assert(eCornerTopRightX/2 == eCornerTopRight, "Check for Non Zero on side");
7221
0
  static_assert(eCornerTopRightY/2 == eCornerTopRight, "Check for Non Zero on side");
7222
0
  static_assert(eCornerBottomRightX/2 == eCornerBottomRight, "Check for Non Zero on side");
7223
0
  static_assert(eCornerBottomRightY/2 == eCornerBottomRight, "Check for Non Zero on side");
7224
0
  static_assert(eCornerBottomLeftX/2 == eCornerBottomLeft, "Check for Non Zero on side");
7225
0
  static_assert(eCornerBottomLeftY/2 == eCornerBottomLeft, "Check for Non Zero on side");
7226
0
7227
0
  NS_FOR_CSS_HALF_CORNERS(corner) {
7228
0
    // corner is a "half corner" value, so dividing by two gives us a
7229
0
    // "full corner" value.
7230
0
    if (NonZeroStyleCoord(aCorners.Get(corner)) &&
7231
0
        IsCornerAdjacentToSide(corner/2, aSide))
7232
0
      return true;
7233
0
  }
7234
0
  return false;
7235
0
}
7236
7237
/* static */ nsTransparencyMode
7238
nsLayoutUtils::GetFrameTransparency(nsIFrame* aBackgroundFrame,
7239
0
                                    nsIFrame* aCSSRootFrame) {
7240
0
  if (aCSSRootFrame->StyleEffects()->mOpacity < 1.0f)
7241
0
    return eTransparencyTransparent;
7242
0
7243
0
  if (HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius))
7244
0
    return eTransparencyTransparent;
7245
0
7246
0
  if (aCSSRootFrame->StyleDisplay()->mAppearance == StyleAppearance::MozWinGlass)
7247
0
    return eTransparencyGlass;
7248
0
7249
0
  if (aCSSRootFrame->StyleDisplay()->mAppearance == StyleAppearance::MozWinBorderlessGlass)
7250
0
    return eTransparencyBorderlessGlass;
7251
0
7252
0
  nsITheme::Transparency transparency;
7253
0
  if (aCSSRootFrame->IsThemed(&transparency))
7254
0
    return transparency == nsITheme::eTransparent
7255
0
         ? eTransparencyTransparent
7256
0
         : eTransparencyOpaque;
7257
0
7258
0
  // We need an uninitialized window to be treated as opaque because
7259
0
  // doing otherwise breaks window display effects on some platforms,
7260
0
  // specifically Vista. (bug 450322)
7261
0
  if (aBackgroundFrame->IsViewportFrame() &&
7262
0
      !aBackgroundFrame->PrincipalChildList().FirstChild()) {
7263
0
    return eTransparencyOpaque;
7264
0
  }
7265
0
7266
0
  ComputedStyle* bgSC;
7267
0
  if (!nsCSSRendering::FindBackground(aBackgroundFrame, &bgSC)) {
7268
0
    return eTransparencyTransparent;
7269
0
  }
7270
0
  const nsStyleBackground* bg = bgSC->StyleBackground();
7271
0
  if (NS_GET_A(bg->BackgroundColor(bgSC)) < 255 ||
7272
0
      // bottom layer's clip is used for the color
7273
0
      bg->BottomLayer().mClip != StyleGeometryBox::BorderBox)
7274
0
    return eTransparencyTransparent;
7275
0
  return eTransparencyOpaque;
7276
0
}
7277
7278
static bool IsPopupFrame(const nsIFrame* aFrame)
7279
0
{
7280
0
  // aFrame is a popup it's the list control frame dropdown for a combobox.
7281
0
  LayoutFrameType frameType = aFrame->Type();
7282
0
  if (!nsLayoutUtils::IsContentSelectEnabled() &&
7283
0
      frameType == LayoutFrameType::ListControl) {
7284
0
    const nsListControlFrame* lcf = static_cast<const nsListControlFrame*>(aFrame);
7285
0
    return lcf->IsInDropDownMode();
7286
0
  }
7287
0
7288
0
  // ... or if it's a XUL menupopup frame.
7289
0
  return frameType == LayoutFrameType::MenuPopup;
7290
0
}
7291
7292
/* static */ bool
7293
nsLayoutUtils::IsPopup(const nsIFrame* aFrame)
7294
0
{
7295
0
  // Optimization: the frame can't possibly be a popup if it has no view.
7296
0
  if (!aFrame->HasView()) {
7297
0
    NS_ASSERTION(!IsPopupFrame(aFrame), "popup frame must have a view");
7298
0
    return false;
7299
0
  }
7300
0
  return IsPopupFrame(aFrame);
7301
0
}
7302
7303
/* static */ nsIFrame*
7304
nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame)
7305
0
{
7306
0
  // We could use GetRootPresContext() here if the
7307
0
  // NS_FRAME_IN_POPUP frame bit is set.
7308
0
  nsIFrame* f = aFrame;
7309
0
  for (;;) {
7310
0
    if (!f->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
7311
0
      f = f->PresShell()->GetRootFrame();
7312
0
      if (!f) {
7313
0
        return aFrame;
7314
0
      }
7315
0
    } else if (IsPopup(f)) {
7316
0
      return f;
7317
0
    }
7318
0
    nsIFrame* parent = GetCrossDocParentFrame(f);
7319
0
    if (!parent)
7320
0
      return f;
7321
0
    f = parent;
7322
0
  }
7323
0
}
7324
7325
/* static */ nsIFrame*
7326
nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame)
7327
0
{
7328
0
  nsIFrame *f = aFrame;
7329
0
  for (;;) {
7330
0
    const nsStyleDisplay* disp = f->StyleDisplay();
7331
0
    if (f->IsTransformed(disp) || f->IsPreserve3DLeaf(disp) || IsPopup(f)) {
7332
0
      return f;
7333
0
    }
7334
0
    nsIFrame* parent = GetCrossDocParentFrame(f);
7335
0
    if (!parent) {
7336
0
      return f;
7337
0
    }
7338
0
    f = parent;
7339
0
  }
7340
0
}
7341
7342
/* static */ gfx::ShapedTextFlags
7343
nsLayoutUtils::GetTextRunFlagsForStyle(ComputedStyle* aComputedStyle,
7344
                                       nsPresContext* aPresContext,
7345
                                       const nsStyleFont* aStyleFont,
7346
                                       const nsStyleText* aStyleText,
7347
                                       nscoord aLetterSpacing)
7348
0
{
7349
0
  gfx::ShapedTextFlags result = gfx::ShapedTextFlags();
7350
0
  if (aLetterSpacing != 0 ||
7351
0
      aStyleText->mTextJustify == StyleTextJustify::InterCharacter) {
7352
0
    result |= gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES;
7353
0
  }
7354
0
  if (aStyleText->mControlCharacterVisibility == NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN) {
7355
0
    result |= gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS;
7356
0
  }
7357
0
  switch (aComputedStyle->StyleText()->mTextRendering) {
7358
0
  case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
7359
0
    result |= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
7360
0
    break;
7361
0
  case NS_STYLE_TEXT_RENDERING_AUTO:
7362
0
    if (aStyleFont->mFont.size < aPresContext->GetAutoQualityMinFontSize()) {
7363
0
      result |= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
7364
0
    }
7365
0
    break;
7366
0
  default:
7367
0
    break;
7368
0
  }
7369
0
  return result | GetTextRunOrientFlagsForStyle(aComputedStyle);
7370
0
}
7371
7372
/* static */ gfx::ShapedTextFlags
7373
nsLayoutUtils::GetTextRunOrientFlagsForStyle(ComputedStyle* aComputedStyle)
7374
0
{
7375
0
  uint8_t writingMode = aComputedStyle->StyleVisibility()->mWritingMode;
7376
0
  switch (writingMode) {
7377
0
  case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
7378
0
    return gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL;
7379
0
7380
0
  case NS_STYLE_WRITING_MODE_VERTICAL_LR:
7381
0
  case NS_STYLE_WRITING_MODE_VERTICAL_RL:
7382
0
    switch (aComputedStyle->StyleVisibility()->mTextOrientation) {
7383
0
    case NS_STYLE_TEXT_ORIENTATION_MIXED:
7384
0
      return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;
7385
0
    case NS_STYLE_TEXT_ORIENTATION_UPRIGHT:
7386
0
      return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
7387
0
    case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
7388
0
      return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
7389
0
    default:
7390
0
      MOZ_ASSERT_UNREACHABLE("unknown text-orientation");
7391
0
      return gfx::ShapedTextFlags();
7392
0
    }
7393
0
7394
0
  case NS_STYLE_WRITING_MODE_SIDEWAYS_LR:
7395
0
    return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;
7396
0
7397
0
  case NS_STYLE_WRITING_MODE_SIDEWAYS_RL:
7398
0
    return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
7399
0
7400
0
  default:
7401
0
    MOZ_ASSERT_UNREACHABLE("unknown writing-mode");
7402
0
    return gfx::ShapedTextFlags();
7403
0
  }
7404
0
}
7405
7406
/* static */ void
7407
nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
7408
0
                                       nsRect* aHStrip, nsRect* aVStrip) {
7409
0
  NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
7410
0
               "expected rects at the same position");
7411
0
  nsRect unionRect(aR1.x, aR1.y, std::max(aR1.width, aR2.width),
7412
0
                   std::max(aR1.height, aR2.height));
7413
0
  nscoord VStripStart = std::min(aR1.width, aR2.width);
7414
0
  nscoord HStripStart = std::min(aR1.height, aR2.height);
7415
0
  *aVStrip = unionRect;
7416
0
  aVStrip->x += VStripStart;
7417
0
  aVStrip->width -= VStripStart;
7418
0
  *aHStrip = unionRect;
7419
0
  aHStrip->y += HStripStart;
7420
0
  aHStrip->height -= HStripStart;
7421
0
}
7422
7423
nsDeviceContext*
7424
nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindowOuter* aWindow)
7425
0
{
7426
0
  if (!aWindow) {
7427
0
    return nullptr;
7428
0
  }
7429
0
7430
0
  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
7431
0
  while (docShell) {
7432
0
    // Now make sure our size is up to date.  That will mean that the device
7433
0
    // context does the right thing on multi-monitor systems when we return it to
7434
0
    // the caller.  It will also make sure that our prescontext has been created,
7435
0
    // if we're supposed to have one.
7436
0
    nsCOMPtr<nsPIDOMWindowOuter> win = docShell->GetWindow();
7437
0
    if (!win) {
7438
0
      // No reason to go on
7439
0
      return nullptr;
7440
0
    }
7441
0
7442
0
    win->EnsureSizeAndPositionUpToDate();
7443
0
7444
0
    RefPtr<nsPresContext> presContext;
7445
0
    docShell->GetPresContext(getter_AddRefs(presContext));
7446
0
    if (presContext) {
7447
0
      nsDeviceContext* context = presContext->DeviceContext();
7448
0
      if (context) {
7449
0
        return context;
7450
0
      }
7451
0
    }
7452
0
7453
0
    nsCOMPtr<nsIDocShellTreeItem> parentItem;
7454
0
    docShell->GetParent(getter_AddRefs(parentItem));
7455
0
    docShell = do_QueryInterface(parentItem);
7456
0
  }
7457
0
7458
0
  return nullptr;
7459
0
}
7460
7461
/* static */ bool
7462
nsLayoutUtils::IsReallyFixedPos(const nsIFrame* aFrame)
7463
0
{
7464
0
  MOZ_ASSERT(aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED,
7465
0
             "IsReallyFixedPos called on non-'position:fixed' frame");
7466
0
  return MayBeReallyFixedPos(aFrame);
7467
0
}
7468
7469
7470
/* static */ bool
7471
nsLayoutUtils::MayBeReallyFixedPos(const nsIFrame* aFrame)
7472
0
{
7473
0
  MOZ_ASSERT(aFrame->GetParent(),
7474
0
             "MayBeReallyFixedPos called on frame not in tree");
7475
0
  LayoutFrameType parentType = aFrame->GetParent()->Type();
7476
0
  return parentType == LayoutFrameType::Viewport ||
7477
0
         parentType == LayoutFrameType::PageContent;
7478
0
}
7479
7480
nsLayoutUtils::SurfaceFromElementResult
7481
nsLayoutUtils::SurfaceFromOffscreenCanvas(OffscreenCanvas* aOffscreenCanvas,
7482
                                          uint32_t aSurfaceFlags,
7483
                                          RefPtr<DrawTarget>& aTarget)
7484
0
{
7485
0
  SurfaceFromElementResult result;
7486
0
7487
0
  nsIntSize size = aOffscreenCanvas->GetWidthHeight();
7488
0
7489
0
  result.mSourceSurface = aOffscreenCanvas->GetSurfaceSnapshot(&result.mAlphaType);
7490
0
  if (!result.mSourceSurface) {
7491
0
    // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just
7492
0
    // draw nothing, so return an empty surface.
7493
0
    result.mAlphaType = gfxAlphaType::Opaque;
7494
0
    RefPtr<DrawTarget> ref =
7495
0
      aTarget ? aTarget
7496
0
              : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
7497
0
    RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
7498
0
                                                         SurfaceFormat::B8G8R8A8);
7499
0
    if (dt) {
7500
0
      result.mSourceSurface = dt->Snapshot();
7501
0
    }
7502
0
  } else if (aTarget) {
7503
0
    RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
7504
0
    if (opt) {
7505
0
      result.mSourceSurface = opt;
7506
0
    }
7507
0
  }
7508
0
7509
0
  result.mHasSize = true;
7510
0
  result.mSize = size;
7511
0
  result.mIsWriteOnly = aOffscreenCanvas->IsWriteOnly();
7512
0
7513
0
  return result;
7514
0
}
7515
7516
nsLayoutUtils::SurfaceFromElementResult
7517
nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement,
7518
                                  uint32_t aSurfaceFlags,
7519
                                  RefPtr<DrawTarget>& aTarget)
7520
0
{
7521
0
  SurfaceFromElementResult result;
7522
0
  nsresult rv;
7523
0
7524
0
  nsCOMPtr<imgIRequest> imgRequest;
7525
0
  rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
7526
0
                            getter_AddRefs(imgRequest));
7527
0
  if (NS_FAILED(rv)) {
7528
0
    return result;
7529
0
  }
7530
0
7531
0
  if (!imgRequest) {
7532
0
    // There's no image request. This is either because a request for
7533
0
    // a non-empty URI failed, or the URI is the empty string.
7534
0
    nsCOMPtr<nsIURI> currentURI;
7535
0
    aElement->GetCurrentURI(getter_AddRefs(currentURI));
7536
0
    if (!currentURI) {
7537
0
      // Treat the empty URI as available instead of broken state.
7538
0
      result.mHasSize = true;
7539
0
    }
7540
0
    return result;
7541
0
  }
7542
0
7543
0
  uint32_t status;
7544
0
  imgRequest->GetImageStatus(&status);
7545
0
  result.mHasSize = status & imgIRequest::STATUS_SIZE_AVAILABLE;
7546
0
  if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) {
7547
0
    // Spec says to use GetComplete, but that only works on
7548
0
    // HTMLImageElement, and we support all sorts of other stuff
7549
0
    // here.  Do this for now pending spec clarification.
7550
0
    result.mIsStillLoading = (status & imgIRequest::STATUS_ERROR) == 0;
7551
0
    return result;
7552
0
  }
7553
0
7554
0
  nsCOMPtr<nsIPrincipal> principal;
7555
0
  rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal));
7556
0
  if (NS_FAILED(rv)) {
7557
0
    return result;
7558
0
  }
7559
0
7560
0
  nsCOMPtr<imgIContainer> imgContainer;
7561
0
  rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
7562
0
  if (NS_FAILED(rv)) {
7563
0
    return result;
7564
0
  }
7565
0
7566
0
  uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
7567
0
7568
0
  uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME_IF_IMAGE)
7569
0
                        ? (uint32_t) imgIContainer::FRAME_FIRST
7570
0
                        : (uint32_t) imgIContainer::FRAME_CURRENT;
7571
0
  uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE
7572
0
                      | imgIContainer::FLAG_ASYNC_NOTIFY;
7573
0
  if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION)
7574
0
    frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
7575
0
  if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
7576
0
    frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
7577
0
  }
7578
0
7579
0
  int32_t imgWidth, imgHeight;
7580
0
  nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
7581
0
  HTMLImageElement* element = HTMLImageElement::FromNodeOrNull(content);
7582
0
  if (aSurfaceFlags & SFE_USE_ELEMENT_SIZE_IF_VECTOR &&
7583
0
      element &&
7584
0
      imgContainer->GetType() == imgIContainer::TYPE_VECTOR) {
7585
0
    // We're holding a strong ref to "element" via "content".
7586
0
    imgWidth = MOZ_KnownLive(element)->Width();
7587
0
    imgHeight = MOZ_KnownLive(element)->Height();
7588
0
  } else {
7589
0
    rv = imgContainer->GetWidth(&imgWidth);
7590
0
    nsresult rv2 = imgContainer->GetHeight(&imgHeight);
7591
0
    if (NS_FAILED(rv) || NS_FAILED(rv2))
7592
0
      return result;
7593
0
  }
7594
0
  result.mSize = IntSize(imgWidth, imgHeight);
7595
0
7596
0
  if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
7597
0
    if (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) {
7598
0
      frameFlags |= imgIContainer::FLAG_WANT_DATA_SURFACE;
7599
0
    }
7600
0
    result.mSourceSurface = imgContainer->GetFrameAtSize(result.mSize, whichFrame, frameFlags);
7601
0
    if (!result.mSourceSurface) {
7602
0
      return result;
7603
0
    }
7604
0
    // The surface we return is likely to be cached. We don't want to have to
7605
0
    // convert to a surface that's compatible with aTarget each time it's used
7606
0
    // (that would result in terrible performance), so we convert once here
7607
0
    // upfront if aTarget is specified.
7608
0
    if (aTarget) {
7609
0
      RefPtr<SourceSurface> optSurface =
7610
0
        aTarget->OptimizeSourceSurface(result.mSourceSurface);
7611
0
      if (optSurface) {
7612
0
        result.mSourceSurface = optSurface;
7613
0
      }
7614
0
    }
7615
0
7616
0
    const auto& format = result.mSourceSurface->GetFormat();
7617
0
    if (IsOpaque(format)) {
7618
0
      result.mAlphaType = gfxAlphaType::Opaque;
7619
0
    } else if (frameFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA) {
7620
0
      result.mAlphaType = gfxAlphaType::NonPremult;
7621
0
    } else {
7622
0
      result.mAlphaType = gfxAlphaType::Premult;
7623
0
    }
7624
0
  } else {
7625
0
    result.mDrawInfo.mImgContainer = imgContainer;
7626
0
    result.mDrawInfo.mWhichFrame = whichFrame;
7627
0
    result.mDrawInfo.mDrawingFlags = frameFlags;
7628
0
  }
7629
0
7630
0
  int32_t corsmode;
7631
0
  if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
7632
0
    result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE);
7633
0
  }
7634
0
7635
0
  result.mPrincipal = principal.forget();
7636
0
  // no images, including SVG images, can load content from another domain.
7637
0
  result.mIsWriteOnly = false;
7638
0
  result.mImageRequest = imgRequest.forget();
7639
0
  return result;
7640
0
}
7641
7642
nsLayoutUtils::SurfaceFromElementResult
7643
nsLayoutUtils::SurfaceFromElement(HTMLImageElement *aElement,
7644
                                  uint32_t aSurfaceFlags,
7645
                                  RefPtr<DrawTarget>& aTarget)
7646
0
{
7647
0
  return SurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement),
7648
0
                            aSurfaceFlags, aTarget);
7649
0
}
7650
7651
nsLayoutUtils::SurfaceFromElementResult
7652
nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement,
7653
                                  uint32_t aSurfaceFlags,
7654
                                  RefPtr<DrawTarget>& aTarget)
7655
0
{
7656
0
  SurfaceFromElementResult result;
7657
0
7658
0
  IntSize size = aElement->GetSize();
7659
0
7660
0
  result.mSourceSurface = aElement->GetSurfaceSnapshot(&result.mAlphaType);
7661
0
  if (!result.mSourceSurface) {
7662
0
    // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just
7663
0
    // draw nothing, so return an empty surface.
7664
0
    result.mAlphaType = gfxAlphaType::Opaque;
7665
0
    RefPtr<DrawTarget> ref =
7666
0
      aTarget ? aTarget
7667
0
              : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
7668
0
    RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
7669
0
                                                        SurfaceFormat::B8G8R8A8);
7670
0
    if (dt) {
7671
0
      result.mSourceSurface = dt->Snapshot();
7672
0
    }
7673
0
  } else if (aTarget) {
7674
0
    RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
7675
0
    if (opt) {
7676
0
      result.mSourceSurface = opt;
7677
0
    }
7678
0
  }
7679
0
7680
0
  // Ensure that any future changes to the canvas trigger proper invalidation,
7681
0
  // in case this is being used by -moz-element()
7682
0
  aElement->MarkContextClean();
7683
0
7684
0
  result.mHasSize = true;
7685
0
  result.mSize = size;
7686
0
  result.mPrincipal = aElement->NodePrincipal();
7687
0
  result.mIsWriteOnly = aElement->IsWriteOnly();
7688
0
7689
0
  return result;
7690
0
}
7691
7692
nsLayoutUtils::SurfaceFromElementResult
7693
nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
7694
                                  uint32_t aSurfaceFlags,
7695
                                  RefPtr<DrawTarget>& aTarget)
7696
0
{
7697
0
  SurfaceFromElementResult result;
7698
0
  result.mAlphaType = gfxAlphaType::Opaque; // Assume opaque.
7699
0
7700
0
  if (aElement->ContainsRestrictedContent()) {
7701
0
    return result;
7702
0
  }
7703
0
7704
0
  uint16_t readyState = aElement->ReadyState();
7705
0
  if (readyState == HAVE_NOTHING ||
7706
0
      readyState == HAVE_METADATA) {
7707
0
    result.mIsStillLoading = true;
7708
0
    return result;
7709
0
  }
7710
0
7711
0
  // If it doesn't have a principal, just bail
7712
0
  nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentVideoPrincipal();
7713
0
  if (!principal)
7714
0
    return result;
7715
0
7716
0
  result.mLayersImage = aElement->GetCurrentImage();
7717
0
  if (!result.mLayersImage)
7718
0
    return result;
7719
0
7720
0
  if (aTarget) {
7721
0
    // They gave us a DrawTarget to optimize for, so even though we have a layers::Image,
7722
0
    // we should unconditionally grab a SourceSurface and try to optimize it.
7723
0
    result.mSourceSurface = result.mLayersImage->GetAsSourceSurface();
7724
0
    if (!result.mSourceSurface)
7725
0
      return result;
7726
0
7727
0
    RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
7728
0
    if (opt) {
7729
0
      result.mSourceSurface = opt;
7730
0
    }
7731
0
  }
7732
0
7733
0
  result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE;
7734
0
  result.mHasSize = true;
7735
0
  result.mSize = result.mLayersImage->GetSize();
7736
0
  result.mPrincipal = principal.forget();
7737
0
  result.mIsWriteOnly = false;
7738
0
7739
0
  return result;
7740
0
}
7741
7742
nsLayoutUtils::SurfaceFromElementResult
7743
nsLayoutUtils::SurfaceFromElement(dom::Element* aElement,
7744
                                  uint32_t aSurfaceFlags,
7745
                                  RefPtr<DrawTarget>& aTarget)
7746
0
{
7747
0
  // If it's a <canvas>, we may be able to just grab its internal surface
7748
0
  if (HTMLCanvasElement* canvas =
7749
0
        HTMLCanvasElement::FromNodeOrNull(aElement)) {
7750
0
    return SurfaceFromElement(canvas, aSurfaceFlags, aTarget);
7751
0
  }
7752
0
7753
0
  // Maybe it's <video>?
7754
0
  if (HTMLVideoElement* video =
7755
0
        HTMLVideoElement::FromNodeOrNull(aElement)) {
7756
0
    return SurfaceFromElement(video, aSurfaceFlags, aTarget);
7757
0
  }
7758
0
7759
0
  // Finally, check if it's a normal image
7760
0
  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
7761
0
7762
0
  if (!imageLoader) {
7763
0
    return SurfaceFromElementResult();
7764
0
  }
7765
0
7766
0
  return SurfaceFromElement(imageLoader, aSurfaceFlags, aTarget);
7767
0
}
7768
7769
/* static */
7770
Element*
7771
nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument* aDocument)
7772
0
{
7773
0
  // If the document is in designMode we should return nullptr.
7774
0
  if (!aDocument || aDocument->HasFlag(NODE_IS_EDITABLE)) {
7775
0
    return nullptr;
7776
0
  }
7777
0
7778
0
  // contenteditable only works with HTML document.
7779
0
  // XXXbz should this test IsHTMLOrXHTML(), or just IsHTML()?
7780
0
  if (!aDocument->IsHTMLOrXHTML()) {
7781
0
    return nullptr;
7782
0
  }
7783
0
7784
0
  Element* rootElement = aDocument->GetRootElement();
7785
0
  if (rootElement && rootElement->IsEditable()) {
7786
0
    return rootElement;
7787
0
  }
7788
0
7789
0
  // If there is no editable root element, check its <body> element.
7790
0
  // Note that the body element could be <frameset> element.
7791
0
  Element* bodyElement = aDocument->GetBody();
7792
0
  if (bodyElement && bodyElement->IsEditable()) {
7793
0
    return bodyElement;
7794
0
  }
7795
0
  return nullptr;
7796
0
}
7797
7798
#ifdef DEBUG
7799
/* static */ void
7800
nsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame* aContainer,
7801
                                              const nsFrameList& aFrameList)
7802
{
7803
  for (nsIFrame* f : aFrameList) {
7804
    // Check only later continuations of f; we deal with checking the
7805
    // earlier continuations when we hit those earlier continuations in
7806
    // the frame list.
7807
    for (nsIFrame *c = f; (c = c->GetNextInFlow());) {
7808
      NS_ASSERTION(c->GetParent() != aContainer ||
7809
                   !aFrameList.ContainsFrame(c),
7810
                   "Two continuations of the same frame in the same "
7811
                   "frame list");
7812
    }
7813
  }
7814
}
7815
7816
// Is one of aFrame's ancestors a letter frame?
7817
static bool
7818
IsInLetterFrame(nsIFrame *aFrame)
7819
{
7820
  for (nsIFrame *f = aFrame->GetParent(); f; f = f->GetParent()) {
7821
    if (f->IsLetterFrame()) {
7822
      return true;
7823
    }
7824
  }
7825
  return false;
7826
}
7827
7828
/* static */ void
7829
nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot)
7830
{
7831
  NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(),
7832
               "frame tree not empty, but caller reported complete status");
7833
7834
  // Also assert that text frames map no text.
7835
  int32_t start, end;
7836
  nsresult rv = aSubtreeRoot->GetOffsets(start, end);
7837
  NS_ASSERTION(NS_SUCCEEDED(rv), "GetOffsets failed");
7838
  // In some cases involving :first-letter, we'll partially unlink a
7839
  // continuation in the middle of a continuation chain from its
7840
  // previous and next continuations before destroying it, presumably so
7841
  // that we don't also destroy the later continuations.  Once we've
7842
  // done this, GetOffsets returns incorrect values.
7843
  // For examples, see list of tests in
7844
  // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29
7845
  NS_ASSERTION(start == end || IsInLetterFrame(aSubtreeRoot),
7846
               "frame tree not empty, but caller reported complete status");
7847
7848
  nsIFrame::ChildListIterator lists(aSubtreeRoot);
7849
  for (; !lists.IsDone(); lists.Next()) {
7850
    nsFrameList::Enumerator childFrames(lists.CurrentList());
7851
    for (; !childFrames.AtEnd(); childFrames.Next()) {
7852
      nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames.get());
7853
    }
7854
  }
7855
}
7856
#endif
7857
7858
static void
7859
GetFontFacesForFramesInner(nsIFrame* aFrame,
7860
                           nsLayoutUtils::UsedFontFaceTable& aFontFaces,
7861
                           uint32_t aMaxRanges,
7862
                           bool aSkipCollapsedWhitespace)
7863
0
{
7864
0
  MOZ_ASSERT(aFrame, "NULL frame pointer");
7865
0
7866
0
  if (aFrame->IsTextFrame()) {
7867
0
    if (!aFrame->GetPrevContinuation()) {
7868
0
      nsLayoutUtils::GetFontFacesForText(aFrame, 0, INT32_MAX, true,
7869
0
                                         aFontFaces, aMaxRanges,
7870
0
                                         aSkipCollapsedWhitespace);
7871
0
    }
7872
0
    return;
7873
0
  }
7874
0
7875
0
  nsIFrame::ChildListID childLists[] = { nsIFrame::kPrincipalList,
7876
0
                                         nsIFrame::kPopupList };
7877
0
  for (size_t i = 0; i < ArrayLength(childLists); ++i) {
7878
0
    nsFrameList children(aFrame->GetChildList(childLists[i]));
7879
0
    for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
7880
0
      nsIFrame* child = e.get();
7881
0
      child = nsPlaceholderFrame::GetRealFrameFor(child);
7882
0
      GetFontFacesForFramesInner(child, aFontFaces, aMaxRanges,
7883
0
                                 aSkipCollapsedWhitespace);
7884
0
    }
7885
0
  }
7886
0
}
7887
7888
/* static */ nsresult
7889
nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame,
7890
                                     UsedFontFaceTable& aFontFaces,
7891
                                     uint32_t aMaxRanges,
7892
                                     bool aSkipCollapsedWhitespace)
7893
0
{
7894
0
  MOZ_ASSERT(aFrame, "NULL frame pointer");
7895
0
7896
0
  while (aFrame) {
7897
0
    GetFontFacesForFramesInner(aFrame, aFontFaces, aMaxRanges,
7898
0
                               aSkipCollapsedWhitespace);
7899
0
    aFrame = GetNextContinuationOrIBSplitSibling(aFrame);
7900
0
  }
7901
0
7902
0
  return NS_OK;
7903
0
}
7904
7905
static void
7906
AddFontsFromTextRun(gfxTextRun* aTextRun,
7907
                    nsTextFrame* aFrame,
7908
                    gfxSkipCharsIterator& aSkipIter,
7909
                    const gfxTextRun::Range& aRange,
7910
                    nsLayoutUtils::UsedFontFaceTable& aFontFaces,
7911
                    uint32_t aMaxRanges)
7912
0
{
7913
0
  gfxTextRun::GlyphRunIterator glyphRuns(aTextRun, aRange);
7914
0
  nsIContent* content = aFrame->GetContent();
7915
0
  int32_t contentLimit = aFrame->GetContentOffset() +
7916
0
                         aFrame->GetInFlowContentLength();
7917
0
  while (glyphRuns.NextRun()) {
7918
0
    gfxFontEntry *fe = glyphRuns.GetGlyphRun()->mFont->GetFontEntry();
7919
0
    // if we have already listed this face, just make sure the match type is
7920
0
    // recorded
7921
0
    InspectorFontFace* fontFace = aFontFaces.Get(fe);
7922
0
    if (fontFace) {
7923
0
      fontFace->AddMatchType(glyphRuns.GetGlyphRun()->mMatchType);
7924
0
    } else {
7925
0
      // A new font entry we haven't seen before
7926
0
      fontFace = new InspectorFontFace(fe, aTextRun->GetFontGroup(),
7927
0
                                       glyphRuns.GetGlyphRun()->mMatchType);
7928
0
      aFontFaces.Put(fe, fontFace);
7929
0
    }
7930
0
7931
0
    // Add this glyph run to the fontFace's list of ranges, unless we have
7932
0
    // already collected as many as wanted.
7933
0
    if (fontFace->RangeCount() < aMaxRanges) {
7934
0
      int32_t start =
7935
0
        aSkipIter.ConvertSkippedToOriginal(glyphRuns.GetStringStart());
7936
0
      int32_t end =
7937
0
        aSkipIter.ConvertSkippedToOriginal(glyphRuns.GetStringEnd());
7938
0
7939
0
      // Mapping back from textrun offsets ("skipped" offsets that reflect the
7940
0
      // text after whitespace collapsing, etc) to DOM content offsets in the
7941
0
      // original text is ambiguous, because many original characters can
7942
0
      // map to a single skipped offset. aSkipIter.ConvertSkippedToOriginal()
7943
0
      // will return an "original" offset that corresponds to the *end* of
7944
0
      // a collapsed run of characters in this case; but that might extend
7945
0
      // beyond the current content node if the textrun mapped multiple nodes.
7946
0
      // So we clamp the end offset to keep it valid for the content node
7947
0
      // that corresponds to the current textframe.
7948
0
      end = std::min(end, contentLimit);
7949
0
7950
0
      if (end > start) {
7951
0
        RefPtr<nsRange> range;
7952
0
        if (NS_FAILED(nsRange::CreateRange(content, start, content, end,
7953
0
                                           getter_AddRefs(range)))) {
7954
0
          NS_WARNING("failed to create range");
7955
0
        } else {
7956
0
          fontFace->AddRange(range);
7957
0
        }
7958
0
      }
7959
0
    }
7960
0
  }
7961
0
}
7962
7963
/* static */ void
7964
nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame,
7965
                                   int32_t aStartOffset,
7966
                                   int32_t aEndOffset,
7967
                                   bool aFollowContinuations,
7968
                                   UsedFontFaceTable& aFontFaces,
7969
                                   uint32_t aMaxRanges,
7970
                                   bool aSkipCollapsedWhitespace)
7971
0
{
7972
0
  MOZ_ASSERT(aFrame, "NULL frame pointer");
7973
0
7974
0
  if (!aFrame->IsTextFrame()) {
7975
0
    return;
7976
0
  }
7977
0
7978
0
  if (!aFrame->StyleVisibility()->IsVisible()) {
7979
0
    return;
7980
0
  }
7981
0
7982
0
  nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame);
7983
0
  do {
7984
0
    int32_t fstart = std::max(curr->GetContentOffset(), aStartOffset);
7985
0
    int32_t fend = std::min(curr->GetContentEnd(), aEndOffset);
7986
0
    if (fstart >= fend) {
7987
0
      curr = static_cast<nsTextFrame*>(curr->GetNextContinuation());
7988
0
      continue;
7989
0
    }
7990
0
7991
0
    // curr is overlapping with the offset we want
7992
0
    gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
7993
0
    gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
7994
0
    if (!textRun) {
7995
0
      NS_WARNING("failed to get textRun, low memory?");
7996
0
      return;
7997
0
    }
7998
0
7999
0
    // include continuations in the range that share the same textrun
8000
0
    nsTextFrame* next = nullptr;
8001
0
    if (aFollowContinuations && fend < aEndOffset) {
8002
0
      next = static_cast<nsTextFrame*>(curr->GetNextContinuation());
8003
0
      while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
8004
0
        fend = std::min(next->GetContentEnd(), aEndOffset);
8005
0
        next = fend < aEndOffset ?
8006
0
          static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr;
8007
0
      }
8008
0
    }
8009
0
8010
0
    if (!aSkipCollapsedWhitespace ||
8011
0
        (curr->HasAnyNoncollapsedCharacters() &&
8012
0
         curr->HasNonSuppressedText())) {
8013
0
      gfxTextRun::Range range(iter.ConvertOriginalToSkipped(fstart),
8014
0
                              iter.ConvertOriginalToSkipped(fend));
8015
0
      AddFontsFromTextRun(textRun, curr, iter, range, aFontFaces, aMaxRanges);
8016
0
    }
8017
0
8018
0
    curr = next;
8019
0
  } while (aFollowContinuations && curr);
8020
0
}
8021
8022
/* static */
8023
size_t
8024
nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame,
8025
                                       MallocSizeOf aMallocSizeOf,
8026
                                       bool clear)
8027
0
{
8028
0
  MOZ_ASSERT(aFrame, "NULL frame pointer");
8029
0
8030
0
  size_t total = 0;
8031
0
8032
0
  if (aFrame->IsTextFrame()) {
8033
0
    nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
8034
0
    for (uint32_t i = 0; i < 2; ++i) {
8035
0
      gfxTextRun *run = textFrame->GetTextRun(
8036
0
        (i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated);
8037
0
      if (run) {
8038
0
        if (clear) {
8039
0
          run->ResetSizeOfAccountingFlags();
8040
0
        } else {
8041
0
          total += run->MaybeSizeOfIncludingThis(aMallocSizeOf);
8042
0
        }
8043
0
      }
8044
0
    }
8045
0
    return total;
8046
0
  }
8047
0
8048
0
  AutoTArray<nsIFrame::ChildList,4> childListArray;
8049
0
  aFrame->GetChildLists(&childListArray);
8050
0
8051
0
  for (nsIFrame::ChildListArrayIterator childLists(childListArray);
8052
0
       !childLists.IsDone(); childLists.Next()) {
8053
0
    for (nsFrameList::Enumerator e(childLists.CurrentList());
8054
0
         !e.AtEnd(); e.Next()) {
8055
0
      total += SizeOfTextRunsForFrames(e.get(), aMallocSizeOf, clear);
8056
0
    }
8057
0
  }
8058
0
  return total;
8059
0
}
8060
8061
/* static */
8062
void
8063
nsLayoutUtils::Initialize()
8064
3
{
8065
3
  Preferences::AddUintVarCache(&sFontSizeInflationMaxRatio,
8066
3
                               "font.size.inflation.maxRatio");
8067
3
  Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine,
8068
3
                               "font.size.inflation.emPerLine");
8069
3
  Preferences::AddUintVarCache(&sFontSizeInflationMinTwips,
8070
3
                               "font.size.inflation.minTwips");
8071
3
  Preferences::AddUintVarCache(&sFontSizeInflationLineThreshold,
8072
3
                               "font.size.inflation.lineThreshold");
8073
3
  Preferences::AddIntVarCache(&sFontSizeInflationMappingIntercept,
8074
3
                              "font.size.inflation.mappingIntercept");
8075
3
  Preferences::AddBoolVarCache(&sFontSizeInflationForceEnabled,
8076
3
                               "font.size.inflation.forceEnabled");
8077
3
  Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess,
8078
3
                               "font.size.inflation.disabledInMasterProcess");
8079
3
  Preferences::AddUintVarCache(&sSystemFontScale,
8080
3
                               "font.size.systemFontScale", 100);
8081
3
  Preferences::AddUintVarCache(&sZoomMaxPercent,
8082
3
                               "zoom.maxPercent", 300);
8083
3
  Preferences::AddUintVarCache(&sZoomMinPercent,
8084
3
                               "zoom.minPercent", 30);
8085
3
  Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled,
8086
3
                               "nglayout.debug.invalidation");
8087
3
  Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled,
8088
3
                               "layout.interruptible-reflow.enabled");
8089
3
  Preferences::AddBoolVarCache(&sSVGTransformBoxEnabled,
8090
3
                               "svg.transform-box.enabled");
8091
3
  Preferences::AddUintVarCache(&sIdlePeriodDeadlineLimit,
8092
3
                               "layout.idle_period.time_limit",
8093
3
                               DEFAULT_IDLE_PERIOD_TIME_LIMIT);
8094
3
  Preferences::AddUintVarCache(&sQuiescentFramesBeforeIdlePeriod,
8095
3
                               "layout.idle_period.required_quiescent_frames",
8096
3
                               DEFAULT_QUIESCENT_FRAMES);
8097
3
8098
3
  nsComputedDOMStyle::RegisterPrefChangeCallbacks();
8099
3
}
8100
8101
/* static */
8102
void
8103
nsLayoutUtils::Shutdown()
8104
0
{
8105
0
  if (sContentMap) {
8106
0
    delete sContentMap;
8107
0
    sContentMap = nullptr;
8108
0
  }
8109
0
8110
0
  nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
8111
0
8112
0
  // so the cached initial quotes array doesn't appear to be a leak
8113
0
  nsStyleList::Shutdown();
8114
0
}
8115
8116
/* static */
8117
void
8118
nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
8119
                                    imgIRequest* aRequest,
8120
                                    bool* aRequestRegistered)
8121
0
{
8122
0
  if (!aPresContext) {
8123
0
    return;
8124
0
  }
8125
0
8126
0
  if (aRequestRegistered && *aRequestRegistered) {
8127
0
    // Our request is already registered with the refresh driver, so
8128
0
    // no need to register it again.
8129
0
    return;
8130
0
  }
8131
0
8132
0
  if (aRequest) {
8133
0
    if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
8134
0
      NS_WARNING("Unable to add image request");
8135
0
      return;
8136
0
    }
8137
0
8138
0
    if (aRequestRegistered) {
8139
0
      *aRequestRegistered = true;
8140
0
    }
8141
0
  }
8142
0
}
8143
8144
/* static */
8145
void
8146
nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
8147
                                              imgIRequest* aRequest,
8148
                                              bool* aRequestRegistered)
8149
0
{
8150
0
  if (!aPresContext) {
8151
0
    return;
8152
0
  }
8153
0
8154
0
  if (aRequestRegistered && *aRequestRegistered) {
8155
0
    // Our request is already registered with the refresh driver, so
8156
0
    // no need to register it again.
8157
0
    return;
8158
0
  }
8159
0
8160
0
  if (aRequest) {
8161
0
    nsCOMPtr<imgIContainer> image;
8162
0
    if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
8163
0
      // Check to verify that the image is animated. If so, then add it to the
8164
0
      // list of images tracked by the refresh driver.
8165
0
      bool isAnimated = false;
8166
0
      nsresult rv = image->GetAnimated(&isAnimated);
8167
0
      if (NS_SUCCEEDED(rv) && isAnimated) {
8168
0
        if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
8169
0
          NS_WARNING("Unable to add image request");
8170
0
          return;
8171
0
        }
8172
0
8173
0
        if (aRequestRegistered) {
8174
0
          *aRequestRegistered = true;
8175
0
        }
8176
0
      }
8177
0
    }
8178
0
  }
8179
0
}
8180
8181
/* static */
8182
void
8183
nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
8184
                                      imgIRequest* aRequest,
8185
                                      bool* aRequestRegistered)
8186
0
{
8187
0
  if (!aPresContext) {
8188
0
    return;
8189
0
  }
8190
0
8191
0
  // Deregister our imgIRequest with the refresh driver to
8192
0
  // complete tear-down, but only if it has been registered
8193
0
  if (aRequestRegistered && !*aRequestRegistered) {
8194
0
    return;
8195
0
  }
8196
0
8197
0
  if (aRequest) {
8198
0
    nsCOMPtr<imgIContainer> image;
8199
0
    if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
8200
0
      aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);
8201
0
8202
0
      if (aRequestRegistered) {
8203
0
        *aRequestRegistered = false;
8204
0
      }
8205
0
    }
8206
0
  }
8207
0
}
8208
8209
/* static */
8210
void
8211
nsLayoutUtils::PostRestyleEvent(Element* aElement,
8212
                                nsRestyleHint aRestyleHint,
8213
                                nsChangeHint aMinChangeHint)
8214
0
{
8215
0
  nsIDocument* doc = aElement->GetComposedDoc();
8216
0
  if (doc) {
8217
0
    RefPtr<nsPresContext> presContext = doc->GetPresContext();
8218
0
    if (presContext) {
8219
0
      presContext->RestyleManager()->PostRestyleEvent(
8220
0
        aElement, aRestyleHint, aMinChangeHint);
8221
0
    }
8222
0
  }
8223
0
}
8224
8225
nsSetAttrRunnable::nsSetAttrRunnable(Element* aElement,
8226
                                     nsAtom* aAttrName,
8227
                                     const nsAString& aValue)
8228
  : mozilla::Runnable("nsSetAttrRunnable")
8229
  , mElement(aElement)
8230
  , mAttrName(aAttrName)
8231
  , mValue(aValue)
8232
0
{
8233
0
  NS_ASSERTION(aElement && aAttrName, "Missing stuff, prepare to crash");
8234
0
}
8235
8236
nsSetAttrRunnable::nsSetAttrRunnable(Element* aElement,
8237
                                     nsAtom* aAttrName,
8238
                                     int32_t aValue)
8239
  : mozilla::Runnable("nsSetAttrRunnable")
8240
  , mElement(aElement)
8241
  , mAttrName(aAttrName)
8242
0
{
8243
0
  NS_ASSERTION(aElement && aAttrName, "Missing stuff, prepare to crash");
8244
0
  mValue.AppendInt(aValue);
8245
0
}
8246
8247
NS_IMETHODIMP
8248
nsSetAttrRunnable::Run()
8249
0
{
8250
0
  return mElement->SetAttr(kNameSpaceID_None, mAttrName, mValue, true);
8251
0
}
8252
8253
nsUnsetAttrRunnable::nsUnsetAttrRunnable(Element* aElement,
8254
                                         nsAtom* aAttrName)
8255
  : mozilla::Runnable("nsUnsetAttrRunnable")
8256
  , mElement(aElement)
8257
  , mAttrName(aAttrName)
8258
0
{
8259
0
  NS_ASSERTION(aElement && aAttrName, "Missing stuff, prepare to crash");
8260
0
}
8261
8262
NS_IMETHODIMP
8263
nsUnsetAttrRunnable::Run()
8264
0
{
8265
0
  return mElement->UnsetAttr(kNameSpaceID_None, mAttrName, true);
8266
0
}
8267
8268
/**
8269
 * Compute the minimum font size inside of a container with the given
8270
 * width, such that **when the user zooms the container to fill the full
8271
 * width of the device**, the fonts satisfy our minima.
8272
 */
8273
static nscoord
8274
MinimumFontSizeFor(nsPresContext* aPresContext, WritingMode aWritingMode,
8275
                   nscoord aContainerISize)
8276
0
{
8277
0
  nsIPresShell* presShell = aPresContext->PresShell();
8278
0
8279
0
  uint32_t emPerLine = presShell->FontSizeInflationEmPerLine();
8280
0
  uint32_t minTwips = presShell->FontSizeInflationMinTwips();
8281
0
  if (emPerLine == 0 && minTwips == 0) {
8282
0
    return 0;
8283
0
  }
8284
0
8285
0
  // Clamp the container width to the device dimensions
8286
0
  nscoord iFrameISize = aWritingMode.IsVertical()
8287
0
    ? aPresContext->GetVisibleArea().height
8288
0
    : aPresContext->GetVisibleArea().width;
8289
0
  nscoord effectiveContainerISize = std::min(iFrameISize, aContainerISize);
8290
0
8291
0
  nscoord byLine = 0, byInch = 0;
8292
0
  if (emPerLine != 0) {
8293
0
    byLine = effectiveContainerISize / emPerLine;
8294
0
  }
8295
0
  if (minTwips != 0) {
8296
0
    // REVIEW: Is this giving us app units and sizes *not* counting
8297
0
    // viewport scaling?
8298
0
    gfxSize screenSize = aPresContext->ScreenSizeInchesForFontInflation();
8299
0
    float deviceISizeInches = aWritingMode.IsVertical()
8300
0
      ? screenSize.height : screenSize.width;
8301
0
    byInch = NSToCoordRound(effectiveContainerISize /
8302
0
                            (deviceISizeInches * 1440 /
8303
0
                             minTwips ));
8304
0
  }
8305
0
  return std::max(byLine, byInch);
8306
0
}
8307
8308
/* static */ float
8309
nsLayoutUtils::FontSizeInflationInner(const nsIFrame *aFrame,
8310
                                      nscoord aMinFontSize)
8311
0
{
8312
0
  // Note that line heights should be inflated by the same ratio as the
8313
0
  // font size of the same text; thus we operate only on the font size
8314
0
  // even when we're scaling a line height.
8315
0
  nscoord styleFontSize = aFrame->StyleFont()->mFont.size;
8316
0
  if (styleFontSize <= 0) {
8317
0
    // Never scale zero font size.
8318
0
    return 1.0;
8319
0
  }
8320
0
8321
0
  if (aMinFontSize <= 0) {
8322
0
    // No need to scale.
8323
0
    return 1.0;
8324
0
  }
8325
0
8326
0
  // If between this current frame and its font inflation container there is a
8327
0
  // non-inline element with fixed width or height, then we should not inflate
8328
0
  // fonts for this frame.
8329
0
  for (const nsIFrame* f = aFrame;
8330
0
       f && !f->IsContainerForFontSizeInflation();
8331
0
       f = f->GetParent()) {
8332
0
    nsIContent* content = f->GetContent();
8333
0
    LayoutFrameType fType = f->Type();
8334
0
    nsIFrame* parent = f->GetParent();
8335
0
    // Also, if there is more than one frame corresponding to a single
8336
0
    // content node, we want the outermost one.
8337
0
    if (!(parent && parent->GetContent() == content) &&
8338
0
        // ignore width/height on inlines since they don't apply
8339
0
        fType != LayoutFrameType::Inline &&
8340
0
        // ignore width on radios and checkboxes since we enlarge them and
8341
0
        // they have width/height in ua.css
8342
0
        fType != LayoutFrameType::CheckboxRadio) {
8343
0
      // ruby annotations should have the same inflation as its
8344
0
      // grandparent, which is the ruby frame contains the annotation.
8345
0
      if (fType == LayoutFrameType::RubyText) {
8346
0
        MOZ_ASSERT(parent && parent->IsRubyTextContainerFrame());
8347
0
        nsIFrame* grandparent = parent->GetParent();
8348
0
        MOZ_ASSERT(grandparent && grandparent->IsRubyFrame());
8349
0
        return FontSizeInflationFor(grandparent);
8350
0
      }
8351
0
      nsStyleCoord stylePosWidth = f->StylePosition()->mWidth;
8352
0
      nsStyleCoord stylePosHeight = f->StylePosition()->mHeight;
8353
0
      if (stylePosWidth.GetUnit() != eStyleUnit_Auto ||
8354
0
          stylePosHeight.GetUnit() != eStyleUnit_Auto) {
8355
0
8356
0
        return 1.0;
8357
0
      }
8358
0
    }
8359
0
  }
8360
0
8361
0
  int32_t interceptParam = nsLayoutUtils::FontSizeInflationMappingIntercept();
8362
0
  float maxRatio = (float)nsLayoutUtils::FontSizeInflationMaxRatio() / 100.0f;
8363
0
8364
0
  float ratio = float(styleFontSize) / float(aMinFontSize);
8365
0
  float inflationRatio;
8366
0
8367
0
  // Given a minimum inflated font size m, a specified font size s, we want to
8368
0
  // find the inflated font size i and then return the ratio of i to s (i/s).
8369
0
  if (interceptParam >= 0) {
8370
0
    // Since the mapping intercept parameter P is greater than zero, we use it
8371
0
    // to determine the point where our mapping function intersects the i=s
8372
0
    // line. This means that we have an equation of the form:
8373
0
    //
8374
0
    // i = m + s·(P/2)/(1 + P/2), if s <= (1 + P/2)·m
8375
0
    // i = s, if s >= (1 + P/2)·m
8376
0
8377
0
    float intercept = 1 + float(interceptParam)/2.0f;
8378
0
    if (ratio >= intercept) {
8379
0
      // If we're already at 1+P/2 or more times the minimum, don't scale.
8380
0
      return 1.0;
8381
0
    }
8382
0
8383
0
    // The point (intercept, intercept) is where the part of the i vs. s graph
8384
0
    // that's not slope 1 meets the i=s line.  (This part of the
8385
0
    // graph is a line from (0, m), to that point). We calculate the
8386
0
    // intersection point to be ((1+P/2)m, (1+P/2)m), where P is the
8387
0
    // intercept parameter above. We then need to return i/s.
8388
0
    inflationRatio = (1.0f + (ratio * (intercept - 1) / intercept)) / ratio;
8389
0
  } else {
8390
0
    // This is the case where P is negative. We essentially want to implement
8391
0
    // the case for P=infinity here, so we make i = s + m, which means that
8392
0
    // i/s = s/s + m/s = 1 + 1/ratio
8393
0
    inflationRatio = 1 + 1.0f / ratio;
8394
0
  }
8395
0
8396
0
  if (maxRatio > 1.0 && inflationRatio > maxRatio) {
8397
0
    return maxRatio;
8398
0
  } else {
8399
0
    return inflationRatio;
8400
0
  }
8401
0
}
8402
8403
static bool
8404
ShouldInflateFontsForContainer(const nsIFrame *aFrame)
8405
0
{
8406
0
  // We only want to inflate fonts for text that is in a place
8407
0
  // with room to expand.  The question is what the best heuristic for
8408
0
  // that is...
8409
0
  // For now, we're going to use NS_FRAME_IN_CONSTRAINED_BSIZE, which
8410
0
  // indicates whether the frame is inside something with a constrained
8411
0
  // block-size (propagating down the tree), but the propagation stops when
8412
0
  // we hit overflow-y [or -x, for vertical mode]: scroll or auto.
8413
0
  const nsStyleText* styleText = aFrame->StyleText();
8414
0
8415
0
  return styleText->mTextSizeAdjust != NS_STYLE_TEXT_SIZE_ADJUST_NONE &&
8416
0
         !(aFrame->GetStateBits() & NS_FRAME_IN_CONSTRAINED_BSIZE) &&
8417
0
         // We also want to disable font inflation for containers that have
8418
0
         // preformatted text.
8419
0
         // MathML cells need special treatment. See bug 1002526 comment 56.
8420
0
         (styleText->WhiteSpaceCanWrap(aFrame) ||
8421
0
          aFrame->IsFrameOfType(nsIFrame::eMathML));
8422
0
}
8423
8424
nscoord
8425
nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame *aFrame)
8426
0
{
8427
0
  nsPresContext *presContext = aFrame->PresContext();
8428
0
  if (!FontSizeInflationEnabled(presContext) ||
8429
0
      presContext->mInflationDisabledForShrinkWrap) {
8430
0
    return 0;
8431
0
  }
8432
0
8433
0
  for (const nsIFrame *f = aFrame; f; f = f->GetParent()) {
8434
0
    if (f->IsContainerForFontSizeInflation()) {
8435
0
      if (!ShouldInflateFontsForContainer(f)) {
8436
0
        return 0;
8437
0
      }
8438
0
8439
0
      nsFontInflationData *data =
8440
0
        nsFontInflationData::FindFontInflationDataFor(aFrame);
8441
0
      // FIXME: The need to null-check here is sort of a bug, and might
8442
0
      // lead to incorrect results.
8443
0
      if (!data || !data->InflationEnabled()) {
8444
0
        return 0;
8445
0
      }
8446
0
8447
0
      return MinimumFontSizeFor(aFrame->PresContext(),
8448
0
                                aFrame->GetWritingMode(),
8449
0
                                data->EffectiveISize());
8450
0
    }
8451
0
  }
8452
0
8453
0
  MOZ_ASSERT(false, "root should always be container");
8454
0
8455
0
  return 0;
8456
0
}
8457
8458
float
8459
nsLayoutUtils::FontSizeInflationFor(const nsIFrame *aFrame)
8460
0
{
8461
0
  if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
8462
0
    const nsIFrame* container = aFrame;
8463
0
    while (!container->IsSVGTextFrame()) {
8464
0
      container = container->GetParent();
8465
0
    }
8466
0
    NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
8467
0
    return
8468
0
      static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
8469
0
  }
8470
0
8471
0
  if (!FontSizeInflationEnabled(aFrame->PresContext())) {
8472
0
    return 1.0f;
8473
0
  }
8474
0
8475
0
  return FontSizeInflationInner(aFrame, InflationMinFontSizeFor(aFrame));
8476
0
}
8477
8478
/* static */ bool
8479
nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext)
8480
0
{
8481
0
  nsIPresShell* presShell = aPresContext->GetPresShell();
8482
0
8483
0
  if (!presShell) {
8484
0
    return false;
8485
0
  }
8486
0
8487
0
  return presShell->FontSizeInflationEnabled();
8488
0
}
8489
8490
/* static */ nsRect
8491
nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame,
8492
                                        const nsSize& aFrameSize)
8493
0
{
8494
0
  nsCSSShadowArray* boxShadows = aFrame->StyleEffects()->mBoxShadow;
8495
0
  if (!boxShadows) {
8496
0
    return nsRect();
8497
0
  }
8498
0
8499
0
  nsRect inputRect(nsPoint(0, 0), aFrameSize);
8500
0
8501
0
  // According to the CSS spec, box-shadow should be based on the border box.
8502
0
  // However, that looks broken when the background extends outside the border
8503
0
  // box, as can be the case with native theming.  To fix that we expand the
8504
0
  // area that we shadow to include the bounds of any native theme drawing.
8505
0
  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
8506
0
  nsITheme::Transparency transparency;
8507
0
  if (aFrame->IsThemed(styleDisplay, &transparency)) {
8508
0
    // For opaque (rectangular) theme widgets we can take the generic
8509
0
    // border-box path with border-radius disabled.
8510
0
    if (transparency != nsITheme::eOpaque) {
8511
0
      nsPresContext *presContext = aFrame->PresContext();
8512
0
      presContext->GetTheme()->
8513
0
        GetWidgetOverflow(presContext->DeviceContext(), aFrame,
8514
0
                          styleDisplay->mAppearance, &inputRect);
8515
0
    }
8516
0
  }
8517
0
8518
0
  nsRect shadows;
8519
0
  int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
8520
0
  for (uint32_t i = 0; i < boxShadows->Length(); ++i) {
8521
0
    nsRect tmpRect = inputRect;
8522
0
    nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
8523
0
8524
0
    // inset shadows are never painted outside the frame
8525
0
    if (shadow->mInset)
8526
0
      continue;
8527
0
8528
0
    tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
8529
0
    tmpRect.Inflate(shadow->mSpread);
8530
0
    tmpRect.Inflate(
8531
0
      nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D));
8532
0
    shadows.UnionRect(shadows, tmpRect);
8533
0
  }
8534
0
  return shadows;
8535
0
}
8536
8537
/* static */ bool
8538
nsLayoutUtils::GetContentViewerSize(nsPresContext* aPresContext,
8539
                                    LayoutDeviceIntSize& aOutSize)
8540
0
{
8541
0
  nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
8542
0
  if (!docShell) {
8543
0
    return false;
8544
0
  }
8545
0
8546
0
  nsCOMPtr<nsIContentViewer> cv;
8547
0
  docShell->GetContentViewer(getter_AddRefs(cv));
8548
0
  if (!cv) {
8549
0
    return false;
8550
0
  }
8551
0
8552
0
  nsIntRect bounds;
8553
0
  cv->GetBounds(bounds);
8554
0
  aOutSize = LayoutDeviceIntRect::FromUnknownRect(bounds).Size();
8555
0
  return true;
8556
0
}
8557
8558
static bool
8559
UpdateCompositionBoundsForRCDRSF(ParentLayerRect& aCompBounds,
8560
                                 nsPresContext* aPresContext,
8561
                                 bool aScaleContentViewerSize)
8562
0
{
8563
0
  nsIFrame* rootFrame = aPresContext->PresShell()->GetRootFrame();
8564
0
  if (!rootFrame) {
8565
0
    return false;
8566
0
  }
8567
0
8568
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
8569
  nsIWidget* widget = rootFrame->GetNearestWidget();
8570
#else
8571
0
  nsView* view = rootFrame->GetView();
8572
0
  nsIWidget* widget = view ? view->GetWidget() : nullptr;
8573
0
#endif
8574
0
8575
0
  if (widget) {
8576
0
    LayoutDeviceIntRect widgetBounds = widget->GetBounds();
8577
0
    widgetBounds.MoveTo(0, 0);
8578
0
    aCompBounds = ParentLayerRect(
8579
0
      ViewAs<ParentLayerPixel>(
8580
0
        widgetBounds,
8581
0
        PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF));
8582
0
    return true;
8583
0
  }
8584
0
8585
0
  LayoutDeviceIntSize contentSize;
8586
0
  if (nsLayoutUtils::GetContentViewerSize(aPresContext, contentSize)) {
8587
0
    LayoutDeviceToParentLayerScale scale;
8588
0
    if (aScaleContentViewerSize && aPresContext->GetParentPresContext()) {
8589
0
      scale = LayoutDeviceToParentLayerScale(
8590
0
        aPresContext->GetParentPresContext()->PresShell()->GetCumulativeResolution());
8591
0
    }
8592
0
    aCompBounds.SizeTo(contentSize * scale);
8593
0
    return true;
8594
0
  }
8595
0
8596
0
  return false;
8597
0
}
8598
8599
/* static */ nsMargin
8600
nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(nsIFrame* aScrollFrame)
8601
0
{
8602
0
  if (!aScrollFrame || !aScrollFrame->GetScrollTargetFrame()) {
8603
0
    return nsMargin();
8604
0
  }
8605
0
  nsPresContext* presContext = aScrollFrame->PresContext();
8606
0
  nsIPresShell* presShell = presContext->GetPresShell();
8607
0
  if (!presShell) {
8608
0
    return nsMargin();
8609
0
  }
8610
0
  bool isRootScrollFrame = aScrollFrame == presShell->GetRootScrollFrame();
8611
0
  bool isRootContentDocRootScrollFrame = isRootScrollFrame
8612
0
                                      && presContext->IsRootContentDocument();
8613
0
  if (!isRootContentDocRootScrollFrame) {
8614
0
    return nsMargin();
8615
0
  }
8616
0
  if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
8617
0
    return nsMargin();
8618
0
  }
8619
0
  nsIScrollableFrame* scrollableFrame = aScrollFrame->GetScrollTargetFrame();
8620
0
  if (!scrollableFrame) {
8621
0
    return nsMargin();
8622
0
  }
8623
0
  return scrollableFrame->GetActualScrollbarSizes();
8624
0
}
8625
8626
/* static */ nsSize
8627
nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame, bool aSubtractScrollbars)
8628
0
{
8629
0
  // If we have a scrollable frame, restrict the composition bounds to its
8630
0
  // scroll port. The scroll port excludes the frame borders and the scroll
8631
0
  // bars, which we don't want to be part of the composition bounds.
8632
0
  nsIScrollableFrame* scrollableFrame = aFrame->GetScrollTargetFrame();
8633
0
  nsRect rect = scrollableFrame ? scrollableFrame->GetScrollPortRect() : aFrame->GetRect();
8634
0
  nsSize size = rect.Size();
8635
0
8636
0
  nsPresContext* presContext = aFrame->PresContext();
8637
0
  nsIPresShell* presShell = presContext->PresShell();
8638
0
8639
0
  bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument()
8640
0
                                      && aFrame == presShell->GetRootScrollFrame();
8641
0
  if (isRootContentDocRootScrollFrame) {
8642
0
    ParentLayerRect compBounds;
8643
0
    if (UpdateCompositionBoundsForRCDRSF(compBounds, presContext, false)) {
8644
0
      int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
8645
0
      size = nsSize(compBounds.width * auPerDevPixel, compBounds.height * auPerDevPixel);
8646
0
    }
8647
0
  }
8648
0
8649
0
  if (aSubtractScrollbars) {
8650
0
    nsMargin margins = ScrollbarAreaToExcludeFromCompositionBoundsFor(aFrame);
8651
0
    size.width -= margins.LeftRight();
8652
0
    size.height -= margins.TopBottom();
8653
0
  }
8654
0
8655
0
  return size;
8656
0
}
8657
8658
/* static */ CSSSize
8659
nsLayoutUtils::CalculateRootCompositionSize(nsIFrame* aFrame,
8660
                                            bool aIsRootContentDocRootScrollFrame,
8661
                                            const FrameMetrics& aMetrics)
8662
0
{
8663
0
8664
0
  if (aIsRootContentDocRootScrollFrame) {
8665
0
    return ViewAs<LayerPixel>(aMetrics.GetCompositionBounds().Size(),
8666
0
                              PixelCastJustification::ParentLayerToLayerForRootComposition)
8667
0
           * LayerToScreenScale(1.0f)
8668
0
           / aMetrics.DisplayportPixelsPerCSSPixel();
8669
0
  }
8670
0
  nsPresContext* presContext = aFrame->PresContext();
8671
0
  ScreenSize rootCompositionSize;
8672
0
  nsPresContext* rootPresContext =
8673
0
    presContext->GetToplevelContentDocumentPresContext();
8674
0
  if (!rootPresContext) {
8675
0
    rootPresContext = presContext->GetRootPresContext();
8676
0
  }
8677
0
  nsIPresShell* rootPresShell = nullptr;
8678
0
  if (rootPresContext) {
8679
0
    rootPresShell = rootPresContext->PresShell();
8680
0
    if (nsIFrame* rootFrame = rootPresShell->GetRootFrame()) {
8681
0
      LayoutDeviceToLayerScale2D cumulativeResolution(
8682
0
        rootPresShell->GetCumulativeResolution()
8683
0
      * nsLayoutUtils::GetTransformToAncestorScale(rootFrame));
8684
0
      ParentLayerRect compBounds;
8685
0
      if (UpdateCompositionBoundsForRCDRSF(compBounds, rootPresContext, true)) {
8686
0
        rootCompositionSize = ViewAs<ScreenPixel>(compBounds.Size(),
8687
0
            PixelCastJustification::ScreenIsParentLayerForRoot);
8688
0
      } else {
8689
0
        int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel();
8690
0
        LayerSize frameSize =
8691
0
          (LayoutDeviceRect::FromAppUnits(rootFrame->GetRect(), rootAUPerDevPixel)
8692
0
           * cumulativeResolution).Size();
8693
0
        rootCompositionSize = frameSize * LayerToScreenScale(1.0f);
8694
0
      }
8695
0
    }
8696
0
  } else {
8697
0
    nsIWidget* widget = aFrame->GetNearestWidget();
8698
0
    LayoutDeviceIntRect widgetBounds = widget->GetBounds();
8699
0
    rootCompositionSize = ScreenSize(
8700
0
      ViewAs<ScreenPixel>(widgetBounds.Size(),
8701
0
                          PixelCastJustification::LayoutDeviceIsScreenForBounds));
8702
0
  }
8703
0
8704
0
  // Adjust composition size for the size of scroll bars.
8705
0
  nsIFrame* rootRootScrollFrame = rootPresShell ? rootPresShell->GetRootScrollFrame() : nullptr;
8706
0
  nsMargin scrollbarMargins = ScrollbarAreaToExcludeFromCompositionBoundsFor(rootRootScrollFrame);
8707
0
  LayoutDeviceMargin margins = LayoutDeviceMargin::FromAppUnits(scrollbarMargins,
8708
0
    rootPresContext->AppUnitsPerDevPixel());
8709
0
  // Scrollbars are not subject to resolution scaling, so LD pixels = layer pixels for them.
8710
0
  rootCompositionSize.width -= margins.LeftRight();
8711
0
  rootCompositionSize.height -= margins.TopBottom();
8712
0
8713
0
  return rootCompositionSize / aMetrics.DisplayportPixelsPerCSSPixel();
8714
0
}
8715
8716
/* static */ nsRect
8717
nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame)
8718
0
{
8719
0
  nsRect contentBounds;
8720
0
  if (aScrollableFrame) {
8721
0
    contentBounds = aScrollableFrame->GetScrollRange();
8722
0
8723
0
    nsPoint scrollPosition = aScrollableFrame->GetScrollPosition();
8724
0
    if (aScrollableFrame->GetScrollStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
8725
0
      contentBounds.y = scrollPosition.y;
8726
0
      contentBounds.height = 0;
8727
0
    }
8728
0
    if (aScrollableFrame->GetScrollStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
8729
0
      contentBounds.x = scrollPosition.x;
8730
0
      contentBounds.width = 0;
8731
0
    }
8732
0
8733
0
    contentBounds.width += aScrollableFrame->GetScrollPortRect().width;
8734
0
    contentBounds.height += aScrollableFrame->GetScrollPortRect().height;
8735
0
  } else {
8736
0
    contentBounds = aRootFrame->GetRect();
8737
0
  }
8738
0
  return contentBounds;
8739
0
}
8740
8741
/* static */ nsRect
8742
nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame* aFrame)
8743
0
{
8744
0
  nsRect scrollableRect =
8745
0
    CalculateScrollableRectForFrame(aFrame->GetScrollTargetFrame(),
8746
0
                                    aFrame->PresShell()->GetRootFrame());
8747
0
  nsSize compSize = CalculateCompositionSizeForFrame(aFrame);
8748
0
8749
0
  if (aFrame == aFrame->PresShell()->GetRootScrollFrame()) {
8750
0
    // the composition size for the root scroll frame does not include the
8751
0
    // local resolution, so we adjust.
8752
0
    float res = aFrame->PresShell()->GetResolution();
8753
0
    compSize.width = NSToCoordRound(compSize.width / res);
8754
0
    compSize.height = NSToCoordRound(compSize.height / res);
8755
0
  }
8756
0
8757
0
  if (scrollableRect.width < compSize.width) {
8758
0
    scrollableRect.x = std::max(0,
8759
0
                                scrollableRect.x - (compSize.width - scrollableRect.width));
8760
0
    scrollableRect.width = compSize.width;
8761
0
  }
8762
0
8763
0
  if (scrollableRect.height < compSize.height) {
8764
0
    scrollableRect.y = std::max(0,
8765
0
                                scrollableRect.y - (compSize.height - scrollableRect.height));
8766
0
    scrollableRect.height = compSize.height;
8767
0
  }
8768
0
  return scrollableRect;
8769
0
}
8770
8771
/* static */ void
8772
nsLayoutUtils::DoLogTestDataForPaint(LayerManager* aManager,
8773
                                     ViewID aScrollId,
8774
                                     const std::string& aKey,
8775
                                     const std::string& aValue)
8776
0
{
8777
0
  MOZ_ASSERT(nsLayoutUtils::IsAPZTestLoggingEnabled(), "don't call me");
8778
0
  if (ClientLayerManager* mgr = aManager->AsClientLayerManager()) {
8779
0
    mgr->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
8780
0
  } else if (WebRenderLayerManager* wrlm = aManager->AsWebRenderLayerManager()) {
8781
0
    wrlm->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
8782
0
  }
8783
0
}
8784
8785
/* static */ bool
8786
nsLayoutUtils::IsAPZTestLoggingEnabled()
8787
0
{
8788
0
  return gfxPrefs::APZTestLoggingEnabled();
8789
0
}
8790
8791
////////////////////////////////////////
8792
// SurfaceFromElementResult
8793
8794
nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()
8795
  // Use safe default values here
8796
  : mIsWriteOnly(true)
8797
  , mIsStillLoading(false)
8798
  , mHasSize(false)
8799
  , mCORSUsed(false)
8800
  , mAlphaType(gfxAlphaType::Opaque)
8801
0
{
8802
0
}
8803
8804
const RefPtr<mozilla::gfx::SourceSurface>&
8805
nsLayoutUtils::SurfaceFromElementResult::GetSourceSurface()
8806
0
{
8807
0
  if (!mSourceSurface && mLayersImage) {
8808
0
    mSourceSurface = mLayersImage->GetAsSourceSurface();
8809
0
  }
8810
0
8811
0
  return mSourceSurface;
8812
0
}
8813
8814
////////////////////////////////////////
8815
8816
bool
8817
nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame)
8818
0
{
8819
0
  return GetAsBlock(aFrame) && !aFrame->IsBlockWrapper();
8820
0
}
8821
8822
bool
8823
nsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext* aPresContext)
8824
0
{
8825
0
  return aPresContext->IsRootPaginatedDocument() &&
8826
0
    (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
8827
0
     aPresContext->Type() == nsPresContext::eContext_PageLayout);
8828
0
}
8829
8830
AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame *aFrame)
8831
0
{
8832
0
  // FIXME: Now that inflation calculations are based on the flow
8833
0
  // root's NCA's (nearest common ancestor of its inflatable
8834
0
  // descendants) width, we could probably disable inflation in
8835
0
  // fewer cases than we currently do.
8836
0
  // MathML cells need special treatment. See bug 1002526 comment 56.
8837
0
  if (aFrame->IsContainerForFontSizeInflation() &&
8838
0
      !aFrame->IsFrameOfType(nsIFrame::eMathML)) {
8839
0
    mPresContext = aFrame->PresContext();
8840
0
    mOldValue = mPresContext->mInflationDisabledForShrinkWrap;
8841
0
    mPresContext->mInflationDisabledForShrinkWrap = true;
8842
0
  } else {
8843
0
    // indicate we have nothing to restore
8844
0
    mPresContext = nullptr;
8845
0
    mOldValue = false;
8846
0
  }
8847
0
}
8848
8849
AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation()
8850
0
{
8851
0
  if (mPresContext) {
8852
0
    mPresContext->mInflationDisabledForShrinkWrap = mOldValue;
8853
0
  }
8854
0
}
8855
8856
namespace mozilla {
8857
8858
Rect NSRectToRect(const nsRect& aRect, double aAppUnitsPerPixel)
8859
0
{
8860
0
  // Note that by making aAppUnitsPerPixel a double we're doing floating-point
8861
0
  // division using a larger type and avoiding rounding error.
8862
0
  return Rect(Float(aRect.x / aAppUnitsPerPixel),
8863
0
              Float(aRect.y / aAppUnitsPerPixel),
8864
0
              Float(aRect.width / aAppUnitsPerPixel),
8865
0
              Float(aRect.height / aAppUnitsPerPixel));
8866
0
}
8867
8868
Rect NSRectToSnappedRect(const nsRect& aRect, double aAppUnitsPerPixel,
8869
                         const gfx::DrawTarget& aSnapDT)
8870
0
{
8871
0
  // Note that by making aAppUnitsPerPixel a double we're doing floating-point
8872
0
  // division using a larger type and avoiding rounding error.
8873
0
  Rect rect(Float(aRect.x / aAppUnitsPerPixel),
8874
0
            Float(aRect.y / aAppUnitsPerPixel),
8875
0
            Float(aRect.width / aAppUnitsPerPixel),
8876
0
            Float(aRect.height / aAppUnitsPerPixel));
8877
0
  MaybeSnapToDevicePixels(rect, aSnapDT, true);
8878
0
  return rect;
8879
0
}
8880
// Similar to a snapped rect, except an axis is left unsnapped if the snapping
8881
// process results in a length of 0.
8882
Rect NSRectToNonEmptySnappedRect(const nsRect& aRect, double aAppUnitsPerPixel,
8883
                                 const gfx::DrawTarget& aSnapDT)
8884
0
{
8885
0
  // Note that by making aAppUnitsPerPixel a double we're doing floating-point
8886
0
  // division using a larger type and avoiding rounding error.
8887
0
  Rect rect(Float(aRect.x / aAppUnitsPerPixel),
8888
0
            Float(aRect.y / aAppUnitsPerPixel),
8889
0
            Float(aRect.width / aAppUnitsPerPixel),
8890
0
            Float(aRect.height / aAppUnitsPerPixel));
8891
0
  MaybeSnapToDevicePixels(rect, aSnapDT, true, false);
8892
0
  return rect;
8893
0
}
8894
8895
void StrokeLineWithSnapping(const nsPoint& aP1, const nsPoint& aP2,
8896
                            int32_t aAppUnitsPerDevPixel,
8897
                            DrawTarget& aDrawTarget,
8898
                            const Pattern& aPattern,
8899
                            const StrokeOptions& aStrokeOptions,
8900
                            const DrawOptions& aDrawOptions)
8901
0
{
8902
0
  Point p1 = NSPointToPoint(aP1, aAppUnitsPerDevPixel);
8903
0
  Point p2 = NSPointToPoint(aP2, aAppUnitsPerDevPixel);
8904
0
  SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
8905
0
                                    aStrokeOptions.mLineWidth);
8906
0
  aDrawTarget.StrokeLine(p1, p2, aPattern, aStrokeOptions, aDrawOptions);
8907
0
}
8908
8909
namespace layout {
8910
8911
void
8912
MaybeSetupTransactionIdAllocator(layers::LayerManager* aManager,
8913
                                 nsPresContext* aPresContext)
8914
0
{
8915
0
  auto backendType = aManager->GetBackendType();
8916
0
  if (backendType == LayersBackend::LAYERS_CLIENT ||
8917
0
      backendType == LayersBackend::LAYERS_WR) {
8918
0
    aManager->SetTransactionIdAllocator(aPresContext->RefreshDriver());
8919
0
  }
8920
0
}
8921
8922
} // namespace layout
8923
} // namespace mozilla
8924
8925
/* static */ bool
8926
nsLayoutUtils::IsOutlineStyleAutoEnabled()
8927
0
{
8928
0
  static bool sOutlineStyleAutoEnabled;
8929
0
  static bool sOutlineStyleAutoPrefCached = false;
8930
0
8931
0
  if (!sOutlineStyleAutoPrefCached) {
8932
0
    sOutlineStyleAutoPrefCached = true;
8933
0
    Preferences::AddBoolVarCache(&sOutlineStyleAutoEnabled,
8934
0
                                 "layout.css.outline-style-auto.enabled",
8935
0
                                 false);
8936
0
  }
8937
0
  return sOutlineStyleAutoEnabled;
8938
0
}
8939
8940
/* static */ void
8941
nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame* aFrame,
8942
                                       ReflowOutput& aMetrics,
8943
                                       const LogicalMargin& aFramePadding,
8944
                                       WritingMode aLineWM,
8945
                                       WritingMode aFrameWM)
8946
0
{
8947
0
  RefPtr<nsFontMetrics> fm =
8948
0
    nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
8949
0
8950
0
  if (fm) {
8951
0
    // Compute final height of the frame.
8952
0
    //
8953
0
    // Do things the standard css2 way -- though it's hard to find it
8954
0
    // in the css2 spec! It's actually found in the css1 spec section
8955
0
    // 4.4 (you will have to read between the lines to really see
8956
0
    // it).
8957
0
    //
8958
0
    // The height of our box is the sum of our font size plus the top
8959
0
    // and bottom border and padding. The height of children do not
8960
0
    // affect our height.
8961
0
    aMetrics.SetBlockStartAscent(aLineWM.IsLineInverted() ? fm->MaxDescent()
8962
0
                                                          : fm->MaxAscent());
8963
0
    aMetrics.BSize(aLineWM) = fm->MaxHeight();
8964
0
  } else {
8965
0
    NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
8966
0
    aMetrics.SetBlockStartAscent(aMetrics.BSize(aLineWM) = 0);
8967
0
  }
8968
0
  aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
8969
0
                               aFramePadding.BStart(aFrameWM));
8970
0
  aMetrics.BSize(aLineWM) += aFramePadding.BStartEnd(aFrameWM);
8971
0
}
8972
8973
/* static */ bool
8974
nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(nsIPresShell* aShell)
8975
0
{
8976
0
  if (nsIDocument* doc = aShell->GetDocument()) {
8977
0
    WidgetEvent event(true, eVoidEvent);
8978
0
    nsTArray<EventTarget*> targets;
8979
0
    nsresult rv = EventDispatcher::Dispatch(doc, nullptr, &event, nullptr,
8980
0
        nullptr, nullptr, &targets);
8981
0
    NS_ENSURE_SUCCESS(rv, false);
8982
0
    for (size_t i = 0; i < targets.Length(); i++) {
8983
0
      if (targets[i]->IsApzAware()) {
8984
0
        return true;
8985
0
      }
8986
0
    }
8987
0
  }
8988
0
  return false;
8989
0
}
8990
8991
static void
8992
MaybeReflowForInflationScreenSizeChange(nsPresContext *aPresContext)
8993
0
{
8994
0
  if (aPresContext) {
8995
0
    nsIPresShell* presShell = aPresContext->GetPresShell();
8996
0
    const bool fontInflationWasEnabled = presShell->FontSizeInflationEnabled();
8997
0
    presShell->RecomputeFontSizeInflationEnabled();
8998
0
    bool changed = false;
8999
0
    if (presShell->FontSizeInflationEnabled() &&
9000
0
        presShell->FontSizeInflationMinTwips() != 0) {
9001
0
      aPresContext->ScreenSizeInchesForFontInflation(&changed);
9002
0
    }
9003
0
9004
0
    changed = changed ||
9005
0
      fontInflationWasEnabled != presShell->FontSizeInflationEnabled();
9006
0
    if (changed) {
9007
0
      nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
9008
0
      if (docShell) {
9009
0
        nsCOMPtr<nsIContentViewer> cv;
9010
0
        docShell->GetContentViewer(getter_AddRefs(cv));
9011
0
        if (cv) {
9012
0
          nsTArray<nsCOMPtr<nsIContentViewer> > array;
9013
0
          cv->AppendSubtree(array);
9014
0
          for (uint32_t i = 0, iEnd = array.Length(); i < iEnd; ++i) {
9015
0
            nsCOMPtr<nsIPresShell> shell;
9016
0
            nsCOMPtr<nsIContentViewer> cv = array[i];
9017
0
            cv->GetPresShell(getter_AddRefs(shell));
9018
0
            if (shell) {
9019
0
              nsIFrame *rootFrame = shell->GetRootFrame();
9020
0
              if (rootFrame) {
9021
0
                shell->FrameNeedsReflow(rootFrame,
9022
0
                                        nsIPresShell::eStyleChange,
9023
0
                                        NS_FRAME_IS_DIRTY);
9024
0
              }
9025
0
            }
9026
0
          }
9027
0
        }
9028
0
      }
9029
0
    }
9030
0
  }
9031
0
}
9032
9033
/* static */ void
9034
nsLayoutUtils::SetVisualViewportSize(nsIPresShell* aPresShell, CSSSize aSize)
9035
0
{
9036
0
  MOZ_ASSERT(aSize.width >= 0.0 && aSize.height >= 0.0);
9037
0
9038
0
  aPresShell->SetVisualViewportSize(
9039
0
    nsPresContext::CSSPixelsToAppUnits(aSize.width),
9040
0
    nsPresContext::CSSPixelsToAppUnits(aSize.height));
9041
0
9042
0
  // When the "font.size.inflation.minTwips" preference is set, the
9043
0
  // layout depends on the size of the screen.  Since when the size
9044
0
  // of the screen changes, the scroll position clamping scroll port
9045
0
  // size also changes, we hook in the needed updates here rather
9046
0
  // than adding a separate notification just for this change.
9047
0
  nsPresContext* presContext = aPresShell->GetPresContext();
9048
0
  MaybeReflowForInflationScreenSizeChange(presContext);
9049
0
}
9050
9051
/* static */ bool
9052
nsLayoutUtils::CanScrollOriginClobberApz(nsAtom* aScrollOrigin)
9053
0
{
9054
0
  return aScrollOrigin != nullptr
9055
0
      && aScrollOrigin != nsGkAtoms::apz
9056
0
      && aScrollOrigin != nsGkAtoms::restore;
9057
0
}
9058
9059
/* static */ ScrollMetadata
9060
nsLayoutUtils::ComputeScrollMetadata(nsIFrame* aForFrame,
9061
                                     nsIFrame* aScrollFrame,
9062
                                     nsIContent* aContent,
9063
                                     const nsIFrame* aReferenceFrame,
9064
                                     LayerManager* aLayerManager,
9065
                                     ViewID aScrollParentId,
9066
                                     const nsRect& aViewport,
9067
                                     const Maybe<nsRect>& aClipRect,
9068
                                     bool aIsRootContent,
9069
                                     const ContainerLayerParameters& aContainerParameters)
9070
0
{
9071
0
  nsPresContext* presContext = aForFrame->PresContext();
9072
0
  int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
9073
0
9074
0
  nsIPresShell* presShell = presContext->GetPresShell();
9075
0
  ScrollMetadata metadata;
9076
0
  FrameMetrics& metrics = metadata.GetMetrics();
9077
0
  metrics.SetViewport(CSSRect::FromAppUnits(aViewport));
9078
0
9079
0
  ViewID scrollId = FrameMetrics::NULL_SCROLL_ID;
9080
0
  if (aContent) {
9081
0
    if (void* paintRequestTime = aContent->GetProperty(nsGkAtoms::paintRequestTime)) {
9082
0
      metrics.SetPaintRequestTime(*static_cast<TimeStamp*>(paintRequestTime));
9083
0
      aContent->DeleteProperty(nsGkAtoms::paintRequestTime);
9084
0
    }
9085
0
    scrollId = nsLayoutUtils::FindOrCreateIDFor(aContent);
9086
0
    nsRect dp;
9087
0
    if (nsLayoutUtils::GetDisplayPort(aContent, &dp)) {
9088
0
      metrics.SetDisplayPort(CSSRect::FromAppUnits(dp));
9089
0
      if (IsAPZTestLoggingEnabled()) {
9090
0
        LogTestDataForPaint(aLayerManager, scrollId, "displayport",
9091
0
                            metrics.GetDisplayPort());
9092
0
      }
9093
0
    }
9094
0
    if (nsLayoutUtils::GetCriticalDisplayPort(aContent, &dp)) {
9095
0
      metrics.SetCriticalDisplayPort(CSSRect::FromAppUnits(dp));
9096
0
      if (IsAPZTestLoggingEnabled()) {
9097
0
        LogTestDataForPaint(aLayerManager, scrollId, "criticalDisplayport",
9098
0
                            metrics.GetCriticalDisplayPort());
9099
0
      }
9100
0
    }
9101
0
    DisplayPortMarginsPropertyData* marginsData =
9102
0
        static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
9103
0
    if (marginsData) {
9104
0
      metrics.SetDisplayPortMargins(marginsData->mMargins);
9105
0
    }
9106
0
  }
9107
0
9108
0
  nsIScrollableFrame* scrollableFrame = nullptr;
9109
0
  if (aScrollFrame)
9110
0
    scrollableFrame = aScrollFrame->GetScrollTargetFrame();
9111
0
9112
0
  metrics.SetScrollableRect(CSSRect::FromAppUnits(
9113
0
    nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame)));
9114
0
9115
0
  if (scrollableFrame) {
9116
0
    CSSPoint scrollPosition = CSSPoint::FromAppUnits(scrollableFrame->GetScrollPosition());
9117
0
    metrics.SetScrollOffset(scrollPosition);
9118
0
9119
0
    CSSRect viewport = metrics.GetViewport();
9120
0
    viewport.MoveTo(scrollPosition);
9121
0
    metrics.SetViewport(viewport);
9122
0
9123
0
    nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
9124
0
    metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
9125
0
9126
0
    // If the frame was scrolled since the last layers update, and by something
9127
0
    // that is higher priority than APZ, we want to tell the APZ to update
9128
0
    // its scroll offset. We want to distinguish the case where the scroll offset
9129
0
    // was "restored" because in that case the restored scroll position should
9130
0
    // not overwrite a user-driven scroll.
9131
0
    if (scrollableFrame->LastScrollOrigin() == nsGkAtoms::restore) {
9132
0
      metrics.SetScrollOffsetRestored(scrollableFrame->CurrentScrollGeneration());
9133
0
    } else if (CanScrollOriginClobberApz(scrollableFrame->LastScrollOrigin())) {
9134
0
      metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
9135
0
    }
9136
0
    scrollableFrame->AllowScrollOriginDowngrade();
9137
0
9138
0
    nsAtom* lastSmoothScrollOrigin = scrollableFrame->LastSmoothScrollOrigin();
9139
0
    if (lastSmoothScrollOrigin) {
9140
0
      metrics.SetSmoothScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
9141
0
    }
9142
0
9143
0
    nsSize lineScrollAmount = scrollableFrame->GetLineScrollAmount();
9144
0
    LayoutDeviceIntSize lineScrollAmountInDevPixels =
9145
0
      LayoutDeviceIntSize::FromAppUnitsRounded(lineScrollAmount, presContext->AppUnitsPerDevPixel());
9146
0
    metadata.SetLineScrollAmount(lineScrollAmountInDevPixels);
9147
0
9148
0
    nsSize pageScrollAmount = scrollableFrame->GetPageScrollAmount();
9149
0
    LayoutDeviceIntSize pageScrollAmountInDevPixels =
9150
0
      LayoutDeviceIntSize::FromAppUnitsRounded(pageScrollAmount, presContext->AppUnitsPerDevPixel());
9151
0
    metadata.SetPageScrollAmount(pageScrollAmountInDevPixels);
9152
0
9153
0
    if (aScrollFrame->GetParent()) {
9154
0
      metadata.SetDisregardedDirection(
9155
0
        WheelHandlingUtils::GetDisregardedWheelScrollDirection(
9156
0
          aScrollFrame->GetParent()));
9157
0
    }
9158
0
9159
0
    metadata.SetUsesContainerScrolling(scrollableFrame->UsesContainerScrolling());
9160
0
9161
0
    metadata.SetSnapInfo(scrollableFrame->GetScrollSnapInfo());
9162
0
9163
0
    ScrollStyles scrollStyles = scrollableFrame->GetScrollStyles();
9164
0
    metadata.SetOverscrollBehavior(OverscrollBehaviorInfo::FromStyleConstants(
9165
0
        scrollStyles.mOverscrollBehaviorX,
9166
0
        scrollStyles.mOverscrollBehaviorY));
9167
0
  }
9168
0
9169
0
  // If we have the scrollparent being the same as the scroll id, the
9170
0
  // compositor-side code could get into an infinite loop while building the
9171
0
  // overscroll handoff chain.
9172
0
  MOZ_ASSERT(aScrollParentId == FrameMetrics::NULL_SCROLL_ID || scrollId != aScrollParentId);
9173
0
  metrics.SetScrollId(scrollId);
9174
0
  metrics.SetIsRootContent(aIsRootContent);
9175
0
  metadata.SetScrollParentId(aScrollParentId);
9176
0
9177
0
  nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
9178
0
  bool isRootScrollFrame = aScrollFrame == rootScrollFrame;
9179
0
  nsIDocument* document = presShell->GetDocument();
9180
0
9181
0
  if (scrollId != FrameMetrics::NULL_SCROLL_ID && !presContext->GetParentPresContext()) {
9182
0
    if ((aScrollFrame && isRootScrollFrame)) {
9183
0
      metadata.SetIsLayersIdRoot(true);
9184
0
    } else {
9185
0
      MOZ_ASSERT(document, "A non-root-scroll frame must be in a document");
9186
0
      if (aContent == document->GetDocumentElement()) {
9187
0
        metadata.SetIsLayersIdRoot(true);
9188
0
      }
9189
0
    }
9190
0
  }
9191
0
9192
0
  // Get whether the root content is RTL(E.g. it's true either if
9193
0
  // "writing-mode: vertical-rl", or if
9194
0
  // "writing-mode: horizontal-tb; direction: rtl;" in CSS).
9195
0
  // For the concept of this and the reason why we need to get this kind of
9196
0
  // information, see the definition of |mIsAutoDirRootContentRTL| in struct
9197
0
  // |ScrollMetadata|.
9198
0
  Element* bodyElement = document ? document->GetBodyElement() : nullptr;
9199
0
  nsIFrame* primaryFrame = bodyElement ? bodyElement->GetPrimaryFrame() :
9200
0
                                           rootScrollFrame;
9201
0
  if (!primaryFrame) {
9202
0
    primaryFrame = rootScrollFrame;
9203
0
  }
9204
0
  if (primaryFrame) {
9205
0
    WritingMode writingModeOfRootScrollFrame =
9206
0
                  primaryFrame->GetWritingMode();
9207
0
    WritingMode::BlockDir blockDirOfRootScrollFrame =
9208
0
                            writingModeOfRootScrollFrame.GetBlockDir();
9209
0
    WritingMode::InlineDir inlineDirOfRootScrollFrame =
9210
0
                             writingModeOfRootScrollFrame.GetInlineDir();
9211
0
    if (blockDirOfRootScrollFrame == WritingMode::BlockDir::eBlockRL ||
9212
0
        (blockDirOfRootScrollFrame == WritingMode::BlockDir::eBlockTB &&
9213
0
          inlineDirOfRootScrollFrame == WritingMode::InlineDir::eInlineRTL)) {
9214
0
      metadata.SetIsAutoDirRootContentRTL(true);
9215
0
    }
9216
0
  }
9217
0
9218
0
  // Only the root scrollable frame for a given presShell should pick up
9219
0
  // the presShell's resolution. All the other frames are 1.0.
9220
0
  if (isRootScrollFrame) {
9221
0
    metrics.SetPresShellResolution(presShell->GetResolution());
9222
0
  } else {
9223
0
    metrics.SetPresShellResolution(1.0f);
9224
0
  }
9225
0
  // The cumulative resolution is the resolution at which the scroll frame's
9226
0
  // content is actually rendered. It includes the pres shell resolutions of
9227
0
  // all the pres shells from here up to the root, as well as any css-driven
9228
0
  // resolution. We don't need to compute it as it's already stored in the
9229
0
  // container parameters.
9230
0
  metrics.SetCumulativeResolution(aContainerParameters.Scale());
9231
0
9232
0
  LayoutDeviceToScreenScale2D resolutionToScreen(
9233
0
      presShell->GetCumulativeResolution()
9234
0
    * nsLayoutUtils::GetTransformToAncestorScale(aScrollFrame ? aScrollFrame : aForFrame));
9235
0
  metrics.SetExtraResolution(metrics.GetCumulativeResolution() / resolutionToScreen);
9236
0
9237
0
  metrics.SetDevPixelsPerCSSPixel(presContext->CSSToDevPixelScale());
9238
0
9239
0
  // Initially, AsyncPanZoomController should render the content to the screen
9240
0
  // at the painted resolution.
9241
0
  const LayerToParentLayerScale layerToParentLayerScale(1.0f);
9242
0
  metrics.SetZoom(metrics.GetCumulativeResolution() * metrics.GetDevPixelsPerCSSPixel()
9243
0
                  * layerToParentLayerScale);
9244
0
9245
0
  // Calculate the composition bounds as the size of the scroll frame and
9246
0
  // its origin relative to the reference frame.
9247
0
  // If aScrollFrame is null, we are in a document without a root scroll frame,
9248
0
  // so it's a xul document. In this case, use the size of the viewport frame.
9249
0
  nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame;
9250
0
  nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),
9251
0
                           frameForCompositionBoundsCalculation->GetSize());
9252
0
  if (scrollableFrame) {
9253
0
    // If we have a scrollable frame, restrict the composition bounds to its
9254
0
    // scroll port. The scroll port excludes the frame borders and the scroll
9255
0
    // bars, which we don't want to be part of the composition bounds.
9256
0
    nsRect scrollPort = scrollableFrame->GetScrollPortRect();
9257
0
    compositionBounds = nsRect(compositionBounds.TopLeft() + scrollPort.TopLeft(),
9258
0
                               scrollPort.Size());
9259
0
  }
9260
0
  ParentLayerRect frameBounds = LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel)
9261
0
                              * metrics.GetCumulativeResolution()
9262
0
                              * layerToParentLayerScale;
9263
0
9264
0
  if (aClipRect) {
9265
0
    ParentLayerRect rect = LayoutDeviceRect::FromAppUnits(*aClipRect, auPerDevPixel)
9266
0
                         * metrics.GetCumulativeResolution()
9267
0
                         * layerToParentLayerScale;
9268
0
    metadata.SetScrollClip(Some(LayerClip(RoundedToInt(rect))));
9269
0
  }
9270
0
9271
0
  // For the root scroll frame of the root content document (RCD-RSF), the above calculation
9272
0
  // will yield the size of the viewport frame as the composition bounds, which
9273
0
  // doesn't actually correspond to what is visible when
9274
0
  // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of
9275
0
  // the prescontext that the viewport frame is reflowed into. In that case if our
9276
0
  // document has a widget then the widget's bounds will correspond to what is
9277
0
  // visible. If we don't have a widget the root view's bounds correspond to what
9278
0
  // would be visible because they don't get modified by setCSSViewport.
9279
0
  bool isRootContentDocRootScrollFrame = isRootScrollFrame
9280
0
                                      && presContext->IsRootContentDocument();
9281
0
  if (isRootContentDocRootScrollFrame) {
9282
0
    UpdateCompositionBoundsForRCDRSF(frameBounds, presContext, true);
9283
0
  }
9284
0
9285
0
  nsMargin sizes = ScrollbarAreaToExcludeFromCompositionBoundsFor(aScrollFrame);
9286
0
  // Scrollbars are not subject to resolution scaling, so LD pixels = layer pixels for them.
9287
0
  ParentLayerMargin boundMargins = LayoutDeviceMargin::FromAppUnits(sizes, auPerDevPixel)
9288
0
    * LayoutDeviceToParentLayerScale(1.0f);
9289
0
  frameBounds.Deflate(boundMargins);
9290
0
9291
0
  metrics.SetCompositionBounds(frameBounds);
9292
0
9293
0
  metrics.SetRootCompositionSize(
9294
0
    nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame,
9295
0
                                                isRootContentDocRootScrollFrame, metrics));
9296
0
9297
0
  if (gfxPrefs::APZPrintTree() || gfxPrefs::APZTestLoggingEnabled()) {
9298
0
    if (nsIContent* content = frameForCompositionBoundsCalculation->GetContent()) {
9299
0
      nsAutoString contentDescription;
9300
0
      if (content->IsElement()) {
9301
0
        content->AsElement()->Describe(contentDescription);
9302
0
      } else {
9303
0
        contentDescription.AssignLiteral("(not an element)");
9304
0
      }
9305
0
      metadata.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription));
9306
0
      if (IsAPZTestLoggingEnabled()) {
9307
0
        LogTestDataForPaint(aLayerManager, scrollId, "contentDescription",
9308
0
                            metadata.GetContentDescription().get());
9309
0
      }
9310
0
    }
9311
0
  }
9312
0
9313
0
  metrics.SetPresShellId(presShell->GetPresShellId());
9314
0
9315
0
  // If the scroll frame's content is marked 'scrollgrab', record this
9316
0
  // in the FrameMetrics so APZ knows to provide the scroll grabbing
9317
0
  // behaviour.
9318
0
  if (aScrollFrame && nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())) {
9319
0
    metadata.SetHasScrollgrab(true);
9320
0
  }
9321
0
9322
0
  // Also compute and set the background color.
9323
0
  // This is needed for APZ overscrolling support.
9324
0
  if (aScrollFrame) {
9325
0
    if (isRootScrollFrame) {
9326
0
      metadata.SetBackgroundColor(Color::FromABGR(
9327
0
        presShell->GetCanvasBackground()));
9328
0
    } else {
9329
0
      ComputedStyle* backgroundStyle;
9330
0
      if (nsCSSRendering::FindBackground(aScrollFrame, &backgroundStyle)) {
9331
0
        nscolor backgroundColor = backgroundStyle->
9332
0
          StyleBackground()->BackgroundColor(backgroundStyle);
9333
0
        metadata.SetBackgroundColor(Color::FromABGR(backgroundColor));
9334
0
      }
9335
0
    }
9336
0
  }
9337
0
9338
0
  if (ShouldDisableApzForElement(aContent)) {
9339
0
    metadata.SetForceDisableApz(true);
9340
0
  }
9341
0
9342
0
  return metadata;
9343
0
}
9344
9345
/*static*/ Maybe<ScrollMetadata>
9346
nsLayoutUtils::GetRootMetadata(nsDisplayListBuilder* aBuilder,
9347
                               LayerManager* aLayerManager,
9348
                               const ContainerLayerParameters& aContainerParameters,
9349
                               const std::function<bool(ViewID& aScrollId)>& aCallback)
9350
0
{
9351
0
  nsIFrame* frame = aBuilder->RootReferenceFrame();
9352
0
  nsPresContext* presContext = frame->PresContext();
9353
0
  nsIPresShell* presShell = presContext->PresShell();
9354
0
  nsIDocument* document = presShell->GetDocument();
9355
0
9356
0
  // If we're using containerless scrolling, there is still one case where we
9357
0
  // want the root container layer to have metrics. If the parent process is
9358
0
  // using XUL windows, there is no root scrollframe, and without explicitly
9359
0
  // creating metrics there will be no guaranteed top-level APZC.
9360
0
  bool addMetrics = gfxPrefs::LayoutUseContainersForRootFrames() ||
9361
0
      (XRE_IsParentProcess() && !presShell->GetRootScrollFrame());
9362
0
9363
0
  // Add metrics if there are none in the layer tree with the id (create an id
9364
0
  // if there isn't one already) of the root scroll frame/root content.
9365
0
  bool ensureMetricsForRootId =
9366
0
    nsLayoutUtils::AsyncPanZoomEnabled(frame) &&
9367
0
    !gfxPrefs::LayoutUseContainersForRootFrames() &&
9368
0
    aBuilder->IsPaintingToWindow() &&
9369
0
    !presContext->GetParentPresContext();
9370
0
9371
0
  nsIContent* content = nullptr;
9372
0
  nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
9373
0
  if (rootScrollFrame) {
9374
0
    content = rootScrollFrame->GetContent();
9375
0
  } else {
9376
0
    // If there is no root scroll frame, pick the document element instead.
9377
0
    // The only case we don't want to do this is in non-APZ fennec, where
9378
0
    // we want the root xul document to get a null scroll id so that the root
9379
0
    // content document gets the first non-null scroll id.
9380
0
    content = document->GetDocumentElement();
9381
0
  }
9382
0
9383
0
  if (ensureMetricsForRootId && content) {
9384
0
    ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
9385
0
    if (aCallback(scrollId)) {
9386
0
      ensureMetricsForRootId = false;
9387
0
    }
9388
0
  }
9389
0
9390
0
  if (addMetrics || ensureMetricsForRootId) {
9391
0
    bool isRootContent = presContext->IsRootContentDocument();
9392
0
9393
0
    nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize());
9394
0
    return Some(nsLayoutUtils::ComputeScrollMetadata(frame,
9395
0
                           rootScrollFrame, content,
9396
0
                           aBuilder->FindReferenceFrameFor(frame),
9397
0
                           aLayerManager, FrameMetrics::NULL_SCROLL_ID, viewport, Nothing(),
9398
0
                           isRootContent, aContainerParameters));
9399
0
  }
9400
0
9401
0
  return Nothing();
9402
0
}
9403
9404
/* static */ bool
9405
nsLayoutUtils::ContainsMetricsWithId(const Layer* aLayer, const ViewID& aScrollId)
9406
0
{
9407
0
  for (uint32_t i = aLayer->GetScrollMetadataCount(); i > 0; i--) {
9408
0
    if (aLayer->GetFrameMetrics(i-1).GetScrollId() == aScrollId) {
9409
0
      return true;
9410
0
    }
9411
0
  }
9412
0
  for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
9413
0
    if (ContainsMetricsWithId(child, aScrollId)) {
9414
0
      return true;
9415
0
    }
9416
0
  }
9417
0
  return false;
9418
0
}
9419
9420
/* static */ uint32_t
9421
nsLayoutUtils::GetTouchActionFromFrame(nsIFrame* aFrame)
9422
0
{
9423
0
  // If aFrame is null then return default value
9424
0
  if (!aFrame) {
9425
0
    return NS_STYLE_TOUCH_ACTION_AUTO;
9426
0
  }
9427
0
9428
0
  // The touch-action CSS property applies to: all elements except:
9429
0
  // non-replaced inline elements, table rows, row groups, table columns, and column groups
9430
0
  bool isNonReplacedInlineElement = aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
9431
0
  if (isNonReplacedInlineElement) {
9432
0
    return NS_STYLE_TOUCH_ACTION_AUTO;
9433
0
  }
9434
0
9435
0
  const nsStyleDisplay* disp = aFrame->StyleDisplay();
9436
0
  bool isTableElement = disp->IsInternalTableStyleExceptCell();
9437
0
  if (isTableElement) {
9438
0
    return NS_STYLE_TOUCH_ACTION_AUTO;
9439
0
  }
9440
0
9441
0
  return disp->mTouchAction;
9442
0
}
9443
9444
/* static */  void
9445
nsLayoutUtils::TransformToAncestorAndCombineRegions(
9446
  const nsRegion& aRegion,
9447
  nsIFrame* aFrame,
9448
  const nsIFrame* aAncestorFrame,
9449
  nsRegion* aPreciseTargetDest,
9450
  nsRegion* aImpreciseTargetDest,
9451
  Maybe<Matrix4x4Flagged>* aMatrixCache,
9452
  const DisplayItemClip* aClip)
9453
0
{
9454
0
  if (aRegion.IsEmpty()) {
9455
0
    return;
9456
0
  }
9457
0
  bool isPrecise;
9458
0
  RegionBuilder<nsRegion> transformedRegion;
9459
0
  for (nsRegion::RectIterator it = aRegion.RectIter(); !it.Done(); it.Next()) {
9460
0
    nsRect transformed = TransformFrameRectToAncestor(
9461
0
      aFrame, it.Get(), aAncestorFrame, &isPrecise, aMatrixCache);
9462
0
    if (aClip) {
9463
0
      transformed = aClip->ApplyNonRoundedIntersection(transformed);
9464
0
      if (aClip->GetRoundedRectCount() > 0) {
9465
0
        isPrecise = false;
9466
0
      }
9467
0
    }
9468
0
    transformedRegion.OrWith(transformed);
9469
0
  }
9470
0
  nsRegion* dest = isPrecise ? aPreciseTargetDest : aImpreciseTargetDest;
9471
0
  dest->OrWith(transformedRegion.ToRegion());
9472
0
  // If the region becomes too complex this has a large performance impact.
9473
0
  // We limit its complexity here.
9474
0
  if (dest->GetNumRects() > 12) {
9475
0
    dest->SimplifyOutward(6);
9476
0
    if (isPrecise) {
9477
0
      aPreciseTargetDest->OrWith(*aImpreciseTargetDest);
9478
0
      *aImpreciseTargetDest = std::move(*aPreciseTargetDest);
9479
0
      aImpreciseTargetDest->SimplifyOutward(6);
9480
0
      *aPreciseTargetDest = nsRegion();
9481
0
    }
9482
0
  }
9483
0
}
9484
9485
/* static */ bool
9486
nsLayoutUtils::ShouldUseNoScriptSheet(nsIDocument* aDocument)
9487
0
{
9488
0
  // also handle the case where print is done from print preview
9489
0
  // see bug #342439 for more details
9490
0
  if (aDocument->IsStaticDocument()) {
9491
0
    aDocument = aDocument->GetOriginalDocument();
9492
0
  }
9493
0
  return aDocument->IsScriptEnabled();
9494
0
}
9495
9496
/* static */ bool
9497
nsLayoutUtils::ShouldUseNoFramesSheet(nsIDocument* aDocument)
9498
0
{
9499
0
  bool allowSubframes = true;
9500
0
  nsIDocShell* docShell = aDocument->GetDocShell();
9501
0
  if (docShell) {
9502
0
    docShell->GetAllowSubframes(&allowSubframes);
9503
0
  }
9504
0
  return !allowSubframes;
9505
0
}
9506
9507
/* static */ void
9508
nsLayoutUtils::GetFrameTextContent(nsIFrame* aFrame, nsAString& aResult)
9509
0
{
9510
0
  aResult.Truncate();
9511
0
  AppendFrameTextContent(aFrame, aResult);
9512
0
}
9513
9514
/* static */ void
9515
nsLayoutUtils::AppendFrameTextContent(nsIFrame* aFrame, nsAString& aResult)
9516
0
{
9517
0
  if (aFrame->IsTextFrame()) {
9518
0
    auto textFrame = static_cast<nsTextFrame*>(aFrame);
9519
0
    auto offset = textFrame->GetContentOffset();
9520
0
    auto length = textFrame->GetContentLength();
9521
0
    textFrame->GetContent()->
9522
0
      GetText()->AppendTo(aResult, offset, length);
9523
0
  } else {
9524
0
    for (nsIFrame* child : aFrame->PrincipalChildList()) {
9525
0
      AppendFrameTextContent(child, aResult);
9526
0
    }
9527
0
  }
9528
0
}
9529
9530
/* static */
9531
nsRect
9532
nsLayoutUtils::GetSelectionBoundingRect(Selection* aSel)
9533
0
{
9534
0
  nsRect res;
9535
0
  // Bounding client rect may be empty after calling GetBoundingClientRect
9536
0
  // when range is collapsed. So we get caret's rect when range is
9537
0
  // collapsed.
9538
0
  if (aSel->IsCollapsed()) {
9539
0
    nsIFrame* frame = nsCaret::GetGeometry(aSel, &res);
9540
0
    if (frame) {
9541
0
      nsIFrame* relativeTo = GetContainingBlockForClientRect(frame);
9542
0
      res = TransformFrameRectToAncestor(frame, res, relativeTo);
9543
0
    }
9544
0
  } else {
9545
0
    int32_t rangeCount = aSel->RangeCount();
9546
0
    RectAccumulator accumulator;
9547
0
    for (int32_t idx = 0; idx < rangeCount; ++idx) {
9548
0
      nsRange* range = aSel->GetRangeAt(idx);
9549
0
      nsRange::CollectClientRectsAndText(&accumulator, nullptr, range,
9550
0
                                         range->GetStartContainer(),
9551
0
                                         range->StartOffset(),
9552
0
                                         range->GetEndContainer(),
9553
0
                                         range->EndOffset(),
9554
0
                                         true, false);
9555
0
    }
9556
0
    res = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
9557
0
      accumulator.mResultRect;
9558
0
  }
9559
0
9560
0
  return res;
9561
0
}
9562
9563
/* static */ nsBlockFrame*
9564
nsLayoutUtils::GetFloatContainingBlock(nsIFrame* aFrame)
9565
0
{
9566
0
  nsIFrame* ancestor = aFrame->GetParent();
9567
0
  while (ancestor && !ancestor->IsFloatContainingBlock()) {
9568
0
    ancestor = ancestor->GetParent();
9569
0
  }
9570
0
  MOZ_ASSERT(!ancestor || GetAsBlock(ancestor),
9571
0
             "Float containing block can only be block frame");
9572
0
  return static_cast<nsBlockFrame*>(ancestor);
9573
0
}
9574
9575
// The implementation of this calculation is adapted from
9576
// Element::GetBoundingClientRect().
9577
/* static */ CSSRect
9578
nsLayoutUtils::GetBoundingContentRect(const nsIContent* aContent,
9579
0
                                      const nsIScrollableFrame* aRootScrollFrame) {
9580
0
  CSSRect result;
9581
0
  if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
9582
0
    nsIFrame* relativeTo = aRootScrollFrame->GetScrolledFrame();
9583
0
    result = CSSRect::FromAppUnits(
9584
0
        nsLayoutUtils::GetAllInFlowRectsUnion(
9585
0
            frame,
9586
0
            relativeTo,
9587
0
            nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS));
9588
0
9589
0
    // If the element is contained in a scrollable frame that is not
9590
0
    // the root scroll frame, make sure to clip the result so that it is
9591
0
    // not larger than the containing scrollable frame's bounds.
9592
0
    nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(frame);
9593
0
    if (scrollFrame && scrollFrame != aRootScrollFrame) {
9594
0
      nsIFrame* subFrame = do_QueryFrame(scrollFrame);
9595
0
      MOZ_ASSERT(subFrame);
9596
0
      // Get the bounds of the scroll frame in the same coordinate space
9597
0
      // as |result|.
9598
0
      CSSRect subFrameRect = CSSRect::FromAppUnits(
9599
0
          nsLayoutUtils::TransformFrameRectToAncestor(
9600
0
              subFrame,
9601
0
              subFrame->GetRectRelativeToSelf(),
9602
0
              relativeTo));
9603
0
9604
0
      result = subFrameRect.Intersect(result);
9605
0
    }
9606
0
  }
9607
0
  return result;
9608
0
}
9609
9610
static already_AddRefed<nsIPresShell>
9611
GetPresShell(const nsIContent* aContent)
9612
0
{
9613
0
  nsCOMPtr<nsIPresShell> result;
9614
0
  if (nsIDocument* doc = aContent->GetComposedDoc()) {
9615
0
    result = doc->GetShell();
9616
0
  }
9617
0
  return result.forget();
9618
0
}
9619
9620
0
static void UpdateDisplayPortMarginsForPendingMetrics(FrameMetrics& aMetrics) {
9621
0
  nsIContent* content = nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());
9622
0
  if (!content) {
9623
0
    return;
9624
0
  }
9625
0
9626
0
  nsCOMPtr<nsIPresShell> shell = GetPresShell(content);
9627
0
  if (!shell) {
9628
0
    return;
9629
0
  }
9630
0
9631
0
  MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
9632
0
9633
0
  if (gfxPrefs::APZAllowZooming() && aMetrics.IsRootContent()) {
9634
0
    // See APZCCallbackHelper::UpdateRootFrame for details.
9635
0
    float presShellResolution = shell->GetResolution();
9636
0
    if (presShellResolution != aMetrics.GetPresShellResolution()) {
9637
0
      return;
9638
0
    }
9639
0
  }
9640
0
9641
0
  nsIScrollableFrame* frame = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
9642
0
9643
0
  if (!frame) {
9644
0
    return;
9645
0
  }
9646
0
9647
0
  if (APZCCallbackHelper::IsScrollInProgress(frame)) {
9648
0
    // If these conditions are true, then the UpdateFrame
9649
0
    // message may be ignored by the main-thread, so we
9650
0
    // shouldn't update the displayport based on it.
9651
0
    return;
9652
0
  }
9653
0
9654
0
  DisplayPortMarginsPropertyData* currentData =
9655
0
    static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
9656
0
  if (!currentData) {
9657
0
    return;
9658
0
  }
9659
0
9660
0
  CSSPoint frameScrollOffset = CSSPoint::FromAppUnits(frame->GetScrollPosition());
9661
0
  APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, frameScrollOffset);
9662
0
9663
0
  nsLayoutUtils::SetDisplayPortMargins(content, shell,
9664
0
                                       aMetrics.GetDisplayPortMargins(), 0);
9665
0
}
9666
9667
/* static */ void
9668
nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages()
9669
0
{
9670
0
  if (XRE_IsContentProcess() &&
9671
0
      mozilla::layers::CompositorBridgeChild::Get() &&
9672
0
      mozilla::layers::CompositorBridgeChild::Get()->GetIPCChannel()) {
9673
0
    CompositorBridgeChild::Get()->GetIPCChannel()->PeekMessages(
9674
0
      [](const IPC::Message& aMsg) -> bool {
9675
0
        if (aMsg.type() == mozilla::layers::PAPZ::Msg_RequestContentRepaint__ID) {
9676
0
          PickleIterator iter(aMsg);
9677
0
          FrameMetrics frame;
9678
0
          if (!IPC::ReadParam(&aMsg, &iter, &frame)) {
9679
0
            MOZ_ASSERT(false);
9680
0
            return true;
9681
0
          }
9682
0
9683
0
          UpdateDisplayPortMarginsForPendingMetrics(frame);
9684
0
        }
9685
0
        return true;
9686
0
      });
9687
0
  }
9688
0
}
9689
9690
/* static */ bool
9691
nsLayoutUtils::IsTransformed(nsIFrame* aForFrame, nsIFrame* aTopFrame)
9692
0
{
9693
0
  for (nsIFrame* f = aForFrame; f != aTopFrame; f = f->GetParent()) {
9694
0
    if (f->IsTransformed()) {
9695
0
      return true;
9696
0
    }
9697
0
  }
9698
0
  return false;
9699
0
}
9700
9701
/*static*/ CSSPoint
9702
nsLayoutUtils::GetCumulativeApzCallbackTransform(nsIFrame* aFrame)
9703
0
{
9704
0
  CSSPoint delta;
9705
0
  if (!aFrame) {
9706
0
    return delta;
9707
0
  }
9708
0
  nsIFrame* frame = aFrame;
9709
0
  nsCOMPtr<nsIContent> content = frame->GetContent();
9710
0
  nsCOMPtr<nsIContent> lastContent;
9711
0
  while (frame) {
9712
0
    if (content && (content != lastContent)) {
9713
0
      void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
9714
0
      if (property) {
9715
0
        delta += *static_cast<CSSPoint*>(property);
9716
0
      }
9717
0
    }
9718
0
    frame = GetCrossDocParentFrame(frame);
9719
0
    lastContent = content;
9720
0
    content = frame ? frame->GetContent() : nullptr;
9721
0
  }
9722
0
  return delta;
9723
0
}
9724
9725
/* static */ nsRect
9726
nsLayoutUtils::ComputePartialPrerenderArea(const nsRect& aDirtyRect,
9727
                                           const nsRect& aOverflow,
9728
                                           const nsSize& aPrerenderSize)
9729
0
{
9730
0
  // Simple calculation for now: center the pre-render area on the dirty rect,
9731
0
  // and clamp to the overflow area. Later we can do more advanced things like
9732
0
  // redistributing from one axis to another, or from one side to another.
9733
0
  nscoord xExcess = std::max(aPrerenderSize.width - aDirtyRect.width, 0);
9734
0
  nscoord yExcess = std::max(aPrerenderSize.height - aDirtyRect.height, 0);
9735
0
  nsRect result = aDirtyRect;
9736
0
  result.Inflate(xExcess / 2, yExcess / 2);
9737
0
  return result.MoveInsideAndClamp(aOverflow);
9738
0
}
9739
9740
static
9741
bool
9742
LineHasNonEmptyContentWorker(nsIFrame* aFrame)
9743
0
{
9744
0
  // Look for non-empty frames, but ignore inline and br frames.
9745
0
  // For inline frames, descend into the children, if any.
9746
0
  if (aFrame->IsInlineFrame()) {
9747
0
    for (nsIFrame* child : aFrame->PrincipalChildList()) {
9748
0
      if (LineHasNonEmptyContentWorker(child)) {
9749
0
        return true;
9750
0
      }
9751
0
    }
9752
0
  } else {
9753
0
    if (!aFrame->IsBrFrame() && !aFrame->IsEmpty()) {
9754
0
      return true;
9755
0
    }
9756
0
  }
9757
0
  return false;
9758
0
}
9759
9760
static
9761
bool
9762
LineHasNonEmptyContent(nsLineBox* aLine)
9763
0
{
9764
0
  int32_t count = aLine->GetChildCount();
9765
0
  for (nsIFrame* frame = aLine->mFirstChild; count > 0;
9766
0
       --count, frame = frame->GetNextSibling()) {
9767
0
    if (LineHasNonEmptyContentWorker(frame)) {
9768
0
      return true;
9769
0
    }
9770
0
  }
9771
0
  return false;
9772
0
}
9773
9774
/* static */ bool
9775
nsLayoutUtils::IsInvisibleBreak(nsINode* aNode, nsIFrame** aNextLineFrame)
9776
0
{
9777
0
  if (aNextLineFrame) {
9778
0
    *aNextLineFrame = nullptr;
9779
0
  }
9780
0
9781
0
  if (!aNode->IsElement() || !aNode->IsEditable()) {
9782
0
    return false;
9783
0
  }
9784
0
  nsIFrame* frame = aNode->AsElement()->GetPrimaryFrame();
9785
0
  if (!frame || !frame->IsBrFrame()) {
9786
0
    return false;
9787
0
  }
9788
0
9789
0
  nsContainerFrame* f = frame->GetParent();
9790
0
  while (f && f->IsFrameOfType(nsBox::eLineParticipant)) {
9791
0
    f = f->GetParent();
9792
0
  }
9793
0
  nsBlockFrame* blockAncestor = do_QueryFrame(f);
9794
0
  if (!blockAncestor) {
9795
0
    // The container frame doesn't support line breaking.
9796
0
    return false;
9797
0
  }
9798
0
9799
0
  bool valid = false;
9800
0
  nsBlockInFlowLineIterator iter(blockAncestor, frame, &valid);
9801
0
  if (!valid) {
9802
0
    return false;
9803
0
  }
9804
0
9805
0
  bool lineNonEmpty = LineHasNonEmptyContent(iter.GetLine());
9806
0
  if (!lineNonEmpty) {
9807
0
    return false;
9808
0
  }
9809
0
9810
0
  while (iter.Next()) {
9811
0
    auto currentLine = iter.GetLine();
9812
0
    // Completely skip empty lines.
9813
0
    if (!currentLine->IsEmpty()) {
9814
0
      // If we come across an inline line, the BR has caused a visible line break.
9815
0
      if (currentLine->IsInline()) {
9816
0
        if (aNextLineFrame) {
9817
0
          *aNextLineFrame = currentLine->mFirstChild;
9818
0
        }
9819
0
        return false;
9820
0
      }
9821
0
      break;
9822
0
    }
9823
0
  }
9824
0
9825
0
  return lineNonEmpty;
9826
0
}
9827
9828
static nsRect
9829
ComputeSVGReferenceRect(nsIFrame* aFrame,
9830
                        StyleGeometryBox aGeometryBox)
9831
0
{
9832
0
  MOZ_ASSERT(aFrame->GetContent()->IsSVGElement());
9833
0
  nsRect r;
9834
0
9835
0
  // For SVG elements without associated CSS layout box, the used value for
9836
0
  // content-box, padding-box, border-box and margin-box is fill-box.
9837
0
  switch (aGeometryBox) {
9838
0
    case StyleGeometryBox::StrokeBox: {
9839
0
      // XXX Bug 1299876
9840
0
      // The size of srtoke-box is not correct if this graphic element has
9841
0
      // specific stroke-linejoin or stroke-linecap.
9842
0
      gfxRect bbox = nsSVGUtils::GetBBox(aFrame,
9843
0
                nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeStroke);
9844
0
      r = nsLayoutUtils::RoundGfxRectToAppRect(bbox,
9845
0
                                         AppUnitsPerCSSPixel());
9846
0
      break;
9847
0
    }
9848
0
    case StyleGeometryBox::ViewBox: {
9849
0
      nsIContent* content = aFrame->GetContent();
9850
0
      nsSVGElement* element = static_cast<nsSVGElement*>(content);
9851
0
      SVGViewportElement* svgElement = element->GetCtx();
9852
0
      MOZ_ASSERT(svgElement);
9853
0
9854
0
      if (svgElement && svgElement->HasViewBoxRect()) {
9855
0
        // If a ‘viewBox‘ attribute is specified for the SVG viewport creating
9856
0
        // element:
9857
0
        // 1. The reference box is positioned at the origin of the coordinate
9858
0
        //    system established by the ‘viewBox‘ attribute.
9859
0
        // 2. The dimension of the reference box is set to the width and height
9860
0
        //    values of the ‘viewBox‘ attribute.
9861
0
        nsSVGViewBox* viewBox = svgElement->GetViewBox();
9862
0
        const nsSVGViewBoxRect& value = viewBox->GetAnimValue();
9863
0
        r = nsRect(nsPresContext::CSSPixelsToAppUnits(value.x),
9864
0
                   nsPresContext::CSSPixelsToAppUnits(value.y),
9865
0
                   nsPresContext::CSSPixelsToAppUnits(value.width),
9866
0
                   nsPresContext::CSSPixelsToAppUnits(value.height));
9867
0
      } else {
9868
0
        // No viewBox is specified, uses the nearest SVG viewport as reference
9869
0
        // box.
9870
0
        svgFloatSize viewportSize = svgElement->GetViewportSize();
9871
0
        r = nsRect(0, 0,
9872
0
                   nsPresContext::CSSPixelsToAppUnits(viewportSize.width),
9873
0
                   nsPresContext::CSSPixelsToAppUnits(viewportSize.height));
9874
0
      }
9875
0
9876
0
      break;
9877
0
    }
9878
0
    case StyleGeometryBox::NoBox:
9879
0
    case StyleGeometryBox::BorderBox:
9880
0
    case StyleGeometryBox::ContentBox:
9881
0
    case StyleGeometryBox::PaddingBox:
9882
0
    case StyleGeometryBox::MarginBox:
9883
0
    case StyleGeometryBox::FillBox: {
9884
0
      gfxRect bbox = nsSVGUtils::GetBBox(aFrame,
9885
0
                                         nsSVGUtils::eBBoxIncludeFill);
9886
0
      r = nsLayoutUtils::RoundGfxRectToAppRect(bbox,
9887
0
                                         AppUnitsPerCSSPixel());
9888
0
      break;
9889
0
    }
9890
0
    default:{
9891
0
      MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type");
9892
0
      gfxRect bbox = nsSVGUtils::GetBBox(aFrame,
9893
0
                                         nsSVGUtils::eBBoxIncludeFill);
9894
0
      r = nsLayoutUtils::RoundGfxRectToAppRect(bbox,
9895
0
                                         AppUnitsPerCSSPixel());
9896
0
      break;
9897
0
    }
9898
0
  }
9899
0
9900
0
  return r;
9901
0
}
9902
9903
static nsRect
9904
ComputeHTMLReferenceRect(nsIFrame* aFrame,
9905
                         StyleGeometryBox aGeometryBox)
9906
0
{
9907
0
  nsRect r;
9908
0
9909
0
  // For elements with associated CSS layout box, the used value for fill-box,
9910
0
  // stroke-box and view-box is border-box.
9911
0
  switch (aGeometryBox) {
9912
0
    case StyleGeometryBox::ContentBox:
9913
0
      r = aFrame->GetContentRectRelativeToSelf();
9914
0
      break;
9915
0
    case StyleGeometryBox::PaddingBox:
9916
0
      r = aFrame->GetPaddingRectRelativeToSelf();
9917
0
      break;
9918
0
    case StyleGeometryBox::MarginBox:
9919
0
      r = aFrame->GetMarginRectRelativeToSelf();
9920
0
      break;
9921
0
    case StyleGeometryBox::NoBox:
9922
0
    case StyleGeometryBox::BorderBox:
9923
0
    case StyleGeometryBox::FillBox:
9924
0
    case StyleGeometryBox::StrokeBox:
9925
0
    case StyleGeometryBox::ViewBox:
9926
0
      r = aFrame->GetRectRelativeToSelf();
9927
0
      break;
9928
0
    default:
9929
0
      MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type");
9930
0
      r = aFrame->GetRectRelativeToSelf();
9931
0
      break;
9932
0
  }
9933
0
9934
0
  return r;
9935
0
}
9936
9937
/* static */ nsRect
9938
nsLayoutUtils::ComputeGeometryBox(nsIFrame* aFrame,
9939
                                  StyleGeometryBox aGeometryBox)
9940
0
{
9941
0
  // We use ComputeSVGReferenceRect for all SVG elements, except <svg>
9942
0
  // element, which does have an associated CSS layout box. In this case we
9943
0
  // should still use ComputeHTMLReferenceRect for region computing.
9944
0
  nsRect r = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)
9945
0
             ? ComputeSVGReferenceRect(aFrame, aGeometryBox)
9946
0
             : ComputeHTMLReferenceRect(aFrame, aGeometryBox);
9947
0
9948
0
  return r;
9949
0
}
9950
9951
/* static */ nsPoint
9952
nsLayoutUtils::ComputeOffsetToUserSpace(nsDisplayListBuilder* aBuilder,
9953
                                        nsIFrame* aFrame)
9954
0
{
9955
0
  nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(aFrame) -
9956
0
                         nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
9957
0
  if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
9958
0
    // Snap the offset if the reference frame is not a SVG frame, since other
9959
0
    // frames will be snapped to pixel when rendering.
9960
0
    offsetToBoundingBox = nsPoint(
9961
0
      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),
9962
0
      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));
9963
0
  }
9964
0
9965
0
  // During SVG painting, the offset computed here is applied to the gfxContext
9966
0
  // "ctx" used to paint the mask. After applying only "offsetToBoundingBox",
9967
0
  // "ctx" would have its origin at the top left corner of frame's bounding box
9968
0
  // (over all continuations).
9969
0
  // However, SVG painting needs the origin to be located at the origin of the
9970
0
  // SVG frame's "user space", i.e. the space in which, for example, the
9971
0
  // frame's BBox lives.
9972
0
  // SVG geometry frames and foreignObject frames apply their own offsets, so
9973
0
  // their position is relative to their user space. So for these frame types,
9974
0
  // if we want "ctx" to be in user space, we first need to subtract the
9975
0
  // frame's position so that SVG painting can later add it again and the
9976
0
  // frame is painted in the right place.
9977
0
  gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
9978
0
  nsPoint toUserSpace =
9979
0
    nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
9980
0
            nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
9981
0
9982
0
  return (offsetToBoundingBox - toUserSpace);
9983
0
}
9984
9985
/* static */ uint8_t
9986
nsLayoutUtils::ControlCharVisibilityDefault()
9987
0
{
9988
0
  return StaticPrefs::layout_css_control_characters_visible()
9989
0
    ? NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE
9990
0
    : NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN;
9991
0
}
9992
9993
/* static */
9994
already_AddRefed<nsFontMetrics>
9995
nsLayoutUtils::GetMetricsFor(nsPresContext* aPresContext,
9996
                             bool aIsVertical,
9997
                             const nsStyleFont* aStyleFont,
9998
                             nscoord aFontSize,
9999
                             bool aUseUserFontSet,
10000
                             FlushUserFontSet aFlushUserFontSet)
10001
0
{
10002
0
  nsFont font = aStyleFont->mFont;
10003
0
  font.size = aFontSize;
10004
0
  gfxFont::Orientation orientation
10005
0
    = aIsVertical ? gfxFont::eVertical : gfxFont::eHorizontal;
10006
0
  nsFontMetrics::Params params;
10007
0
  params.language = aStyleFont->mLanguage;
10008
0
  params.explicitLanguage = aStyleFont->mExplicitLanguage;
10009
0
  params.orientation = orientation;
10010
0
  params.userFontSet = aUseUserFontSet
10011
0
    ? aPresContext->GetUserFontSet(aFlushUserFontSet == FlushUserFontSet::Yes)
10012
0
    : nullptr;
10013
0
  params.textPerf = aPresContext->GetTextPerfMetrics();
10014
0
  return aPresContext->DeviceContext()->GetMetricsFor(font, params);
10015
0
}
10016
10017
/* static */ void
10018
nsLayoutUtils::FixupNoneGeneric(nsFont* aFont,
10019
                                const nsPresContext* aPresContext,
10020
                                uint8_t aGenericFontID,
10021
                                const nsFont* aDefaultVariableFont)
10022
0
{
10023
0
  bool useDocumentFonts =
10024
0
    aPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts);
10025
0
  if (aGenericFontID == kGenericFont_NONE ||
10026
0
      (!useDocumentFonts && (aGenericFontID == kGenericFont_cursive ||
10027
0
                             aGenericFontID == kGenericFont_fantasy))) {
10028
0
    FontFamilyType defaultGeneric =
10029
0
      aDefaultVariableFont->fontlist.GetDefaultFontType();
10030
0
    MOZ_ASSERT(aDefaultVariableFont->fontlist.IsEmpty() &&
10031
0
               (defaultGeneric == eFamily_serif ||
10032
0
                defaultGeneric == eFamily_sans_serif));
10033
0
    if (defaultGeneric != eFamily_none) {
10034
0
      if (useDocumentFonts) {
10035
0
        aFont->fontlist.SetDefaultFontType(defaultGeneric);
10036
0
      } else {
10037
0
        // Either prioritize the first generic in the list,
10038
0
        // or (if there isn't one) prepend the default variable font.
10039
0
        if (!aFont->fontlist.PrioritizeFirstGeneric()) {
10040
0
          aFont->fontlist.PrependGeneric(defaultGeneric);
10041
0
        }
10042
0
      }
10043
0
    }
10044
0
  } else {
10045
0
    aFont->fontlist.SetDefaultFontType(eFamily_none);
10046
0
  }
10047
0
}
10048
10049
/* static */ void
10050
nsLayoutUtils::ApplyMinFontSize(nsStyleFont* aFont,
10051
                                const nsPresContext* aPresContext,
10052
                                nscoord aMinFontSize)
10053
0
{
10054
0
  nscoord fontSize = aFont->mSize;
10055
0
10056
0
  // enforce the user' specified minimum font-size on the value that we expose
10057
0
  // (but don't change font-size:0, since that would unhide hidden text)
10058
0
  if (fontSize > 0) {
10059
0
    if (aMinFontSize < 0) {
10060
0
      aMinFontSize = 0;
10061
0
    } else {
10062
0
      aMinFontSize = (aMinFontSize * aFont->mMinFontSizeRatio) / 100;
10063
0
    }
10064
0
    if (fontSize < aMinFontSize && !aPresContext->IsChrome()) {
10065
0
      // override the minimum font-size constraint
10066
0
      fontSize = aMinFontSize;
10067
0
    }
10068
0
  }
10069
0
  aFont->mFont.size = fontSize;
10070
0
}
10071
10072
/* static */ void
10073
nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont, LookAndFeel::FontID aFontID,
10074
                                 const nsPresContext* aPresContext,
10075
                                 const nsFont* aDefaultVariableFont)
10076
0
{
10077
0
  gfxFontStyle fontStyle;
10078
0
  float devPerCSS =
10079
0
    (float)AppUnitsPerCSSPixel() /
10080
0
    aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
10081
0
  nsAutoString systemFontName;
10082
0
  if (LookAndFeel::GetFont(aFontID, systemFontName, fontStyle, devPerCSS)) {
10083
0
    systemFontName.Trim("\"'");
10084
0
    aSystemFont->fontlist =
10085
0
      FontFamilyList(NS_ConvertUTF16toUTF8(systemFontName), eUnquotedName);
10086
0
    aSystemFont->fontlist.SetDefaultFontType(eFamily_none);
10087
0
    aSystemFont->style = fontStyle.style;
10088
0
    aSystemFont->systemFont = fontStyle.systemFont;
10089
0
    aSystemFont->weight = fontStyle.weight;
10090
0
    aSystemFont->stretch = fontStyle.stretch;
10091
0
    aSystemFont->size =
10092
0
      NSFloatPixelsToAppUnits(fontStyle.size,
10093
0
                              aPresContext->DeviceContext()->
10094
0
                                AppUnitsPerDevPixelAtUnitFullZoom());
10095
0
    //aSystemFont->langGroup = fontStyle.langGroup;
10096
0
    aSystemFont->sizeAdjust = fontStyle.sizeAdjust;
10097
0
10098
#ifdef XP_WIN
10099
    // XXXldb This platform-specific stuff should be in the
10100
    // LookAndFeel implementation, not here.
10101
    // XXXzw Should we even still *have* this code?  It looks to be making
10102
    // old, probably obsolete assumptions.
10103
10104
    if (aFontID == LookAndFeel::eFont_Field ||
10105
        aFontID == LookAndFeel::eFont_Button ||
10106
        aFontID == LookAndFeel::eFont_List) {
10107
      // As far as I can tell the system default fonts and sizes
10108
      // on MS-Windows for Buttons, Listboxes/Comboxes and Text Fields are
10109
      // all pre-determined and cannot be changed by either the control panel
10110
      // or programmatically.
10111
      // Fields (text fields)
10112
      // Button and Selects (listboxes/comboboxes)
10113
      //    We use whatever font is defined by the system. Which it appears
10114
      //    (and the assumption is) it is always a proportional font. Then we
10115
      //    always use 2 points smaller than what the browser has defined as
10116
      //    the default proportional font.
10117
      // Assumption: system defined font is proportional
10118
      aSystemFont->size =
10119
        std::max(aDefaultVariableFont->size -
10120
                 nsPresContext::CSSPointsToAppUnits(2), 0);
10121
    }
10122
#endif
10123
  }
10124
0
}
10125
10126
static inline void
10127
AssertValidFontTag(const nsString& aString)
10128
0
{
10129
0
  // To be valid as a font feature tag, a string MUST be:
10130
0
  MOZ_ASSERT(aString.Length() == 4 &&              // (1) exactly 4 chars long
10131
0
             NS_IsAscii(aString.BeginReading()) && // (2) entirely ASCII
10132
0
             isprint(aString[0]) &&                // (3) all printable chars
10133
0
             isprint(aString[1]) &&
10134
0
             isprint(aString[2]) &&
10135
0
             isprint(aString[3]));
10136
0
}
10137
10138
/* static */ void
10139
nsLayoutUtils::ComputeFontFeatures(const nsCSSValuePairList *aFeaturesList,
10140
                                   nsTArray<gfxFontFeature>& aFeatureSettings)
10141
0
{
10142
0
  aFeatureSettings.Clear();
10143
0
  for (const nsCSSValuePairList* p = aFeaturesList; p; p = p->mNext) {
10144
0
    gfxFontFeature feat;
10145
0
10146
0
    MOZ_ASSERT(aFeaturesList->mXValue.GetUnit() == eCSSUnit_String,
10147
0
               "unexpected value unit");
10148
0
10149
0
    // tag is a 4-byte ASCII sequence
10150
0
    nsAutoString tag;
10151
0
    p->mXValue.GetStringValue(tag);
10152
0
    AssertValidFontTag(tag);
10153
0
    if (tag.Length() != 4) {
10154
0
      continue;
10155
0
    }
10156
0
    // parsing validates that these are ASCII chars
10157
0
    // tags are always big-endian
10158
0
    feat.mTag = (tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8)  | tag[3];
10159
0
10160
0
    // value
10161
0
    NS_ASSERTION(p->mYValue.GetUnit() == eCSSUnit_Integer,
10162
0
                 "should have found an integer unit");
10163
0
    feat.mValue = p->mYValue.GetIntValue();
10164
0
10165
0
    aFeatureSettings.AppendElement(feat);
10166
0
  }
10167
0
}
10168
10169
/* static */ void
10170
nsLayoutUtils::ComputeFontVariations(const nsCSSValuePairList* aVariationsList,
10171
                                     nsTArray<gfxFontVariation>& aVariationSettings)
10172
0
{
10173
0
  aVariationSettings.Clear();
10174
0
  for (const nsCSSValuePairList* p = aVariationsList; p; p = p->mNext) {
10175
0
    gfxFontVariation var;
10176
0
10177
0
    MOZ_ASSERT(aVariationsList->mXValue.GetUnit() == eCSSUnit_String,
10178
0
               "unexpected value unit");
10179
0
10180
0
    // tag is a 4-byte ASCII sequence
10181
0
    nsAutoString tag;
10182
0
    p->mXValue.GetStringValue(tag);
10183
0
    AssertValidFontTag(tag);
10184
0
    if (tag.Length() != 4) {
10185
0
      continue;
10186
0
    }
10187
0
    // parsing validates that these are ASCII chars
10188
0
    // tags are always big-endian
10189
0
    var.mTag = (tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8)  | tag[3];
10190
0
10191
0
    // value
10192
0
    NS_ASSERTION(p->mYValue.GetUnit() == eCSSUnit_Number,
10193
0
                 "should have found a number unit");
10194
0
    var.mValue = p->mYValue.GetFloatValue();
10195
0
10196
0
    aVariationSettings.AppendElement(var);
10197
0
  }
10198
0
}
10199
10200
/* static */ uint32_t
10201
nsLayoutUtils::ParseFontLanguageOverride(const nsAString& aLangTag)
10202
0
{
10203
0
  if (!aLangTag.Length() || aLangTag.Length() > 4) {
10204
0
    return NO_FONT_LANGUAGE_OVERRIDE;
10205
0
  }
10206
0
  uint32_t index, result = 0;
10207
0
  for (index = 0; index < aLangTag.Length(); ++index) {
10208
0
    char16_t ch = aLangTag[index];
10209
0
    if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII
10210
0
      return NO_FONT_LANGUAGE_OVERRIDE;
10211
0
    }
10212
0
    result = (result << 8) + ch;
10213
0
  }
10214
0
  while (index++ < 4) {
10215
0
    result = (result << 8) + 0x20;
10216
0
  }
10217
0
  return result;
10218
0
}
10219
10220
/* static */ ComputedStyle*
10221
nsLayoutUtils::StyleForScrollbar(nsIFrame* aScrollbarPart)
10222
0
{
10223
0
  // Get the closest content node which is not an anonymous scrollbar
10224
0
  // part. It should be the originating element of the scrollbar part.
10225
0
  nsIContent* content = aScrollbarPart->GetContent();
10226
0
  // Note that the content may be a normal element with scrollbar part
10227
0
  // value specified for its -moz-appearance, so don't rely on it being
10228
0
  // a native anonymous. Also note that we have to check the node name
10229
0
  // because anonymous element like generated content may originate a
10230
0
  // scrollbar.
10231
0
  MOZ_ASSERT(content, "No content for the scrollbar part?");
10232
0
  while (content && content->IsInNativeAnonymousSubtree() &&
10233
0
         content->IsAnyOfXULElements(nsGkAtoms::scrollbar,
10234
0
                                     nsGkAtoms::scrollbarbutton,
10235
0
                                     nsGkAtoms::scrollcorner,
10236
0
                                     nsGkAtoms::slider,
10237
0
                                     nsGkAtoms::thumb)) {
10238
0
    content = content->GetParent();
10239
0
  }
10240
0
  MOZ_ASSERT(content, "Native anonymous element with no originating node?");
10241
0
  // Use the style from the primary frame of the content.
10242
0
  // Note: it is important to use the primary frame rather than an
10243
0
  // ancestor frame of the scrollbar part for the correct handling of
10244
0
  // viewport scrollbar. The content of the scroll frame of the viewport
10245
0
  // is the root element, but its style inherits from the viewport.
10246
0
  // Since we need to use the style of root element for the viewport
10247
0
  // scrollbar, we have to get the style from the primary frame.
10248
0
  if (nsIFrame* primaryFrame = content->GetPrimaryFrame()) {
10249
0
    return primaryFrame->Style();
10250
0
  }
10251
0
  // If the element doesn't have primary frame, get the computed style
10252
0
  // from the element directly. This can happen on viewport, because
10253
0
  // the scrollbar of viewport may be shown when the root element has
10254
0
  // > display: none; overflow: scroll;
10255
0
  nsPresContext* pc = aScrollbarPart->PresContext();
10256
0
  MOZ_ASSERT(content == pc->Document()->GetRootElement(),
10257
0
             "Root element is the only case for this fallback "
10258
0
             "path to be triggered");
10259
0
  RefPtr<ComputedStyle> style =
10260
0
    pc->StyleSet()->ResolveServoStyle(*content->AsElement());
10261
0
  // Dropping the strong reference is fine because the style should be
10262
0
  // held strongly by the element.
10263
0
  return style.get();
10264
0
}
10265
10266
static float
10267
ResolveTransformOrigin(const nsStyleCoord& aCoord,
10268
                       TransformReferenceBox& aRefBox,
10269
                       TransformReferenceBox::DimensionGetter aGetter)
10270
0
{
10271
0
  float result = 0.0;
10272
0
  const float scale = mozilla::AppUnitsPerCSSPixel();
10273
0
  if (aCoord.GetUnit() == eStyleUnit_Calc) {
10274
0
    const nsStyleCoord::Calc *calc = aCoord.GetCalcValue();
10275
0
    result = NSAppUnitsToFloatPixels((aRefBox.*aGetter)(), scale) *
10276
0
               calc->mPercent +
10277
0
               NSAppUnitsToFloatPixels(calc->mLength, scale);
10278
0
  } else if (aCoord.GetUnit() == eStyleUnit_Percent) {
10279
0
    result = NSAppUnitsToFloatPixels((aRefBox.*aGetter)(), scale) *
10280
0
               aCoord.GetPercentValue();
10281
0
  } else {
10282
0
    MOZ_ASSERT(aCoord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
10283
0
    result = NSAppUnitsToFloatPixels(aCoord.GetCoordValue(), scale);
10284
0
  }
10285
0
  return result;
10286
0
}
10287
10288
/* static */ Maybe<MotionPathData>
10289
nsLayoutUtils::ResolveMotionPath(const nsIFrame* aFrame)
10290
0
{
10291
0
  MOZ_ASSERT(aFrame);
10292
0
10293
0
  const nsStyleDisplay* display = aFrame->StyleDisplay();
10294
0
  if (!display->mMotion || !display->mMotion->HasPath()) {
10295
0
    return Nothing();
10296
0
  }
10297
0
10298
0
  const UniquePtr<StyleMotion>& motion = display->mMotion;
10299
0
  // Bug 1429299 - Implement offset-distance for motion path. For now, we use
10300
0
  // the default value, i.e. 0%.
10301
0
  float distance = 0.0;
10302
0
  float angle = 0.0;
10303
0
  Point point;
10304
0
  if (motion->OffsetPath().GetType() == StyleShapeSourceType::Path) {
10305
0
    // Build the path and compute the point and angle for creating the
10306
0
    // equivalent translate and rotate.
10307
0
    // Here we only need to build a valid path for motion path, so
10308
0
    // using the default values of stroke-width, stoke-linecap, and fill-rule
10309
0
    // is fine for now because what we want is get the point and its normal
10310
0
    // vector along the path, instead of rendering it.
10311
0
    // FIXME: Bug 1484780, we should cache the path to avoid rebuilding it here
10312
0
    // at every restyle. (Caching the path avoids the cost of flattening it
10313
0
    // again each time.)
10314
0
    RefPtr<DrawTarget> drawTarget =
10315
0
      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
10316
0
    RefPtr<PathBuilder> builder =
10317
0
      drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
10318
0
    RefPtr<gfx::Path> gfxPath =
10319
0
      SVGPathData::BuildPath(motion->OffsetPath().GetPath()->Path(),
10320
0
                             builder,
10321
0
                             NS_STYLE_STROKE_LINECAP_BUTT,
10322
0
                             0.0);
10323
0
    if (!gfxPath) {
10324
0
      return Nothing();
10325
0
    }
10326
0
    float pathLength = gfxPath->ComputeLength();
10327
0
    float computedDistance = distance * pathLength;
10328
0
    Point tangent;
10329
0
    point = gfxPath->ComputePointAtLength(computedDistance, &tangent);
10330
0
    // Bug 1429301 - Implement offset-rotate for motion path.
10331
0
    // After implement offset-rotate, |angle| will be adjusted more.
10332
0
    // For now, the default value of offset-rotate is "auto", so we use the
10333
0
    // directional tangent vector.
10334
0
    angle = atan2(tangent.y, tangent.x);
10335
0
  } else {
10336
0
    // Bug 1480665: Implement ray() function.
10337
0
    NS_WARNING("Unsupported offset-path value");
10338
0
  }
10339
0
10340
0
  // Compute the offset for motion path translate.
10341
0
  // We need to resolve transform-origin here to calculate the correct path
10342
0
  // translate. (i.e. Center transform-origin on the path.)
10343
0
  TransformReferenceBox refBox(aFrame);
10344
0
  Point origin(
10345
0
    ResolveTransformOrigin(display->mTransformOrigin[0],
10346
0
                           refBox,
10347
0
                           &TransformReferenceBox::Width),
10348
0
    ResolveTransformOrigin(display->mTransformOrigin[1],
10349
0
                           refBox,
10350
0
                           &TransformReferenceBox::Height)
10351
0
  );
10352
0
  // Bug 1186329: the translate parameters will be adjusted more after we
10353
0
  // implement offset-position and offset-anchor.
10354
0
  return Some(MotionPathData { point - origin, angle });
10355
0
}