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