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