Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "nsLayoutUtils.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/BasicEvents.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "nsPresContext.h"
13 #include "nsIContent.h"
14 #include "nsIDOMHTMLDocument.h"
15 #include "nsIDOMHTMLElement.h"
16 #include "nsFrameList.h"
17 #include "nsGkAtoms.h"
18 #include "nsIAtom.h"
19 #include "nsCSSPseudoElements.h"
20 #include "nsCSSAnonBoxes.h"
21 #include "nsCSSColorUtils.h"
22 #include "nsView.h"
23 #include "nsPlaceholderFrame.h"
24 #include "nsIScrollableFrame.h"
25 #include "nsIDOMEvent.h"
26 #include "nsDisplayList.h"
27 #include "nsRegion.h"
28 #include "nsFrameManager.h"
29 #include "nsBlockFrame.h"
30 #include "nsBidiPresUtils.h"
31 #include "imgIContainer.h"
32 #include "ImageOps.h"
33 #include "gfxRect.h"
34 #include "gfxContext.h"
35 #include "nsRenderingContext.h"
36 #include "nsIInterfaceRequestorUtils.h"
37 #include "nsCSSRendering.h"
38 #include "nsThemeConstants.h"
39 #include "nsPIDOMWindow.h"
40 #include "nsIDocShell.h"
41 #include "nsIWidget.h"
42 #include "gfxMatrix.h"
43 #include "gfxPoint3D.h"
44 #include "gfxPrefs.h"
45 #include "gfxTypes.h"
46 #include "nsTArray.h"
47 #include "mozilla/dom/HTMLCanvasElement.h"
48 #include "nsICanvasRenderingContextInternal.h"
49 #include "gfxPlatform.h"
50 #include <algorithm>
51 #include "mozilla/dom/HTMLVideoElement.h"
52 #include "mozilla/dom/HTMLImageElement.h"
53 #include "mozilla/dom/DOMRect.h"
54 #include "imgIRequest.h"
55 #include "nsIImageLoadingContent.h"
56 #include "nsCOMPtr.h"
57 #include "nsCSSProps.h"
58 #include "nsListControlFrame.h"
59 #include "mozilla/dom/Element.h"
60 #include "nsCanvasFrame.h"
61 #include "gfxDrawable.h"
62 #include "gfxUtils.h"
63 #include "nsDataHashtable.h"
64 #include "nsTextFrame.h"
65 #include "nsFontFaceList.h"
66 #include "nsFontInflationData.h"
67 #include "nsSVGUtils.h"
68 #include "SVGTextFrame.h"
69 #include "nsStyleStructInlines.h"
70 #include "nsStyleTransformMatrix.h"
71 #include "nsIFrameInlines.h"
72 #include "ImageContainer.h"
73 #include "nsComputedDOMStyle.h"
74 #include "ActiveLayerTracker.h"
75 #include "mozilla/gfx/2D.h"
76 #include "gfx2DGlue.h"
77 #include "mozilla/LookAndFeel.h"
78 #include "UnitTransforms.h"
79 #include "TiledLayerBuffer.h" // For TILEDLAYERBUFFER_TILE_SIZE
81 #include "mozilla/Preferences.h"
83 #include "mozilla/LookAndFeel.h"
85 #ifdef MOZ_XUL
86 #include "nsXULPopupManager.h"
87 #endif
89 #include "GeckoProfiler.h"
90 #include "nsAnimationManager.h"
91 #include "nsTransitionManager.h"
92 #include "RestyleManager.h"
94 // Additional includes used on B2G by code in GetOrMaybeCreateDisplayPort().
95 #ifdef MOZ_WIDGET_GONK
96 #include "mozilla/layers/AsyncPanZoomController.h"
97 #endif
99 using namespace mozilla;
100 using namespace mozilla::css;
101 using namespace mozilla::dom;
102 using namespace mozilla::layers;
103 using namespace mozilla::layout;
104 using namespace mozilla::gfx;
106 using mozilla::image::Angle;
107 using mozilla::image::Flip;
108 using mozilla::image::ImageOps;
109 using mozilla::image::Orientation;
111 #define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled"
112 #define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled"
113 #define TEXT_ALIGN_TRUE_ENABLED_PREF_NAME "layout.css.text-align-true-value.enabled"
115 #ifdef DEBUG
116 // TODO: remove, see bug 598468.
117 bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
118 #endif // DEBUG
120 typedef FrameMetrics::ViewID ViewID;
122 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine;
123 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationMinTwips;
124 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationLineThreshold;
125 /* static */ int32_t nsLayoutUtils::sFontSizeInflationMappingIntercept;
126 /* static */ uint32_t nsLayoutUtils::sFontSizeInflationMaxRatio;
127 /* static */ bool nsLayoutUtils::sFontSizeInflationForceEnabled;
128 /* static */ bool nsLayoutUtils::sFontSizeInflationDisabledInMasterProcess;
129 /* static */ bool nsLayoutUtils::sInvalidationDebuggingIsEnabled;
130 /* static */ bool nsLayoutUtils::sCSSVariablesEnabled;
131 /* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled;
133 static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID;
135 typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
136 static ContentMap* sContentMap = nullptr;
137 static ContentMap& GetContentMap() {
138 if (!sContentMap) {
139 sContentMap = new ContentMap();
140 }
141 return *sContentMap;
142 }
144 // When the pref "layout.css.grid.enabled" changes, this function is invoked
145 // to let us update kDisplayKTable, to selectively disable or restore the
146 // entries for "grid" and "inline-grid" in that table.
147 static void
148 GridEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
149 {
150 MOZ_ASSERT(strncmp(aPrefName, GRID_ENABLED_PREF_NAME,
151 ArrayLength(GRID_ENABLED_PREF_NAME)) == 0,
152 "We only registered this callback for a single pref, so it "
153 "should only be called for that pref");
155 static int32_t sIndexOfGridInDisplayTable;
156 static int32_t sIndexOfInlineGridInDisplayTable;
157 static bool sAreGridKeywordIndicesInitialized; // initialized to false
159 bool isGridEnabled =
160 Preferences::GetBool(GRID_ENABLED_PREF_NAME, false);
161 if (!sAreGridKeywordIndicesInitialized) {
162 // First run: find the position of "grid" and "inline-grid" in
163 // kDisplayKTable.
164 sIndexOfGridInDisplayTable =
165 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_grid,
166 nsCSSProps::kDisplayKTable);
167 MOZ_ASSERT(sIndexOfGridInDisplayTable >= 0,
168 "Couldn't find grid in kDisplayKTable");
169 sIndexOfInlineGridInDisplayTable =
170 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_grid,
171 nsCSSProps::kDisplayKTable);
172 MOZ_ASSERT(sIndexOfInlineGridInDisplayTable >= 0,
173 "Couldn't find inline-grid in kDisplayKTable");
174 sAreGridKeywordIndicesInitialized = true;
175 }
177 // OK -- now, stomp on or restore the "grid" entries in kDisplayKTable,
178 // depending on whether the grid pref is enabled vs. disabled.
179 if (sIndexOfGridInDisplayTable >= 0) {
180 nsCSSProps::kDisplayKTable[sIndexOfGridInDisplayTable] =
181 isGridEnabled ? eCSSKeyword_grid : eCSSKeyword_UNKNOWN;
182 }
183 if (sIndexOfInlineGridInDisplayTable >= 0) {
184 nsCSSProps::kDisplayKTable[sIndexOfInlineGridInDisplayTable] =
185 isGridEnabled ? eCSSKeyword_inline_grid : eCSSKeyword_UNKNOWN;
186 }
187 }
189 // When the pref "layout.css.sticky.enabled" changes, this function is invoked
190 // to let us update kPositionKTable, to selectively disable or restore the
191 // entry for "sticky" in that table.
192 static void
193 StickyEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
194 {
195 MOZ_ASSERT(strncmp(aPrefName, STICKY_ENABLED_PREF_NAME,
196 ArrayLength(STICKY_ENABLED_PREF_NAME)) == 0,
197 "We only registered this callback for a single pref, so it "
198 "should only be called for that pref");
200 static int32_t sIndexOfStickyInPositionTable;
201 static bool sIsStickyKeywordIndexInitialized; // initialized to false
203 bool isStickyEnabled =
204 Preferences::GetBool(STICKY_ENABLED_PREF_NAME, false);
206 if (!sIsStickyKeywordIndexInitialized) {
207 // First run: find the position of "sticky" in kPositionKTable.
208 sIndexOfStickyInPositionTable =
209 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_sticky,
210 nsCSSProps::kPositionKTable);
211 MOZ_ASSERT(sIndexOfStickyInPositionTable >= 0,
212 "Couldn't find sticky in kPositionKTable");
213 sIsStickyKeywordIndexInitialized = true;
214 }
216 // OK -- now, stomp on or restore the "sticky" entry in kPositionKTable,
217 // depending on whether the sticky pref is enabled vs. disabled.
218 nsCSSProps::kPositionKTable[sIndexOfStickyInPositionTable] =
219 isStickyEnabled ? eCSSKeyword_sticky : eCSSKeyword_UNKNOWN;
220 }
222 // When the pref "layout.css.text-align-true-value.enabled" changes, this
223 // function is called to let us update kTextAlignKTable & kTextAlignLastKTable,
224 // to selectively disable or restore the entries for "true" in those tables.
225 static void
226 TextAlignTrueEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
227 {
228 NS_ASSERTION(strcmp(aPrefName, TEXT_ALIGN_TRUE_ENABLED_PREF_NAME) == 0,
229 "Did you misspell " TEXT_ALIGN_TRUE_ENABLED_PREF_NAME " ?");
231 static bool sIsInitialized;
232 static int32_t sIndexOfTrueInTextAlignTable;
233 static int32_t sIndexOfTrueInTextAlignLastTable;
234 bool isTextAlignTrueEnabled =
235 Preferences::GetBool(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, false);
237 if (!sIsInitialized) {
238 // First run: find the position of "true" in kTextAlignKTable.
239 sIndexOfTrueInTextAlignTable =
240 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true,
241 nsCSSProps::kTextAlignKTable);
242 // First run: find the position of "true" in kTextAlignLastKTable.
243 sIndexOfTrueInTextAlignLastTable =
244 nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true,
245 nsCSSProps::kTextAlignLastKTable);
246 sIsInitialized = true;
247 }
249 // OK -- now, stomp on or restore the "true" entry in the keyword tables,
250 // depending on whether the pref is enabled vs. disabled.
251 MOZ_ASSERT(sIndexOfTrueInTextAlignTable >= 0);
252 nsCSSProps::kTextAlignKTable[sIndexOfTrueInTextAlignTable] =
253 isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN;
254 MOZ_ASSERT(sIndexOfTrueInTextAlignLastTable >= 0);
255 nsCSSProps::kTextAlignLastKTable[sIndexOfTrueInTextAlignLastTable] =
256 isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN;
257 }
259 template <class AnimationsOrTransitions>
260 static AnimationsOrTransitions*
261 HasAnimationOrTransitionForCompositor(nsIContent* aContent,
262 nsIAtom* aAnimationProperty,
263 nsCSSProperty aProperty)
264 {
265 AnimationsOrTransitions* animations =
266 static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty));
267 if (animations) {
268 bool propertyMatches = animations->HasAnimationOfProperty(aProperty);
269 if (propertyMatches &&
270 animations->CanPerformOnCompositorThread(
271 CommonElementAnimationData::CanAnimate_AllowPartial)) {
272 return animations;
273 }
274 }
276 return nullptr;
277 }
279 bool
280 nsLayoutUtils::HasAnimationsForCompositor(nsIContent* aContent,
281 nsCSSProperty aProperty)
282 {
283 if (!aContent->MayHaveAnimations())
284 return false;
285 return HasAnimationOrTransitionForCompositor<ElementAnimations>
286 (aContent, nsGkAtoms::animationsProperty, aProperty) ||
287 HasAnimationOrTransitionForCompositor<ElementTransitions>
288 (aContent, nsGkAtoms::transitionsProperty, aProperty);
289 }
291 template <class AnimationsOrTransitions>
292 AnimationsOrTransitions*
293 mozilla::HasAnimationOrTransition(nsIContent* aContent,
294 nsIAtom* aAnimationProperty,
295 nsCSSProperty aProperty)
296 {
297 AnimationsOrTransitions* animations =
298 static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty));
299 if (animations) {
300 bool propertyMatches = animations->HasAnimationOfProperty(aProperty);
301 if (propertyMatches) {
302 return animations;
303 }
304 }
306 return nullptr;
307 }
309 template ElementAnimations*
310 mozilla::HasAnimationOrTransition<ElementAnimations>(nsIContent* aContent,
311 nsIAtom* aAnimationProperty,
312 nsCSSProperty aProperty);
314 template ElementTransitions*
315 mozilla::HasAnimationOrTransition<ElementTransitions>(nsIContent* aContent,
316 nsIAtom* aAnimationProperty,
317 nsCSSProperty aProperty);
320 bool
321 nsLayoutUtils::HasAnimations(nsIContent* aContent,
322 nsCSSProperty aProperty)
323 {
324 if (!aContent->MayHaveAnimations())
325 return false;
326 return HasAnimationOrTransition<ElementAnimations>
327 (aContent, nsGkAtoms::animationsProperty, aProperty) ||
328 HasAnimationOrTransition<ElementTransitions>
329 (aContent, nsGkAtoms::transitionsProperty, aProperty);
330 }
332 static gfxSize
333 GetScaleForValue(const nsStyleAnimation::Value& aValue,
334 nsIFrame* aFrame)
335 {
336 if (!aFrame) {
337 NS_WARNING("No frame.");
338 return gfxSize();
339 }
340 if (aValue.GetUnit() != nsStyleAnimation::eUnit_Transform) {
341 NS_WARNING("Expected a transform.");
342 return gfxSize();
343 }
345 nsCSSValueSharedList* list = aValue.GetCSSValueSharedListValue();
346 MOZ_ASSERT(list->mHead);
348 if (list->mHead->mValue.GetUnit() == eCSSUnit_None) {
349 // There is an animation, but no actual transform yet.
350 return gfxSize();
351 }
353 nsRect frameBounds = aFrame->GetRect();
354 bool dontCare;
355 gfx3DMatrix transform = nsStyleTransformMatrix::ReadTransforms(
356 list->mHead,
357 aFrame->StyleContext(),
358 aFrame->PresContext(), dontCare, frameBounds,
359 aFrame->PresContext()->AppUnitsPerDevPixel());
361 gfxMatrix transform2d;
362 bool canDraw2D = transform.CanDraw2D(&transform2d);
363 if (!canDraw2D) {
364 return gfxSize();
365 }
367 return transform2d.ScaleFactors(true);
368 }
370 float
371 GetSuitableScale(float aMaxScale, float aMinScale)
372 {
373 // If the minimum scale >= 1.0f, use it; if the maximum <= 1.0f, use it;
374 // otherwise use 1.0f.
375 if (aMinScale >= 1.0f) {
376 return aMinScale;
377 }
378 else if (aMaxScale <= 1.0f) {
379 return aMaxScale;
380 }
382 return 1.0f;
383 }
385 gfxSize
386 nsLayoutUtils::ComputeSuitableScaleForAnimation(nsIContent* aContent)
387 {
388 gfxSize maxScale(1.0f, 1.0f);
389 gfxSize minScale(1.0f, 1.0f);
391 ElementAnimations* animations = HasAnimationOrTransitionForCompositor<ElementAnimations>
392 (aContent, nsGkAtoms::animationsProperty, eCSSProperty_transform);
393 if (animations) {
394 for (uint32_t animIdx = animations->mAnimations.Length(); animIdx-- != 0; ) {
395 mozilla::StyleAnimation& anim = animations->mAnimations[animIdx];
396 for (uint32_t propIdx = anim.mProperties.Length(); propIdx-- != 0; ) {
397 AnimationProperty& prop = anim.mProperties[propIdx];
398 if (prop.mProperty == eCSSProperty_transform) {
399 for (uint32_t segIdx = prop.mSegments.Length(); segIdx-- != 0; ) {
400 AnimationPropertySegment& segment = prop.mSegments[segIdx];
401 gfxSize from = GetScaleForValue(segment.mFromValue,
402 aContent->GetPrimaryFrame());
403 maxScale.width = std::max<float>(maxScale.width, from.width);
404 maxScale.height = std::max<float>(maxScale.height, from.height);
405 minScale.width = std::min<float>(minScale.width, from.width);
406 minScale.height = std::min<float>(minScale.height, from.height);
407 gfxSize to = GetScaleForValue(segment.mToValue,
408 aContent->GetPrimaryFrame());
409 maxScale.width = std::max<float>(maxScale.width, to.width);
410 maxScale.height = std::max<float>(maxScale.height, to.height);
411 minScale.width = std::min<float>(minScale.width, to.width);
412 minScale.height = std::min<float>(minScale.height, to.height);
413 }
414 }
415 }
416 }
417 }
419 ElementTransitions* transitions = HasAnimationOrTransitionForCompositor<ElementTransitions>
420 (aContent, nsGkAtoms::transitionsProperty, eCSSProperty_transform);
421 if (transitions) {
422 for (uint32_t i = 0, i_end = transitions->mPropertyTransitions.Length();
423 i < i_end; ++i){
424 ElementPropertyTransition &pt = transitions->mPropertyTransitions[i];
425 if (pt.IsRemovedSentinel()) {
426 continue;
427 }
428 MOZ_ASSERT(pt.mProperties.Length() == 1,
429 "Should have one animation property for a transition");
430 MOZ_ASSERT(pt.mProperties[0].mSegments.Length() == 1,
431 "Animation property should have one segment for a transition");
432 const AnimationPropertySegment& segment = pt.mProperties[0].mSegments[0];
434 if (pt.mProperties[0].mProperty == eCSSProperty_transform) {
435 gfxSize start = GetScaleForValue(segment.mFromValue,
436 aContent->GetPrimaryFrame());
437 maxScale.width = std::max<float>(maxScale.width, start.width);
438 maxScale.height = std::max<float>(maxScale.height, start.height);
439 minScale.width = std::min<float>(minScale.width, start.width);
440 minScale.height = std::min<float>(minScale.height, start.height);
441 gfxSize end = GetScaleForValue(segment.mToValue,
442 aContent->GetPrimaryFrame());
443 maxScale.width = std::max<float>(maxScale.width, end.width);
444 maxScale.height = std::max<float>(maxScale.height, end.height);
445 minScale.width = std::min<float>(minScale.width, end.width);
446 minScale.height = std::min<float>(minScale.height, end.height);
447 }
448 }
449 }
451 return gfxSize(GetSuitableScale(maxScale.width, minScale.width),
452 GetSuitableScale(maxScale.height, minScale.height));
453 }
455 bool
456 nsLayoutUtils::AreAsyncAnimationsEnabled()
457 {
458 static bool sAreAsyncAnimationsEnabled;
459 static bool sAsyncPrefCached = false;
461 if (!sAsyncPrefCached) {
462 sAsyncPrefCached = true;
463 Preferences::AddBoolVarCache(&sAreAsyncAnimationsEnabled,
464 "layers.offmainthreadcomposition.async-animations");
465 }
467 return sAreAsyncAnimationsEnabled &&
468 gfxPlatform::OffMainThreadCompositingEnabled();
469 }
471 bool
472 nsLayoutUtils::IsAnimationLoggingEnabled()
473 {
474 static bool sShouldLog;
475 static bool sShouldLogPrefCached;
477 if (!sShouldLogPrefCached) {
478 sShouldLogPrefCached = true;
479 Preferences::AddBoolVarCache(&sShouldLog,
480 "layers.offmainthreadcomposition.log-animations");
481 }
483 return sShouldLog;
484 }
486 bool
487 nsLayoutUtils::UseBackgroundNearestFiltering()
488 {
489 static bool sUseBackgroundNearestFilteringEnabled;
490 static bool sUseBackgroundNearestFilteringPrefInitialised = false;
492 if (!sUseBackgroundNearestFilteringPrefInitialised) {
493 sUseBackgroundNearestFilteringPrefInitialised = true;
494 sUseBackgroundNearestFilteringEnabled =
495 Preferences::GetBool("gfx.filter.nearest.force-enabled", false);
496 }
498 return sUseBackgroundNearestFilteringEnabled;
499 }
501 bool
502 nsLayoutUtils::GPUImageScalingEnabled()
503 {
504 static bool sGPUImageScalingEnabled;
505 static bool sGPUImageScalingPrefInitialised = false;
507 if (!sGPUImageScalingPrefInitialised) {
508 sGPUImageScalingPrefInitialised = true;
509 sGPUImageScalingEnabled =
510 Preferences::GetBool("layout.gpu-image-scaling.enabled", false);
511 }
513 return sGPUImageScalingEnabled;
514 }
516 bool
517 nsLayoutUtils::AnimatedImageLayersEnabled()
518 {
519 static bool sAnimatedImageLayersEnabled;
520 static bool sAnimatedImageLayersPrefCached = false;
522 if (!sAnimatedImageLayersPrefCached) {
523 sAnimatedImageLayersPrefCached = true;
524 Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled,
525 "layout.animated-image-layers.enabled",
526 false);
527 }
529 return sAnimatedImageLayersEnabled;
530 }
532 bool
533 nsLayoutUtils::CSSFiltersEnabled()
534 {
535 static bool sCSSFiltersEnabled;
536 static bool sCSSFiltersPrefCached = false;
538 if (!sCSSFiltersPrefCached) {
539 sCSSFiltersPrefCached = true;
540 Preferences::AddBoolVarCache(&sCSSFiltersEnabled,
541 "layout.css.filters.enabled",
542 false);
543 }
545 return sCSSFiltersEnabled;
546 }
548 bool
549 nsLayoutUtils::UnsetValueEnabled()
550 {
551 static bool sUnsetValueEnabled;
552 static bool sUnsetValuePrefCached = false;
554 if (!sUnsetValuePrefCached) {
555 sUnsetValuePrefCached = true;
556 Preferences::AddBoolVarCache(&sUnsetValueEnabled,
557 "layout.css.unset-value.enabled",
558 false);
559 }
561 return sUnsetValueEnabled;
562 }
564 bool
565 nsLayoutUtils::IsTextAlignTrueValueEnabled()
566 {
567 static bool sTextAlignTrueValueEnabled;
568 static bool sTextAlignTrueValueEnabledPrefCached = false;
570 if (!sTextAlignTrueValueEnabledPrefCached) {
571 sTextAlignTrueValueEnabledPrefCached = true;
572 Preferences::AddBoolVarCache(&sTextAlignTrueValueEnabled,
573 TEXT_ALIGN_TRUE_ENABLED_PREF_NAME,
574 false);
575 }
577 return sTextAlignTrueValueEnabled;
578 }
580 void
581 nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
582 nsOverflowAreas& aOverflowAreas,
583 FrameChildListIDs aSkipChildLists)
584 {
585 // Iterate over all children except pop-ups.
586 FrameChildListIDs skip = aSkipChildLists |
587 nsIFrame::kSelectPopupList | nsIFrame::kPopupList;
588 for (nsIFrame::ChildListIterator childLists(aFrame);
589 !childLists.IsDone(); childLists.Next()) {
590 if (skip.Contains(childLists.CurrentID())) {
591 continue;
592 }
594 nsFrameList children = childLists.CurrentList();
595 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
596 nsIFrame* child = e.get();
597 nsOverflowAreas childOverflow =
598 child->GetOverflowAreas() + child->GetPosition();
599 aOverflowAreas.UnionWith(childOverflow);
600 }
601 }
602 }
604 static void DestroyViewID(void* aObject, nsIAtom* aPropertyName,
605 void* aPropertyValue, void* aData)
606 {
607 ViewID* id = static_cast<ViewID*>(aPropertyValue);
608 GetContentMap().Remove(*id);
609 delete id;
610 }
612 /**
613 * A namespace class for static layout utilities.
614 */
616 bool
617 nsLayoutUtils::FindIDFor(const nsIContent* aContent, ViewID* aOutViewId)
618 {
619 void* scrollIdProperty = aContent->GetProperty(nsGkAtoms::RemoteId);
620 if (scrollIdProperty) {
621 *aOutViewId = *static_cast<ViewID*>(scrollIdProperty);
622 return true;
623 }
624 return false;
625 }
627 ViewID
628 nsLayoutUtils::FindOrCreateIDFor(nsIContent* aContent)
629 {
630 ViewID scrollId;
632 if (!FindIDFor(aContent, &scrollId)) {
633 scrollId = sScrollIdCounter++;
634 aContent->SetProperty(nsGkAtoms::RemoteId, new ViewID(scrollId),
635 DestroyViewID);
636 GetContentMap().Put(scrollId, aContent);
637 }
639 return scrollId;
640 }
642 nsIContent*
643 nsLayoutUtils::FindContentFor(ViewID aId)
644 {
645 NS_ABORT_IF_FALSE(aId != FrameMetrics::NULL_SCROLL_ID,
646 "Cannot find a content element in map for null IDs.");
647 nsIContent* content;
648 bool exists = GetContentMap().Get(aId, &content);
650 if (exists) {
651 return content;
652 } else {
653 return nullptr;
654 }
655 }
657 nsIScrollableFrame*
658 nsLayoutUtils::FindScrollableFrameFor(ViewID aId)
659 {
660 nsIContent* content = FindContentFor(aId);
661 if (!content) {
662 return nullptr;
663 }
665 nsIFrame* scrolledFrame = content->GetPrimaryFrame();
666 if (scrolledFrame && content->OwnerDoc()->GetRootElement() == content) {
667 // The content is the root element of a subdocument, so return the root scrollable
668 // for the subdocument.
669 scrolledFrame = scrolledFrame->PresContext()->PresShell()->GetRootScrollFrame();
670 }
671 return scrolledFrame ? scrolledFrame->GetScrollTargetFrame() : nullptr;
672 }
674 bool
675 nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult)
676 {
677 DisplayPortPropertyData* rectData =
678 static_cast<DisplayPortPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPort));
679 DisplayPortMarginsPropertyData* marginsData =
680 static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
681 if (!rectData && !marginsData) {
682 return false;
683 }
685 if (aResult) {
686 if (rectData && marginsData) {
687 // choose margins if equal priority
688 if (rectData->mPriority > marginsData->mPriority) {
689 marginsData = nullptr;
690 } else {
691 rectData = nullptr;
692 }
693 }
695 if (rectData) {
696 *aResult = rectData->mRect;
697 } else {
698 nsRect* baseData =
699 static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase));
700 nsRect base;
701 if (baseData) {
702 base = *baseData;
703 }
705 nsIFrame* frame = aContent->GetPrimaryFrame();
706 if (frame) {
707 bool isRoot = false;
708 if (aContent->OwnerDoc()->GetRootElement() == aContent) {
709 // We want the scroll frame, the root scroll frame differs from all
710 // others in that the primary frame is not the scroll frame.
711 frame = frame->PresContext()->PresShell()->GetRootScrollFrame();
712 isRoot = true;
713 }
715 // first convert the base rect to layer pixels
716 nsPresContext* presContext = frame->PresContext();
717 int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
718 gfxSize res = presContext->PresShell()->GetCumulativeResolution();
719 gfxSize parentRes = res;
720 if (isRoot) {
721 // the base rect for root scroll frames is specified in the parent document
722 // coordinate space, so it doesn't include the local resolution.
723 gfxSize localRes = presContext->PresShell()->GetResolution();
724 parentRes.width /= localRes.width;
725 parentRes.height /= localRes.height;
726 }
727 LayerRect rect;
728 rect.x = parentRes.width * NSAppUnitsToFloatPixels(base.x, auPerDevPixel);
729 rect.y = parentRes.height * NSAppUnitsToFloatPixels(base.y, auPerDevPixel);
730 rect.width =
731 parentRes.width * NSAppUnitsToFloatPixels(base.width, auPerDevPixel);
732 rect.height =
733 parentRes.height * NSAppUnitsToFloatPixels(base.height, auPerDevPixel);
735 rect.Inflate(marginsData->mMargins);
737 nsIScrollableFrame* scrollableFrame = frame->GetScrollTargetFrame();
738 nsPoint scrollPos(
739 scrollableFrame ? scrollableFrame->GetScrollPosition() : nsPoint(0,0));
740 if (marginsData->mAlignmentX > 0 || marginsData->mAlignmentY > 0) {
741 // Avoid division by zero.
742 if (marginsData->mAlignmentX == 0) {
743 marginsData->mAlignmentX = 1;
744 }
745 if (marginsData->mAlignmentY == 0) {
746 marginsData->mAlignmentY = 1;
747 }
749 LayerPoint scrollPosLayer(
750 res.width * NSAppUnitsToFloatPixels(scrollPos.x, auPerDevPixel),
751 res.height * NSAppUnitsToFloatPixels(scrollPos.y, auPerDevPixel));
752 rect += scrollPosLayer;
754 // Inflate the rectangle by 1 so that we always push to the next tile
755 // boundary. This is desirable to stop from having a rectangle with a
756 // moving origin occasionally being smaller when it coincidentally lines
757 // up to tile boundaries.
758 rect.Inflate(1);
760 float left =
761 marginsData->mAlignmentX * floor(rect.x / marginsData->mAlignmentX);
762 float top =
763 marginsData->mAlignmentY * floor(rect.y / marginsData->mAlignmentY);
764 float right =
765 marginsData->mAlignmentX * ceil(rect.XMost() / marginsData->mAlignmentX);
766 float bottom =
767 marginsData->mAlignmentY * ceil(rect.YMost() / marginsData->mAlignmentY);
768 rect = LayerRect(left, top, right - left, bottom - top);
769 rect -= scrollPosLayer;
770 }
772 nsRect result;
773 result.x = NSFloatPixelsToAppUnits(rect.x / res.width, auPerDevPixel);
774 result.y = NSFloatPixelsToAppUnits(rect.y / res.height, auPerDevPixel);
775 result.width =
776 NSFloatPixelsToAppUnits(rect.width / res.width, auPerDevPixel);
777 result.height =
778 NSFloatPixelsToAppUnits(rect.height / res.height, auPerDevPixel);
780 // Finally, clamp the display port to the expanded scrollable rect.
781 nsRect expandedScrollableRect = CalculateExpandedScrollableRect(frame);
782 result = expandedScrollableRect.Intersect(result + scrollPos) - scrollPos;
784 *aResult = result;
785 }
786 }
787 }
789 return true;
790 }
792 void
793 nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
794 nsIPresShell* aPresShell,
795 const LayerMargin& aMargins,
796 uint32_t aAlignmentX,
797 uint32_t aAlignmentY,
798 uint32_t aPriority,
799 RepaintMode aRepaintMode)
800 {
801 DisplayPortMarginsPropertyData* currentData =
802 static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
803 if (currentData && currentData->mPriority > aPriority) {
804 return;
805 }
807 aContent->SetProperty(nsGkAtoms::DisplayPortMargins,
808 new DisplayPortMarginsPropertyData(
809 aMargins, aAlignmentX, aAlignmentY, aPriority),
810 nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
812 nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame();
813 if (rootScrollFrame && aContent == rootScrollFrame->GetContent()) {
814 // We are setting a root displayport for a document.
815 // The pres shell needs a special flag set.
816 aPresShell->SetIgnoreViewportScrolling(true);
817 }
819 if (aRepaintMode == RepaintMode::Repaint) {
820 nsIFrame* rootFrame = aPresShell->FrameManager()->GetRootFrame();
821 if (rootFrame) {
822 rootFrame->SchedulePaint();
823 }
824 }
825 }
827 void
828 nsLayoutUtils::SetDisplayPortBase(nsIContent* aContent, const nsRect& aBase)
829 {
830 aContent->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aBase),
831 nsINode::DeleteProperty<nsRect>);
832 }
834 void
835 nsLayoutUtils::SetDisplayPortBaseIfNotSet(nsIContent* aContent, const nsRect& aBase)
836 {
837 if (!aContent->GetProperty(nsGkAtoms::DisplayPortBase)) {
838 SetDisplayPortBase(aContent, aBase);
839 }
840 }
842 bool
843 nsLayoutUtils::GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult)
844 {
845 void* property = aContent->GetProperty(nsGkAtoms::CriticalDisplayPort);
846 if (!property) {
847 return false;
848 }
850 if (aResult) {
851 *aResult = *static_cast<nsRect*>(property);
852 }
853 return true;
854 }
856 nsIFrame*
857 nsLayoutUtils::LastContinuationWithChild(nsIFrame* aFrame)
858 {
859 NS_PRECONDITION(aFrame, "NULL frame pointer");
860 aFrame = aFrame->LastContinuation();
861 while (!aFrame->GetFirstPrincipalChild() &&
862 aFrame->GetPrevContinuation()) {
863 aFrame = aFrame->GetPrevContinuation();
864 }
865 return aFrame;
866 }
868 /**
869 * GetFirstChildFrame returns the first "real" child frame of a
870 * given frame. It will descend down into pseudo-frames (unless the
871 * pseudo-frame is the :before generated frame).
872 * @param aFrame the frame
873 * @param aFrame the frame's content node
874 */
875 static nsIFrame*
876 GetFirstChildFrame(nsIFrame* aFrame,
877 nsIContent* aContent)
878 {
879 NS_PRECONDITION(aFrame, "NULL frame pointer");
881 // Get the first child frame
882 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
884 // If the child frame is a pseudo-frame, then return its first child.
885 // Note that the frame we create for the generated content is also a
886 // pseudo-frame and so don't drill down in that case
887 if (childFrame &&
888 childFrame->IsPseudoFrame(aContent) &&
889 !childFrame->IsGeneratedContentFrame()) {
890 return GetFirstChildFrame(childFrame, aContent);
891 }
893 return childFrame;
894 }
896 /**
897 * GetLastChildFrame returns the last "real" child frame of a
898 * given frame. It will descend down into pseudo-frames (unless the
899 * pseudo-frame is the :after generated frame).
900 * @param aFrame the frame
901 * @param aFrame the frame's content node
902 */
903 static nsIFrame*
904 GetLastChildFrame(nsIFrame* aFrame,
905 nsIContent* aContent)
906 {
907 NS_PRECONDITION(aFrame, "NULL frame pointer");
909 // Get the last continuation frame that's a parent
910 nsIFrame* lastParentContinuation =
911 nsLayoutUtils::LastContinuationWithChild(aFrame);
912 nsIFrame* lastChildFrame =
913 lastParentContinuation->GetLastChild(nsIFrame::kPrincipalList);
914 if (lastChildFrame) {
915 // Get the frame's first continuation. This matters in case the frame has
916 // been continued across multiple lines or split by BiDi resolution.
917 lastChildFrame = lastChildFrame->FirstContinuation();
919 // If the last child frame is a pseudo-frame, then return its last child.
920 // Note that the frame we create for the generated content is also a
921 // pseudo-frame and so don't drill down in that case
922 if (lastChildFrame &&
923 lastChildFrame->IsPseudoFrame(aContent) &&
924 !lastChildFrame->IsGeneratedContentFrame()) {
925 return GetLastChildFrame(lastChildFrame, aContent);
926 }
928 return lastChildFrame;
929 }
931 return nullptr;
932 }
934 //static
935 FrameChildListID
936 nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame)
937 {
938 nsIFrame::ChildListID id = nsIFrame::kPrincipalList;
940 if (aChildFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
941 nsIFrame* pif = aChildFrame->GetPrevInFlow();
942 if (pif->GetParent() == aChildFrame->GetParent()) {
943 id = nsIFrame::kExcessOverflowContainersList;
944 }
945 else {
946 id = nsIFrame::kOverflowContainersList;
947 }
948 }
949 // See if the frame is moved out of the flow
950 else if (aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
951 // Look at the style information to tell
952 const nsStyleDisplay* disp = aChildFrame->StyleDisplay();
954 if (NS_STYLE_POSITION_ABSOLUTE == disp->mPosition) {
955 id = nsIFrame::kAbsoluteList;
956 } else if (NS_STYLE_POSITION_FIXED == disp->mPosition) {
957 if (nsLayoutUtils::IsReallyFixedPos(aChildFrame)) {
958 id = nsIFrame::kFixedList;
959 } else {
960 id = nsIFrame::kAbsoluteList;
961 }
962 #ifdef MOZ_XUL
963 } else if (NS_STYLE_DISPLAY_POPUP == disp->mDisplay) {
964 // Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set
965 #ifdef DEBUG
966 nsIFrame* parent = aChildFrame->GetParent();
967 NS_ASSERTION(parent && parent->GetType() == nsGkAtoms::popupSetFrame,
968 "Unexpected parent");
969 #endif // DEBUG
971 id = nsIFrame::kPopupList;
972 #endif // MOZ_XUL
973 } else {
974 NS_ASSERTION(aChildFrame->IsFloating(), "not a floated frame");
975 id = nsIFrame::kFloatList;
976 }
978 } else {
979 nsIAtom* childType = aChildFrame->GetType();
980 if (nsGkAtoms::menuPopupFrame == childType) {
981 nsIFrame* parent = aChildFrame->GetParent();
982 MOZ_ASSERT(parent, "nsMenuPopupFrame can't be the root frame");
983 if (parent) {
984 if (parent->GetType() == nsGkAtoms::popupSetFrame) {
985 id = nsIFrame::kPopupList;
986 } else {
987 nsIFrame* firstPopup = parent->GetFirstChild(nsIFrame::kPopupList);
988 MOZ_ASSERT(!firstPopup || !firstPopup->GetNextSibling(),
989 "We assume popupList only has one child, but it has more.");
990 id = firstPopup == aChildFrame
991 ? nsIFrame::kPopupList
992 : nsIFrame::kPrincipalList;
993 }
994 } else {
995 id = nsIFrame::kPrincipalList;
996 }
997 } else if (nsGkAtoms::tableColGroupFrame == childType) {
998 id = nsIFrame::kColGroupList;
999 } else if (nsGkAtoms::tableCaptionFrame == childType) {
1000 id = nsIFrame::kCaptionList;
1001 } else {
1002 id = nsIFrame::kPrincipalList;
1003 }
1004 }
1006 #ifdef DEBUG
1007 // Verify that the frame is actually in that child list or in the
1008 // corresponding overflow list.
1009 nsIFrame* parent = aChildFrame->GetParent();
1010 bool found = parent->GetChildList(id).ContainsFrame(aChildFrame);
1011 if (!found) {
1012 if (!(aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
1013 found = parent->GetChildList(nsIFrame::kOverflowList)
1014 .ContainsFrame(aChildFrame);
1015 }
1016 else if (aChildFrame->IsFloating()) {
1017 found = parent->GetChildList(nsIFrame::kOverflowOutOfFlowList)
1018 .ContainsFrame(aChildFrame);
1019 if (!found) {
1020 found = parent->GetChildList(nsIFrame::kPushedFloatsList)
1021 .ContainsFrame(aChildFrame);
1022 }
1023 }
1024 // else it's positioned and should have been on the 'id' child list.
1025 NS_POSTCONDITION(found, "not in child list");
1026 }
1027 #endif
1029 return id;
1030 }
1032 // static
1033 nsIFrame*
1034 nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame)
1035 {
1036 NS_PRECONDITION(aFrame, "NULL frame pointer");
1037 NS_ASSERTION(!aFrame->GetPrevContinuation(),
1038 "aFrame must be first continuation");
1040 nsIFrame* cif = aFrame->GetContentInsertionFrame();
1041 nsIFrame* firstFrame = GetFirstChildFrame(cif, aFrame->GetContent());
1043 if (firstFrame && IsGeneratedContentFor(nullptr, firstFrame,
1044 nsCSSPseudoElements::before)) {
1045 return firstFrame;
1046 }
1048 return nullptr;
1049 }
1051 // static
1052 nsIFrame*
1053 nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame)
1054 {
1055 NS_PRECONDITION(aFrame, "NULL frame pointer");
1057 nsIFrame* cif = aFrame->GetContentInsertionFrame();
1058 nsIFrame* lastFrame = GetLastChildFrame(cif, aFrame->GetContent());
1060 if (lastFrame && IsGeneratedContentFor(nullptr, lastFrame,
1061 nsCSSPseudoElements::after)) {
1062 return lastFrame;
1063 }
1065 return nullptr;
1066 }
1068 // static
1069 nsIFrame*
1070 nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame, nsIAtom* aFrameType)
1071 {
1072 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
1073 if (frame->GetType() == aFrameType) {
1074 return frame;
1075 }
1076 }
1077 return nullptr;
1078 }
1080 // static
1081 nsIFrame*
1082 nsLayoutUtils::GetStyleFrame(nsIFrame* aFrame)
1083 {
1084 if (aFrame->GetType() == nsGkAtoms::tableOuterFrame) {
1085 nsIFrame* inner = aFrame->GetFirstPrincipalChild();
1086 NS_ASSERTION(inner, "Outer table must have an inner");
1087 return inner;
1088 }
1090 return aFrame;
1091 }
1093 nsIFrame*
1094 nsLayoutUtils::GetStyleFrame(const nsIContent* aContent)
1095 {
1096 nsIFrame *frame = aContent->GetPrimaryFrame();
1097 if (!frame) {
1098 return nullptr;
1099 }
1101 return nsLayoutUtils::GetStyleFrame(frame);
1102 }
1104 nsIFrame*
1105 nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
1106 NS_ASSERTION(nsGkAtoms::placeholderFrame == aFrame->GetType(),
1107 "Must have a placeholder here");
1108 if (aFrame->GetStateBits() & PLACEHOLDER_FOR_FLOAT) {
1109 nsIFrame *outOfFlowFrame =
1110 nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
1111 NS_ASSERTION(outOfFlowFrame->IsFloating(),
1112 "How did that happen?");
1113 return outOfFlowFrame;
1114 }
1116 return nullptr;
1117 }
1119 // static
1120 bool
1121 nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
1122 nsIFrame* aFrame,
1123 nsIAtom* aPseudoElement)
1124 {
1125 NS_PRECONDITION(aFrame, "Must have a frame");
1126 NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
1128 if (!aFrame->IsGeneratedContentFrame()) {
1129 return false;
1130 }
1131 nsIFrame* parent = aFrame->GetParent();
1132 NS_ASSERTION(parent, "Generated content can't be root frame");
1133 if (parent->IsGeneratedContentFrame()) {
1134 // Not the root of the generated content
1135 return false;
1136 }
1138 if (aContent && parent->GetContent() != aContent) {
1139 return false;
1140 }
1142 return (aFrame->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore) ==
1143 (aPseudoElement == nsCSSPseudoElements::before);
1144 }
1146 // static
1147 nsIFrame*
1148 nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame,
1149 nsPoint* aExtraOffset)
1150 {
1151 nsIFrame* p = aFrame->GetParent();
1152 if (p)
1153 return p;
1155 nsView* v = aFrame->GetView();
1156 if (!v)
1157 return nullptr;
1158 v = v->GetParent(); // anonymous inner view
1159 if (!v)
1160 return nullptr;
1161 if (aExtraOffset) {
1162 *aExtraOffset += v->GetPosition();
1163 }
1164 v = v->GetParent(); // subdocumentframe's view
1165 return v ? v->GetFrame() : nullptr;
1166 }
1168 // static
1169 bool
1170 nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
1171 nsIFrame* aCommonAncestor)
1172 {
1173 if (aFrame == aAncestorFrame)
1174 return false;
1175 return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
1176 }
1178 // static
1179 bool
1180 nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
1181 const nsIFrame* aCommonAncestor)
1182 {
1183 for (const nsIFrame* f = aFrame; f != aCommonAncestor;
1184 f = GetCrossDocParentFrame(f)) {
1185 if (f == aAncestorFrame)
1186 return true;
1187 }
1188 return aCommonAncestor == aAncestorFrame;
1189 }
1191 // static
1192 bool
1193 nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
1194 nsIFrame* aCommonAncestor)
1195 {
1196 if (aFrame == aAncestorFrame)
1197 return false;
1198 for (nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) {
1199 if (f == aAncestorFrame)
1200 return true;
1201 }
1202 return aCommonAncestor == aAncestorFrame;
1203 }
1205 // static
1206 int32_t
1207 nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1,
1208 nsIContent* aContent2,
1209 int32_t aIf1Ancestor,
1210 int32_t aIf2Ancestor,
1211 const nsIContent* aCommonAncestor)
1212 {
1213 NS_PRECONDITION(aContent1, "aContent1 must not be null");
1214 NS_PRECONDITION(aContent2, "aContent2 must not be null");
1216 nsAutoTArray<nsINode*, 32> content1Ancestors;
1217 nsINode* c1;
1218 for (c1 = aContent1; c1 && c1 != aCommonAncestor; c1 = c1->GetParentNode()) {
1219 content1Ancestors.AppendElement(c1);
1220 }
1221 if (!c1 && aCommonAncestor) {
1222 // So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
1223 // Never mind. We can continue as if aCommonAncestor was null.
1224 aCommonAncestor = nullptr;
1225 }
1227 nsAutoTArray<nsINode*, 32> content2Ancestors;
1228 nsINode* c2;
1229 for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetParentNode()) {
1230 content2Ancestors.AppendElement(c2);
1231 }
1232 if (!c2 && aCommonAncestor) {
1233 // So, it turns out aCommonAncestor was not an ancestor of c2.
1234 // We need to retry with no common ancestor hint.
1235 return DoCompareTreePosition(aContent1, aContent2,
1236 aIf1Ancestor, aIf2Ancestor, nullptr);
1237 }
1239 int last1 = content1Ancestors.Length() - 1;
1240 int last2 = content2Ancestors.Length() - 1;
1241 nsINode* content1Ancestor = nullptr;
1242 nsINode* content2Ancestor = nullptr;
1243 while (last1 >= 0 && last2 >= 0
1244 && ((content1Ancestor = content1Ancestors.ElementAt(last1)) ==
1245 (content2Ancestor = content2Ancestors.ElementAt(last2)))) {
1246 last1--;
1247 last2--;
1248 }
1250 if (last1 < 0) {
1251 if (last2 < 0) {
1252 NS_ASSERTION(aContent1 == aContent2, "internal error?");
1253 return 0;
1254 }
1255 // aContent1 is an ancestor of aContent2
1256 return aIf1Ancestor;
1257 }
1259 if (last2 < 0) {
1260 // aContent2 is an ancestor of aContent1
1261 return aIf2Ancestor;
1262 }
1264 // content1Ancestor != content2Ancestor, so they must be siblings with the same parent
1265 nsINode* parent = content1Ancestor->GetParentNode();
1266 #ifdef DEBUG
1267 // TODO: remove the uglyness, see bug 598468.
1268 NS_ASSERTION(gPreventAssertInCompareTreePosition || parent,
1269 "no common ancestor at all???");
1270 #endif // DEBUG
1271 if (!parent) { // different documents??
1272 return 0;
1273 }
1275 int32_t index1 = parent->IndexOf(content1Ancestor);
1276 int32_t index2 = parent->IndexOf(content2Ancestor);
1277 if (index1 < 0 || index2 < 0) {
1278 // one of them must be anonymous; we can't determine the order
1279 return 0;
1280 }
1282 return index1 - index2;
1283 }
1285 // static
1286 nsIFrame*
1287 nsLayoutUtils::FillAncestors(nsIFrame* aFrame,
1288 nsIFrame* aStopAtAncestor,
1289 nsTArray<nsIFrame*>* aAncestors)
1290 {
1291 while (aFrame && aFrame != aStopAtAncestor) {
1292 aAncestors->AppendElement(aFrame);
1293 aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
1294 }
1295 return aFrame;
1296 }
1298 // Return true if aFrame1 is after aFrame2
1299 static bool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2)
1300 {
1301 nsIFrame* f = aFrame2;
1302 do {
1303 f = f->GetNextSibling();
1304 if (f == aFrame1)
1305 return true;
1306 } while (f);
1307 return false;
1308 }
1310 // static
1311 int32_t
1312 nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
1313 nsIFrame* aFrame2,
1314 int32_t aIf1Ancestor,
1315 int32_t aIf2Ancestor,
1316 nsIFrame* aCommonAncestor)
1317 {
1318 NS_PRECONDITION(aFrame1, "aFrame1 must not be null");
1319 NS_PRECONDITION(aFrame2, "aFrame2 must not be null");
1321 nsAutoTArray<nsIFrame*,20> frame2Ancestors;
1322 nsIFrame* nonCommonAncestor =
1323 FillAncestors(aFrame2, aCommonAncestor, &frame2Ancestors);
1325 return DoCompareTreePosition(aFrame1, aFrame2, frame2Ancestors,
1326 aIf1Ancestor, aIf2Ancestor,
1327 nonCommonAncestor ? aCommonAncestor : nullptr);
1328 }
1330 // static
1331 int32_t
1332 nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
1333 nsIFrame* aFrame2,
1334 nsTArray<nsIFrame*>& aFrame2Ancestors,
1335 int32_t aIf1Ancestor,
1336 int32_t aIf2Ancestor,
1337 nsIFrame* aCommonAncestor)
1338 {
1339 NS_PRECONDITION(aFrame1, "aFrame1 must not be null");
1340 NS_PRECONDITION(aFrame2, "aFrame2 must not be null");
1342 nsPresContext* presContext = aFrame1->PresContext();
1343 if (presContext != aFrame2->PresContext()) {
1344 NS_ERROR("no common ancestor at all, different documents");
1345 return 0;
1346 }
1348 nsAutoTArray<nsIFrame*,20> frame1Ancestors;
1349 if (aCommonAncestor &&
1350 !FillAncestors(aFrame1, aCommonAncestor, &frame1Ancestors)) {
1351 // We reached the root of the frame tree ... if aCommonAncestor was set,
1352 // it is wrong
1353 return DoCompareTreePosition(aFrame1, aFrame2,
1354 aIf1Ancestor, aIf2Ancestor, nullptr);
1355 }
1357 int32_t last1 = int32_t(frame1Ancestors.Length()) - 1;
1358 int32_t last2 = int32_t(aFrame2Ancestors.Length()) - 1;
1359 while (last1 >= 0 && last2 >= 0 &&
1360 frame1Ancestors[last1] == aFrame2Ancestors[last2]) {
1361 last1--;
1362 last2--;
1363 }
1365 if (last1 < 0) {
1366 if (last2 < 0) {
1367 NS_ASSERTION(aFrame1 == aFrame2, "internal error?");
1368 return 0;
1369 }
1370 // aFrame1 is an ancestor of aFrame2
1371 return aIf1Ancestor;
1372 }
1374 if (last2 < 0) {
1375 // aFrame2 is an ancestor of aFrame1
1376 return aIf2Ancestor;
1377 }
1379 nsIFrame* ancestor1 = frame1Ancestors[last1];
1380 nsIFrame* ancestor2 = aFrame2Ancestors[last2];
1381 // Now we should be able to walk sibling chains to find which one is first
1382 if (IsFrameAfter(ancestor2, ancestor1))
1383 return -1;
1384 if (IsFrameAfter(ancestor1, ancestor2))
1385 return 1;
1386 NS_WARNING("Frames were in different child lists???");
1387 return 0;
1388 }
1390 // static
1391 nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
1392 if (!aFrame) {
1393 return nullptr;
1394 }
1396 nsIFrame* next;
1397 while ((next = aFrame->GetNextSibling()) != nullptr) {
1398 aFrame = next;
1399 }
1400 return aFrame;
1401 }
1403 // static
1404 nsView*
1405 nsLayoutUtils::FindSiblingViewFor(nsView* aParentView, nsIFrame* aFrame) {
1406 nsIFrame* parentViewFrame = aParentView->GetFrame();
1407 nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nullptr;
1408 for (nsView* insertBefore = aParentView->GetFirstChild(); insertBefore;
1409 insertBefore = insertBefore->GetNextSibling()) {
1410 nsIFrame* f = insertBefore->GetFrame();
1411 if (!f) {
1412 // this view could be some anonymous view attached to a meaningful parent
1413 for (nsView* searchView = insertBefore->GetParent(); searchView;
1414 searchView = searchView->GetParent()) {
1415 f = searchView->GetFrame();
1416 if (f) {
1417 break;
1418 }
1419 }
1420 NS_ASSERTION(f, "Can't find a frame anywhere!");
1421 }
1422 if (!f || !aFrame->GetContent() || !f->GetContent() ||
1423 CompareTreePosition(aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) {
1424 // aFrame's content is after f's content (or we just don't know),
1425 // so put our view before f's view
1426 return insertBefore;
1427 }
1428 }
1429 return nullptr;
1430 }
1432 //static
1433 nsIScrollableFrame*
1434 nsLayoutUtils::GetScrollableFrameFor(const nsIFrame *aScrolledFrame)
1435 {
1436 nsIFrame *frame = aScrolledFrame->GetParent();
1437 nsIScrollableFrame *sf = do_QueryFrame(frame);
1438 return sf;
1439 }
1441 /* static */ void
1442 nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer,
1443 const nsIFrame* aViewportFrame,
1444 const nsRect& aAnchorRect,
1445 const nsIFrame* aFixedPosFrame,
1446 nsPresContext* aPresContext,
1447 const ContainerLayerParameters& aContainerParameters) {
1448 // Find out the rect of the viewport frame relative to the reference frame.
1449 // This, in conjunction with the container scale, will correspond to the
1450 // coordinate-space of the built layer.
1451 float factor = aPresContext->AppUnitsPerDevPixel();
1452 Rect anchorRect(NSAppUnitsToFloatPixels(aAnchorRect.x, factor) *
1453 aContainerParameters.mXScale,
1454 NSAppUnitsToFloatPixels(aAnchorRect.y, factor) *
1455 aContainerParameters.mYScale,
1456 NSAppUnitsToFloatPixels(aAnchorRect.width, factor) *
1457 aContainerParameters.mXScale,
1458 NSAppUnitsToFloatPixels(aAnchorRect.height, factor) *
1459 aContainerParameters.mYScale);
1460 // Need to transform anchorRect from the container layer's coordinate system
1461 // into aLayer's coordinate system.
1462 Matrix transform2d;
1463 if (aLayer->GetTransform().Is2D(&transform2d)) {
1464 transform2d.Invert();
1465 anchorRect = transform2d.TransformBounds(anchorRect);
1466 } else {
1467 NS_ERROR("3D transform found between fixedpos content and its viewport (should never happen)");
1468 anchorRect = Rect(0,0,0,0);
1469 }
1471 // Work out the anchor point for this fixed position layer. We assume that
1472 // any positioning set (left/top/right/bottom) indicates that the
1473 // corresponding side of its container should be the anchor point,
1474 // defaulting to top-left.
1475 LayerPoint anchor(anchorRect.x, anchorRect.y);
1476 // Make sure the layer is aware of any fixed position margins that have
1477 // been set.
1478 nsMargin fixedMargins = aPresContext->PresShell()->GetContentDocumentFixedPositionMargins();
1479 LayerMargin fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins.top, factor) *
1480 aContainerParameters.mYScale,
1481 NSAppUnitsToFloatPixels(fixedMargins.right, factor) *
1482 aContainerParameters.mXScale,
1483 NSAppUnitsToFloatPixels(fixedMargins.bottom, factor) *
1484 aContainerParameters.mYScale,
1485 NSAppUnitsToFloatPixels(fixedMargins.left, factor) *
1486 aContainerParameters.mXScale);
1488 if (aFixedPosFrame != aViewportFrame) {
1489 const nsStylePosition* position = aFixedPosFrame->StylePosition();
1490 if (position->mOffset.GetRightUnit() != eStyleUnit_Auto) {
1491 if (position->mOffset.GetLeftUnit() != eStyleUnit_Auto) {
1492 anchor.x = anchorRect.x + anchorRect.width / 2.f;
1493 } else {
1494 anchor.x = anchorRect.XMost();
1495 }
1496 }
1497 if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto) {
1498 if (position->mOffset.GetTopUnit() != eStyleUnit_Auto) {
1499 anchor.y = anchorRect.y + anchorRect.height / 2.f;
1500 } else {
1501 anchor.y = anchorRect.YMost();
1502 }
1503 }
1505 // If the frame is auto-positioned on either axis, set the top/left layer
1506 // margins to -1, to indicate to the compositor that this layer is
1507 // unaffected by fixed margins.
1508 if (position->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
1509 position->mOffset.GetRightUnit() == eStyleUnit_Auto) {
1510 fixedLayerMargins.left = -1;
1511 }
1512 if (position->mOffset.GetTopUnit() == eStyleUnit_Auto &&
1513 position->mOffset.GetBottomUnit() == eStyleUnit_Auto) {
1514 fixedLayerMargins.top = -1;
1515 }
1516 }
1518 aLayer->SetFixedPositionAnchor(anchor);
1519 aLayer->SetFixedPositionMargins(fixedLayerMargins);
1520 }
1522 bool
1523 nsLayoutUtils::ViewportHasDisplayPort(nsPresContext* aPresContext, nsRect* aDisplayPort)
1524 {
1525 nsIFrame* rootScrollFrame =
1526 aPresContext->PresShell()->GetRootScrollFrame();
1527 return rootScrollFrame &&
1528 nsLayoutUtils::GetDisplayPort(rootScrollFrame->GetContent(), aDisplayPort);
1529 }
1531 bool
1532 nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame, nsRect* aDisplayPort)
1533 {
1534 // Fixed-pos frames are parented by the viewport frame or the page content frame.
1535 // We'll assume that printing/print preview don't have displayports for their
1536 // pages!
1537 nsIFrame* parent = aFrame->GetParent();
1538 if (!parent || parent->GetParent() ||
1539 aFrame->StyleDisplay()->mPosition != NS_STYLE_POSITION_FIXED) {
1540 return false;
1541 }
1542 return ViewportHasDisplayPort(aFrame->PresContext(), aDisplayPort);
1543 }
1545 static nsIFrame*
1546 GetAnimatedGeometryRootForFrame(nsIFrame* aFrame,
1547 const nsIFrame* aStopAtAncestor)
1548 {
1549 nsIFrame* f = aFrame;
1550 nsIFrame* stickyFrame = nullptr;
1551 while (f != aStopAtAncestor) {
1552 if (nsLayoutUtils::IsPopup(f))
1553 break;
1554 if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(f))
1555 break;
1556 if (!f->GetParent() &&
1557 nsLayoutUtils::ViewportHasDisplayPort(f->PresContext())) {
1558 // Viewport frames in a display port need to be animated geometry roots
1559 // for background-attachment:fixed elements.
1560 break;
1561 }
1562 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(f);
1563 if (!parent)
1564 break;
1565 nsIAtom* parentType = parent->GetType();
1566 #ifdef ANDROID
1567 // Treat the slider thumb as being as an active scrolled root
1568 // on mobile so that it can move without repainting.
1569 if (parentType == nsGkAtoms::sliderFrame)
1570 break;
1571 #endif
1572 // Sticky frames are active if their nearest scrollable frame
1573 // is also active, just keep a record of sticky frames that we
1574 // encounter for now.
1575 if (f->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY &&
1576 !stickyFrame) {
1577 stickyFrame = f;
1578 }
1579 if (parentType == nsGkAtoms::scrollFrame) {
1580 nsIScrollableFrame* sf = do_QueryFrame(parent);
1581 if (sf->IsScrollingActive() && sf->GetScrolledFrame() == f) {
1582 // If we found a sticky frame inside this active scroll frame,
1583 // then use that. Otherwise use the scroll frame.
1584 if (stickyFrame) {
1585 return stickyFrame;
1586 }
1587 return f;
1588 } else {
1589 stickyFrame = nullptr;
1590 }
1591 }
1592 // Fixed-pos frames are parented by the viewport frame, which has no parent
1593 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) {
1594 return f;
1595 }
1596 f = parent;
1597 }
1598 return f;
1599 }
1601 nsIFrame*
1602 nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem,
1603 nsDisplayListBuilder* aBuilder)
1604 {
1605 nsIFrame* f = aItem->Frame();
1606 if (aItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER) {
1607 nsDisplayScrollLayer* scrollLayerItem =
1608 static_cast<nsDisplayScrollLayer*>(aItem);
1609 nsIFrame* scrolledFrame = scrollLayerItem->GetScrolledFrame();
1610 return GetAnimatedGeometryRootForFrame(scrolledFrame,
1611 aBuilder->FindReferenceFrameFor(scrolledFrame));
1612 }
1613 if (aItem->ShouldFixToViewport(aBuilder)) {
1614 // Make its active scrolled root be the active scrolled root of
1615 // the enclosing viewport, since it shouldn't be scrolled by scrolled
1616 // frames in its document. InvalidateFixedBackgroundFramesFromList in
1617 // nsGfxScrollFrame will not repaint this item when scrolling occurs.
1618 nsIFrame* viewportFrame =
1619 nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame);
1620 NS_ASSERTION(viewportFrame, "no viewport???");
1621 return GetAnimatedGeometryRootForFrame(viewportFrame,
1622 aBuilder->FindReferenceFrameFor(viewportFrame));
1623 }
1624 return GetAnimatedGeometryRootForFrame(f, aItem->ReferenceFrame());
1625 }
1627 // static
1628 nsIScrollableFrame*
1629 nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,
1630 Direction aDirection)
1631 {
1632 NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
1633 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
1634 nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
1635 if (scrollableFrame) {
1636 ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles();
1637 uint32_t directions = scrollableFrame->GetPerceivedScrollingDirections();
1638 if (aDirection == eVertical ?
1639 (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN &&
1640 (directions & nsIScrollableFrame::VERTICAL)) :
1641 (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
1642 (directions & nsIScrollableFrame::HORIZONTAL)))
1643 return scrollableFrame;
1644 }
1645 }
1646 return nullptr;
1647 }
1649 // static
1650 nsIScrollableFrame*
1651 nsLayoutUtils::GetNearestScrollableFrame(nsIFrame* aFrame, uint32_t aFlags)
1652 {
1653 NS_ASSERTION(aFrame, "GetNearestScrollableFrame expects a non-null frame");
1654 for (nsIFrame* f = aFrame; f; f = (aFlags & SCROLLABLE_SAME_DOC) ?
1655 f->GetParent() : nsLayoutUtils::GetCrossDocParentFrame(f)) {
1656 nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
1657 if (scrollableFrame) {
1658 ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles();
1659 if ((aFlags & SCROLLABLE_INCLUDE_HIDDEN) ||
1660 ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN ||
1661 ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN)
1662 return scrollableFrame;
1663 }
1664 }
1665 return nullptr;
1666 }
1668 // static
1669 nsRect
1670 nsLayoutUtils::GetScrolledRect(nsIFrame* aScrolledFrame,
1671 const nsRect& aScrolledFrameOverflowArea,
1672 const nsSize& aScrollPortSize,
1673 uint8_t aDirection)
1674 {
1675 nscoord x1 = aScrolledFrameOverflowArea.x,
1676 x2 = aScrolledFrameOverflowArea.XMost(),
1677 y1 = aScrolledFrameOverflowArea.y,
1678 y2 = aScrolledFrameOverflowArea.YMost();
1679 if (y1 < 0) {
1680 y1 = 0;
1681 }
1682 if (aDirection != NS_STYLE_DIRECTION_RTL) {
1683 if (x1 < 0) {
1684 x1 = 0;
1685 }
1686 } else {
1687 if (x2 > aScrollPortSize.width) {
1688 x2 = aScrollPortSize.width;
1689 }
1690 // When the scrolled frame chooses a size larger than its available width (because
1691 // its padding alone is larger than the available width), we need to keep the
1692 // start-edge of the scroll frame anchored to the start-edge of the scrollport.
1693 // When the scrolled frame is RTL, this means moving it in our left-based
1694 // coordinate system, so we need to compensate for its extra width here by
1695 // effectively repositioning the frame.
1696 nscoord extraWidth = std::max(0, aScrolledFrame->GetSize().width - aScrollPortSize.width);
1697 x2 += extraWidth;
1698 }
1699 return nsRect(x1, y1, x2 - x1, y2 - y1);
1700 }
1702 //static
1703 bool
1704 nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
1705 nsStyleContext* aStyleContext,
1706 nsCSSPseudoElements::Type aPseudoElement,
1707 nsPresContext* aPresContext)
1708 {
1709 NS_PRECONDITION(aPresContext, "Must have a prescontext");
1711 nsRefPtr<nsStyleContext> pseudoContext;
1712 if (aContent) {
1713 pseudoContext = aPresContext->StyleSet()->
1714 ProbePseudoElementStyle(aContent->AsElement(), aPseudoElement,
1715 aStyleContext);
1716 }
1717 return pseudoContext != nullptr;
1718 }
1720 nsPoint
1721 nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame)
1722 {
1723 if (!aDOMEvent)
1724 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1725 WidgetEvent* event = aDOMEvent->GetInternalNSEvent();
1726 if (!event)
1727 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1728 return GetEventCoordinatesRelativeTo(event, aFrame);
1729 }
1731 nsPoint
1732 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
1733 nsIFrame* aFrame)
1734 {
1735 if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT &&
1736 aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
1737 aEvent->eventStructType != NS_WHEEL_EVENT &&
1738 aEvent->eventStructType != NS_DRAG_EVENT &&
1739 aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT &&
1740 aEvent->eventStructType != NS_POINTER_EVENT &&
1741 aEvent->eventStructType != NS_GESTURENOTIFY_EVENT &&
1742 aEvent->eventStructType != NS_TOUCH_EVENT &&
1743 aEvent->eventStructType != NS_QUERY_CONTENT_EVENT))
1744 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1746 return GetEventCoordinatesRelativeTo(aEvent,
1747 LayoutDeviceIntPoint::ToUntyped(aEvent->AsGUIEvent()->refPoint),
1748 aFrame);
1749 }
1751 nsPoint
1752 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
1753 const nsIntPoint aPoint,
1754 nsIFrame* aFrame)
1755 {
1756 if (!aFrame) {
1757 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1758 }
1760 nsIWidget* widget = aEvent->AsGUIEvent()->widget;
1761 if (!widget) {
1762 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1763 }
1765 return GetEventCoordinatesRelativeTo(widget, aPoint, aFrame);
1766 }
1768 nsPoint
1769 nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget,
1770 const nsIntPoint aPoint,
1771 nsIFrame* aFrame)
1772 {
1773 if (!aFrame || !aWidget) {
1774 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1775 }
1777 nsView* view = aFrame->GetView();
1778 if (view) {
1779 nsIWidget* frameWidget = view->GetWidget();
1780 if (frameWidget && frameWidget == aWidget) {
1781 // Special case this cause it happens a lot.
1782 // This also fixes bug 664707, events in the extra-special case of select
1783 // dropdown popups that are transformed.
1784 nsPresContext* presContext = aFrame->PresContext();
1785 nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x),
1786 presContext->DevPixelsToAppUnits(aPoint.y));
1787 return pt - view->ViewToWidgetOffset();
1788 }
1789 }
1791 /* If we walk up the frame tree and discover that any of the frames are
1792 * transformed, we need to do extra work to convert from the global
1793 * space to the local space.
1794 */
1795 nsIFrame* rootFrame = aFrame;
1796 bool transformFound = false;
1797 for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) {
1798 if (f->IsTransformed()) {
1799 transformFound = true;
1800 }
1802 rootFrame = f;
1803 }
1805 nsView* rootView = rootFrame->GetView();
1806 if (!rootView) {
1807 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1808 }
1810 nsPoint widgetToView = TranslateWidgetToView(rootFrame->PresContext(),
1811 aWidget, aPoint, rootView);
1813 if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
1814 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
1815 }
1817 // Convert from root document app units to app units of the document aFrame
1818 // is in.
1819 int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
1820 int32_t localAPD = aFrame->PresContext()->AppUnitsPerDevPixel();
1821 widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD);
1823 /* If we encountered a transform, we can't do simple arithmetic to figure
1824 * out how to convert back to aFrame's coordinates and must use the CTM.
1825 */
1826 if (transformFound || aFrame->IsSVGText()) {
1827 return TransformRootPointToFrame(aFrame, widgetToView);
1828 }
1830 /* Otherwise, all coordinate systems are translations of one another,
1831 * so we can just subtract out the difference.
1832 */
1833 return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame);
1834 }
1836 nsIFrame*
1837 nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
1838 const WidgetEvent* aEvent)
1839 {
1840 #ifdef MOZ_XUL
1841 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1842 if (!pm) {
1843 return nullptr;
1844 }
1845 nsTArray<nsIFrame*> popups;
1846 pm->GetVisiblePopups(popups);
1847 uint32_t i;
1848 // Search from top to bottom
1849 for (i = 0; i < popups.Length(); i++) {
1850 nsIFrame* popup = popups[i];
1851 if (popup->PresContext()->GetRootPresContext() == aPresContext &&
1852 popup->GetScrollableOverflowRect().Contains(
1853 GetEventCoordinatesRelativeTo(aEvent, popup))) {
1854 return popup;
1855 }
1856 }
1857 #endif
1858 return nullptr;
1859 }
1861 gfx3DMatrix
1862 nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin,
1863 const gfx3DMatrix &aMatrix)
1864 {
1865 gfx3DMatrix result = aMatrix;
1867 /* Translate to the origin before aMatrix */
1868 result.Translate(-aOrigin);
1870 /* Translate back into position after aMatrix */
1871 result.TranslatePost(aOrigin);
1873 return result;
1874 }
1876 static void ConstrainToCoordValues(float& aStart, float& aSize)
1877 {
1878 MOZ_ASSERT(aSize >= 0);
1880 // Here we try to make sure that the resulting nsRect will continue to cover
1881 // as much of the area that was covered by the original gfx Rect as possible.
1883 // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since
1884 // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this
1885 // range:
1886 float end = aStart + aSize;
1887 aStart = clamped(aStart, float(nscoord_MIN), float(nscoord_MAX));
1888 end = clamped(end, float(nscoord_MIN), float(nscoord_MAX));
1890 aSize = end - aStart;
1892 // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()
1893 // can't return a value greater than nscoord_MAX. If aSize is greater than
1894 // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect
1895 // centered:
1896 if (aSize > nscoord_MAX) {
1897 float excess = aSize - nscoord_MAX;
1898 excess /= 2;
1899 aStart += excess;
1900 aSize = nscoord_MAX;
1901 }
1902 }
1904 /**
1905 * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX.
1906 *
1907 * @param aVal The value to constrain (in/out)
1908 */
1909 static void ConstrainToCoordValues(gfxFloat& aVal)
1910 {
1911 if (aVal <= nscoord_MIN)
1912 aVal = nscoord_MIN;
1913 else if (aVal >= nscoord_MAX)
1914 aVal = nscoord_MAX;
1915 }
1917 static void ConstrainToCoordValues(gfxFloat& aStart, gfxFloat& aSize)
1918 {
1919 gfxFloat max = aStart + aSize;
1921 // Clamp the end points to within nscoord range
1922 ConstrainToCoordValues(aStart);
1923 ConstrainToCoordValues(max);
1925 aSize = max - aStart;
1926 // If the width if still greater than the max nscoord, then bring both
1927 // endpoints in by the same amount until it fits.
1928 if (aSize > nscoord_MAX) {
1929 gfxFloat excess = aSize - nscoord_MAX;
1930 excess /= 2;
1932 aStart += excess;
1933 aSize = nscoord_MAX;
1934 } else if (aSize < nscoord_MIN) {
1935 gfxFloat excess = aSize - nscoord_MIN;
1936 excess /= 2;
1938 aStart -= excess;
1939 aSize = nscoord_MIN;
1940 }
1941 }
1943 nsRect
1944 nsLayoutUtils::RoundGfxRectToAppRect(const Rect &aRect, float aFactor)
1945 {
1946 /* Get a new Rect whose units are app units by scaling by the specified factor. */
1947 Rect scaledRect = aRect;
1948 scaledRect.ScaleRoundOut(aFactor);
1950 /* We now need to constrain our results to the max and min values for coords. */
1951 ConstrainToCoordValues(scaledRect.x, scaledRect.width);
1952 ConstrainToCoordValues(scaledRect.y, scaledRect.height);
1954 /* Now typecast everything back. This is guaranteed to be safe. */
1955 return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()),
1956 nscoord(scaledRect.Width()), nscoord(scaledRect.Height()));
1957 }
1959 nsRect
1960 nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor)
1961 {
1962 /* Get a new gfxRect whose units are app units by scaling by the specified factor. */
1963 gfxRect scaledRect = aRect;
1964 scaledRect.ScaleRoundOut(aFactor);
1966 /* We now need to constrain our results to the max and min values for coords. */
1967 ConstrainToCoordValues(scaledRect.x, scaledRect.width);
1968 ConstrainToCoordValues(scaledRect.y, scaledRect.height);
1970 /* Now typecast everything back. This is guaranteed to be safe. */
1971 return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()),
1972 nscoord(scaledRect.Width()), nscoord(scaledRect.Height()));
1973 }
1976 nsRegion
1977 nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect,
1978 const nscoord aRadii[8],
1979 const nsRect& aContainedRect)
1980 {
1981 // rectFullHeight and rectFullWidth together will approximately contain
1982 // the total area of the frame minus the rounded corners.
1983 nsRect rectFullHeight = aRoundedRect;
1984 nscoord xDiff = std::max(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]);
1985 rectFullHeight.x += xDiff;
1986 rectFullHeight.width -= std::max(aRadii[NS_CORNER_TOP_RIGHT_X],
1987 aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff;
1988 nsRect r1;
1989 r1.IntersectRect(rectFullHeight, aContainedRect);
1991 nsRect rectFullWidth = aRoundedRect;
1992 nscoord yDiff = std::max(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]);
1993 rectFullWidth.y += yDiff;
1994 rectFullWidth.height -= std::max(aRadii[NS_CORNER_BOTTOM_LEFT_Y],
1995 aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff;
1996 nsRect r2;
1997 r2.IntersectRect(rectFullWidth, aContainedRect);
1999 nsRegion result;
2000 result.Or(r1, r2);
2001 return result;
2002 }
2004 // Helper for RoundedRectIntersectsRect.
2005 static bool
2006 CheckCorner(nscoord aXOffset, nscoord aYOffset,
2007 nscoord aXRadius, nscoord aYRadius)
2008 {
2009 NS_ABORT_IF_FALSE(aXOffset > 0 && aYOffset > 0,
2010 "must not pass nonpositives to CheckCorner");
2011 NS_ABORT_IF_FALSE(aXRadius >= 0 && aYRadius >= 0,
2012 "must not pass negatives to CheckCorner");
2014 // Avoid floating point math unless we're either (1) within the
2015 // quarter-ellipse area at the rounded corner or (2) outside the
2016 // rounding.
2017 if (aXOffset >= aXRadius || aYOffset >= aYRadius)
2018 return true;
2020 // Convert coordinates to a unit circle with (0,0) as the center of
2021 // curvature, and see if we're inside the circle or outside.
2022 float scaledX = float(aXRadius - aXOffset) / float(aXRadius);
2023 float scaledY = float(aYRadius - aYOffset) / float(aYRadius);
2024 return scaledX * scaledX + scaledY * scaledY < 1.0f;
2025 }
2027 bool
2028 nsLayoutUtils::RoundedRectIntersectsRect(const nsRect& aRoundedRect,
2029 const nscoord aRadii[8],
2030 const nsRect& aTestRect)
2031 {
2032 if (!aTestRect.Intersects(aRoundedRect))
2033 return false;
2035 // distances from this edge of aRoundedRect to opposite edge of aTestRect,
2036 // which we know are positive due to the Intersects check above.
2037 nsMargin insets;
2038 insets.top = aTestRect.YMost() - aRoundedRect.y;
2039 insets.right = aRoundedRect.XMost() - aTestRect.x;
2040 insets.bottom = aRoundedRect.YMost() - aTestRect.y;
2041 insets.left = aTestRect.XMost() - aRoundedRect.x;
2043 // Check whether the bottom-right corner of aTestRect is inside the
2044 // top left corner of aBounds when rounded by aRadii, etc. If any
2045 // corner is not, then fail; otherwise succeed.
2046 return CheckCorner(insets.left, insets.top,
2047 aRadii[NS_CORNER_TOP_LEFT_X],
2048 aRadii[NS_CORNER_TOP_LEFT_Y]) &&
2049 CheckCorner(insets.right, insets.top,
2050 aRadii[NS_CORNER_TOP_RIGHT_X],
2051 aRadii[NS_CORNER_TOP_RIGHT_Y]) &&
2052 CheckCorner(insets.right, insets.bottom,
2053 aRadii[NS_CORNER_BOTTOM_RIGHT_X],
2054 aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) &&
2055 CheckCorner(insets.left, insets.bottom,
2056 aRadii[NS_CORNER_BOTTOM_LEFT_X],
2057 aRadii[NS_CORNER_BOTTOM_LEFT_Y]);
2058 }
2060 nsRect
2061 nsLayoutUtils::MatrixTransformRectOut(const nsRect &aBounds,
2062 const gfx3DMatrix &aMatrix, float aFactor)
2063 {
2064 nsRect outside = aBounds;
2065 outside.ScaleRoundOut(1/aFactor);
2066 gfxRect image = aMatrix.TransformBounds(gfxRect(outside.x,
2067 outside.y,
2068 outside.width,
2069 outside.height));
2070 return RoundGfxRectToAppRect(image, aFactor);
2071 }
2073 nsRect
2074 nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds,
2075 const gfx3DMatrix &aMatrix, float aFactor)
2076 {
2077 gfxRect image = aMatrix.TransformBounds(gfxRect(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
2078 NSAppUnitsToDoublePixels(aBounds.y, aFactor),
2079 NSAppUnitsToDoublePixels(aBounds.width, aFactor),
2080 NSAppUnitsToDoublePixels(aBounds.height, aFactor)));
2082 return RoundGfxRectToAppRect(image, aFactor);
2083 }
2085 nsPoint
2086 nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint,
2087 const gfx3DMatrix &aMatrix, float aFactor)
2088 {
2089 gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
2090 NSAppUnitsToFloatPixels(aPoint.y, aFactor)));
2091 return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
2092 NSFloatPixelsToAppUnits(float(image.y), aFactor));
2093 }
2095 gfx3DMatrix
2096 nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame, const nsIFrame *aAncestor)
2097 {
2098 nsIFrame* parent;
2099 gfx3DMatrix ctm;
2100 if (aFrame == aAncestor) {
2101 return ctm;
2102 }
2103 ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
2104 while (parent && parent != aAncestor) {
2105 if (!parent->Preserves3DChildren()) {
2106 ctm.ProjectTo2D();
2107 }
2108 ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
2109 }
2110 return ctm;
2111 }
2113 static nsIFrame*
2114 FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2)
2115 {
2116 nsAutoTArray<nsIFrame*,100> ancestors1;
2117 nsAutoTArray<nsIFrame*,100> ancestors2;
2118 nsIFrame* commonAncestor = nullptr;
2119 if (aFrame1->PresContext() == aFrame2->PresContext()) {
2120 commonAncestor = aFrame1->PresContext()->PresShell()->GetRootFrame();
2121 }
2122 for (nsIFrame* f = aFrame1; f != commonAncestor;
2123 f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
2124 ancestors1.AppendElement(f);
2125 }
2126 for (nsIFrame* f = aFrame2; f != commonAncestor;
2127 f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
2128 ancestors2.AppendElement(f);
2129 }
2130 uint32_t minLengths = std::min(ancestors1.Length(), ancestors2.Length());
2131 for (uint32_t i = 1; i <= minLengths; ++i) {
2132 if (ancestors1[ancestors1.Length() - i] == ancestors2[ancestors2.Length() - i]) {
2133 commonAncestor = ancestors1[ancestors1.Length() - i];
2134 } else {
2135 break;
2136 }
2137 }
2138 return commonAncestor;
2139 }
2141 nsLayoutUtils::TransformResult
2142 nsLayoutUtils::TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame,
2143 uint32_t aPointCount, CSSPoint* aPoints)
2144 {
2145 nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
2146 if (!nearestCommonAncestor) {
2147 return NO_COMMON_ANCESTOR;
2148 }
2149 gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
2150 if (downToDest.IsSingular()) {
2151 return NONINVERTIBLE_TRANSFORM;
2152 }
2153 downToDest.Invert();
2154 gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor);
2155 CSSToLayoutDeviceScale devPixelsPerCSSPixelFromFrame(
2156 double(nsPresContext::AppUnitsPerCSSPixel())/
2157 aFromFrame->PresContext()->AppUnitsPerDevPixel());
2158 CSSToLayoutDeviceScale devPixelsPerCSSPixelToFrame(
2159 double(nsPresContext::AppUnitsPerCSSPixel())/
2160 aToFrame->PresContext()->AppUnitsPerDevPixel());
2161 for (uint32_t i = 0; i < aPointCount; ++i) {
2162 LayoutDevicePoint devPixels = aPoints[i] * devPixelsPerCSSPixelFromFrame;
2163 gfxPoint toDevPixels = downToDest.ProjectPoint(
2164 upToAncestor.ProjectPoint(gfxPoint(devPixels.x, devPixels.y)));
2165 // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct
2166 // answer instead of some inaccuracy multiplying a number by its reciprocal.
2167 aPoints[i] = LayoutDevicePoint(toDevPixels.x, toDevPixels.y) /
2168 devPixelsPerCSSPixelToFrame;
2169 }
2170 return TRANSFORM_SUCCEEDED;
2171 }
2173 bool
2174 nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
2175 gfx3DMatrix* aTransform)
2176 {
2177 // FIXME/bug 796690: we can sometimes compute a transform in these
2178 // cases, it just increases complexity considerably. Punt for now.
2179 if (aFrame->Preserves3DChildren() || aFrame->HasTransformGetter()) {
2180 return false;
2181 }
2183 nsIFrame* root = nsLayoutUtils::GetDisplayRootFrame(aFrame);
2184 if (root->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)) {
2185 // Content may have been invalidated, so we can't reliably compute
2186 // the "layer transform" in general.
2187 return false;
2188 }
2189 // If the caller doesn't care about the value, early-return to skip
2190 // overhead below.
2191 if (!aTransform) {
2192 return true;
2193 }
2195 nsDisplayListBuilder builder(root, nsDisplayListBuilder::OTHER,
2196 false/*don't build caret*/);
2197 nsDisplayList list;
2198 nsDisplayTransform* item =
2199 new (&builder) nsDisplayTransform(&builder, aFrame, &list);
2201 *aTransform =
2202 item->GetTransform();
2203 item->~nsDisplayTransform();
2205 return true;
2206 }
2208 static bool
2209 TransformGfxPointFromAncestor(nsIFrame *aFrame,
2210 const gfxPoint &aPoint,
2211 nsIFrame *aAncestor,
2212 gfxPoint* aOut)
2213 {
2214 gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
2216 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
2217 nsRect childBounds = aFrame->GetVisualOverflowRectRelativeToSelf();
2218 gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor),
2219 NSAppUnitsToFloatPixels(childBounds.y, factor),
2220 NSAppUnitsToFloatPixels(childBounds.width, factor),
2221 NSAppUnitsToFloatPixels(childBounds.height, factor));
2222 return ctm.UntransformPoint(aPoint, childGfxBounds, aOut);
2223 }
2225 static gfxRect
2226 TransformGfxRectToAncestor(nsIFrame *aFrame,
2227 const gfxRect &aRect,
2228 const nsIFrame *aAncestor,
2229 bool* aPreservesAxisAlignedRectangles = nullptr)
2230 {
2231 gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
2232 if (aPreservesAxisAlignedRectangles) {
2233 gfxMatrix matrix2d;
2234 *aPreservesAxisAlignedRectangles =
2235 ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles();
2236 }
2237 return ctm.TransformBounds(aRect);
2238 }
2240 static SVGTextFrame*
2241 GetContainingSVGTextFrame(nsIFrame* aFrame)
2242 {
2243 if (!aFrame->IsSVGText()) {
2244 return nullptr;
2245 }
2247 return static_cast<SVGTextFrame*>
2248 (nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(),
2249 nsGkAtoms::svgTextFrame));
2250 }
2252 nsPoint
2253 nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame,
2254 const nsPoint& aPoint,
2255 nsIFrame* aAncestor)
2256 {
2257 SVGTextFrame* text = GetContainingSVGTextFrame(aFrame);
2259 float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
2260 gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
2261 NSAppUnitsToFloatPixels(aPoint.y, factor));
2263 if (text) {
2264 if (!TransformGfxPointFromAncestor(text, result, aAncestor, &result)) {
2265 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2266 }
2267 result = text->TransformFramePointToTextChild(result, aFrame);
2268 } else {
2269 if (!TransformGfxPointFromAncestor(aFrame, result, nullptr, &result)) {
2270 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2271 }
2272 }
2274 return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
2275 NSFloatPixelsToAppUnits(float(result.y), factor));
2276 }
2278 nsRect
2279 nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
2280 const nsRect& aRect,
2281 const nsIFrame* aAncestor,
2282 bool* aPreservesAxisAlignedRectangles /* = nullptr */)
2283 {
2284 SVGTextFrame* text = GetContainingSVGTextFrame(aFrame);
2286 float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2287 gfxRect result;
2289 if (text) {
2290 result = text->TransformFrameRectFromTextChild(aRect, aFrame);
2291 result = TransformGfxRectToAncestor(text, result, aAncestor);
2292 // TransformFrameRectFromTextChild could involve any kind of transform, we
2293 // could drill down into it to get an answer out of it but we don't yet.
2294 if (aPreservesAxisAlignedRectangles)
2295 *aPreservesAxisAlignedRectangles = false;
2296 } else {
2297 result = gfxRect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
2298 NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
2299 NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
2300 NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
2301 result = TransformGfxRectToAncestor(aFrame, result, aAncestor, aPreservesAxisAlignedRectangles);
2302 }
2304 float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
2305 return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),
2306 NSFloatPixelsToAppUnits(float(result.y), destAppUnitsPerDevPixel),
2307 NSFloatPixelsToAppUnits(float(result.width), destAppUnitsPerDevPixel),
2308 NSFloatPixelsToAppUnits(float(result.height), destAppUnitsPerDevPixel));
2309 }
2311 static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
2312 nsIntPoint offset(0, 0);
2313 nsIWidget* parent = aWidget->GetParent();
2314 while (parent) {
2315 nsIntRect bounds;
2316 aWidget->GetBounds(bounds);
2317 offset += bounds.TopLeft();
2318 aWidget = parent;
2319 parent = aWidget->GetParent();
2320 }
2321 aRootWidget = aWidget;
2322 return offset;
2323 }
2325 nsPoint
2326 nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext,
2327 nsIWidget* aWidget, nsIntPoint aPt,
2328 nsView* aView)
2329 {
2330 nsPoint viewOffset;
2331 nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset);
2332 if (!viewWidget) {
2333 return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
2334 }
2336 nsIWidget* fromRoot;
2337 nsIntPoint fromOffset = GetWidgetOffset(aWidget, fromRoot);
2338 nsIWidget* toRoot;
2339 nsIntPoint toOffset = GetWidgetOffset(viewWidget, toRoot);
2341 nsIntPoint widgetPoint;
2342 if (fromRoot == toRoot) {
2343 widgetPoint = aPt + fromOffset - toOffset;
2344 } else {
2345 nsIntPoint screenPoint = aWidget->WidgetToScreenOffset();
2346 widgetPoint = aPt + screenPoint - viewWidget->WidgetToScreenOffset();
2347 }
2349 nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x),
2350 aPresContext->DevPixelsToAppUnits(widgetPoint.y));
2351 return widgetAppUnits - viewOffset;
2352 }
2354 // Combine aNewBreakType with aOrigBreakType, but limit the break types
2355 // to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH.
2356 uint8_t
2357 nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType,
2358 uint8_t aNewBreakType)
2359 {
2360 uint8_t breakType = aOrigBreakType;
2361 switch(breakType) {
2362 case NS_STYLE_CLEAR_LEFT:
2363 if (NS_STYLE_CLEAR_RIGHT == aNewBreakType ||
2364 NS_STYLE_CLEAR_BOTH == aNewBreakType) {
2365 breakType = NS_STYLE_CLEAR_BOTH;
2366 }
2367 break;
2368 case NS_STYLE_CLEAR_RIGHT:
2369 if (NS_STYLE_CLEAR_LEFT == aNewBreakType ||
2370 NS_STYLE_CLEAR_BOTH == aNewBreakType) {
2371 breakType = NS_STYLE_CLEAR_BOTH;
2372 }
2373 break;
2374 case NS_STYLE_CLEAR_NONE:
2375 if (NS_STYLE_CLEAR_LEFT == aNewBreakType ||
2376 NS_STYLE_CLEAR_RIGHT == aNewBreakType ||
2377 NS_STYLE_CLEAR_BOTH == aNewBreakType) {
2378 breakType = aNewBreakType;
2379 }
2380 }
2381 return breakType;
2382 }
2384 #ifdef MOZ_DUMP_PAINTING
2385 #include <stdio.h>
2387 static bool gDumpEventList = false;
2388 int gPaintCount = 0;
2389 #endif
2391 nsresult
2392 nsLayoutUtils::GetRemoteContentIds(nsIFrame* aFrame,
2393 const nsRect& aTarget,
2394 nsTArray<ViewID> &aOutIDs,
2395 bool aIgnoreRootScrollFrame)
2396 {
2397 nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY,
2398 false);
2399 nsDisplayList list;
2401 if (aIgnoreRootScrollFrame) {
2402 nsIFrame* rootScrollFrame =
2403 aFrame->PresContext()->PresShell()->GetRootScrollFrame();
2404 if (rootScrollFrame) {
2405 builder.SetIgnoreScrollFrame(rootScrollFrame);
2406 }
2407 }
2409 builder.EnterPresShell(aFrame, aTarget);
2410 aFrame->BuildDisplayListForStackingContext(&builder, aTarget, &list);
2411 builder.LeavePresShell(aFrame, aTarget);
2413 nsAutoTArray<nsIFrame*,8> outFrames;
2414 nsDisplayItem::HitTestState hitTestState(&aOutIDs);
2415 list.HitTest(&builder, aTarget, &hitTestState, &outFrames);
2416 list.DeleteAll();
2418 return NS_OK;
2419 }
2421 nsIFrame*
2422 nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt, uint32_t aFlags)
2423 {
2424 PROFILER_LABEL("nsLayoutUtils", "GetFrameForPoint");
2425 nsresult rv;
2426 nsAutoTArray<nsIFrame*,8> outFrames;
2427 rv = GetFramesForArea(aFrame, nsRect(aPt, nsSize(1, 1)), outFrames, aFlags);
2428 NS_ENSURE_SUCCESS(rv, nullptr);
2429 return outFrames.Length() ? outFrames.ElementAt(0) : nullptr;
2430 }
2432 nsresult
2433 nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
2434 nsTArray<nsIFrame*> &aOutFrames,
2435 uint32_t aFlags)
2436 {
2437 PROFILER_LABEL("nsLayoutUtils","GetFramesForArea");
2438 nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY,
2439 false);
2440 nsDisplayList list;
2441 nsRect target(aRect);
2443 if (aFlags & IGNORE_PAINT_SUPPRESSION) {
2444 builder.IgnorePaintSuppression();
2445 }
2447 if (aFlags & IGNORE_ROOT_SCROLL_FRAME) {
2448 nsIFrame* rootScrollFrame =
2449 aFrame->PresContext()->PresShell()->GetRootScrollFrame();
2450 if (rootScrollFrame) {
2451 builder.SetIgnoreScrollFrame(rootScrollFrame);
2452 }
2453 }
2454 if (aFlags & IGNORE_CROSS_DOC) {
2455 builder.SetDescendIntoSubdocuments(false);
2456 }
2458 builder.EnterPresShell(aFrame, target);
2459 aFrame->BuildDisplayListForStackingContext(&builder, target, &list);
2460 builder.LeavePresShell(aFrame, target);
2462 #ifdef MOZ_DUMP_PAINTING
2463 if (gDumpEventList) {
2464 fprintf_stderr(stderr, "Event handling --- (%d,%d):\n", aRect.x, aRect.y);
2465 nsFrame::PrintDisplayList(&builder, list);
2466 }
2467 #endif
2469 nsDisplayItem::HitTestState hitTestState;
2470 list.HitTest(&builder, target, &hitTestState, &aOutFrames);
2471 list.DeleteAll();
2472 return NS_OK;
2473 }
2475 // This function is only used on B2G, and some compilers complain about
2476 // unused static functions, so we need to #ifdef it.
2477 #ifdef MOZ_WIDGET_GONK
2478 // aScrollFrame and aScrollFrameAsScrollable must be non-nullptr
2479 static FrameMetrics
2480 CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
2481 nsIScrollableFrame* aScrollFrameAsScrollable) {
2482 // Calculate the metrics necessary for calculating the displayport.
2483 // This code has a lot in common with the code in RecordFrameMetrics();
2484 // we may want to refactor this at some point.
2485 FrameMetrics metrics;
2486 nsPresContext* presContext = aScrollFrame->PresContext();
2487 nsIPresShell* presShell = presContext->PresShell();
2488 CSSToLayoutDeviceScale deviceScale(float(nsPresContext::AppUnitsPerCSSPixel())
2489 / presContext->AppUnitsPerDevPixel());
2490 ParentLayerToLayerScale resolution(presShell->GetResolution().width);
2491 LayoutDeviceToLayerScale cumulativeResolution(presShell->GetCumulativeResolution().width);
2493 metrics.mDevPixelsPerCSSPixel = deviceScale;
2494 metrics.mResolution = resolution;
2495 metrics.mCumulativeResolution = cumulativeResolution;
2496 metrics.SetZoom(deviceScale * cumulativeResolution * LayerToScreenScale(1));
2498 // Only the size of the composition bounds is relevant to the
2499 // displayport calculation, not its origin.
2500 nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(aScrollFrame);
2501 metrics.mCompositionBounds
2502 = RoundedToInt(LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0, 0), compositionSize),
2503 presContext->AppUnitsPerDevPixel())
2504 * (cumulativeResolution / resolution));
2506 // This function is used for setting a display port for subframes, so
2507 // aScrollFrame will not be the root content document's root scroll frame.
2508 metrics.SetRootCompositionSize(
2509 nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame, false, metrics));
2511 metrics.SetScrollOffset(CSSPoint::FromAppUnits(
2512 aScrollFrameAsScrollable->GetScrollPosition()));
2514 metrics.mScrollableRect = CSSRect::FromAppUnits(
2515 nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrameAsScrollable, nullptr));
2517 return metrics;
2518 }
2519 #endif
2521 bool
2522 nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
2523 nsIFrame* aScrollFrame,
2524 nsRect aDisplayPortBase,
2525 nsRect* aOutDisplayport) {
2526 nsIContent* content = aScrollFrame->GetContent();
2527 nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame);
2528 if (!content || !scrollableFrame) {
2529 return false;
2530 }
2532 // Set the base rect. Note that this will not influence 'haveDisplayPort',
2533 // which is based on either the whole rect or margins being set, but it
2534 // will affect what is returned in 'aOutDisplayPort' if margins are set.
2535 SetDisplayPortBase(content, aDisplayPortBase);
2537 bool haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
2539 #ifdef MOZ_WIDGET_GONK
2540 // On B2G, we perform an optimization where we ensure that at least one
2541 // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.
2542 // If that's not the case yet, and we are async-scrollable, we will get a
2543 // displayport.
2544 // Note: we only do this in processes where we do subframe scrolling to
2545 // begin with (i.e., not in the parent process on B2G).
2546 if (WantSubAPZC() &&
2547 !aBuilder.HaveScrollableDisplayPort() &&
2548 scrollableFrame->WantAsyncScroll()) {
2550 // If we don't already have a displayport, calculate and set one.
2551 if (!haveDisplayPort) {
2552 FrameMetrics metrics = CalculateFrameMetricsForDisplayPort(aScrollFrame, scrollableFrame);
2553 LayerMargin displayportMargins = AsyncPanZoomController::CalculatePendingDisplayPort(
2554 metrics, ScreenPoint(0.0f, 0.0f), 0.0);
2555 nsIPresShell* presShell = aScrollFrame->PresContext()->GetPresShell();
2556 gfx::IntSize alignment = gfxPrefs::LayersTilesEnabled()
2557 ? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) :
2558 gfx::IntSize(0, 0);
2559 nsLayoutUtils::SetDisplayPortMargins(
2560 content, presShell, displayportMargins, alignment.width,
2561 alignment.height, 0, nsLayoutUtils::RepaintMode::DoNotRepaint);
2562 haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
2563 NS_ASSERTION(haveDisplayPort, "should have a displayport after having just set it");
2564 }
2566 // Record that the we now have a scrollable display port.
2567 aBuilder.SetHaveScrollableDisplayPort();
2568 }
2569 #endif
2571 return haveDisplayPort;
2572 }
2574 nsresult
2575 nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
2576 const nsRegion& aDirtyRegion, nscolor aBackstop,
2577 uint32_t aFlags)
2578 {
2579 PROFILER_LABEL("nsLayoutUtils","PaintFrame");
2580 if (aFlags & PAINT_WIDGET_LAYERS) {
2581 nsView* view = aFrame->GetView();
2582 if (!(view && view->GetWidget() && GetDisplayRootFrame(aFrame) == aFrame)) {
2583 aFlags &= ~PAINT_WIDGET_LAYERS;
2584 NS_ASSERTION(aRenderingContext, "need a rendering context");
2585 }
2586 }
2588 nsPresContext* presContext = aFrame->PresContext();
2589 nsIPresShell* presShell = presContext->PresShell();
2590 nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
2591 if (!rootPresContext) {
2592 return NS_OK;
2593 }
2595 nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::PAINTING,
2596 !(aFlags & PAINT_HIDE_CARET));
2598 nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
2599 bool usingDisplayPort = false;
2600 nsRect displayport;
2601 if (rootScrollFrame && !aFrame->GetParent()) {
2602 nsRect displayportBase(
2603 nsPoint(0,0),
2604 nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame));
2605 usingDisplayPort = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
2606 builder, rootScrollFrame, displayportBase, &displayport);
2607 }
2609 nsRegion visibleRegion;
2610 if (aFlags & PAINT_WIDGET_LAYERS) {
2611 // This layer tree will be reused, so we'll need to calculate it
2612 // for the whole "visible" area of the window
2613 //
2614 // |ignoreViewportScrolling| and |usingDisplayPort| are persistent
2615 // document-rendering state. We rely on PresShell to flush
2616 // retained layers as needed when that persistent state changes.
2617 if (!usingDisplayPort) {
2618 visibleRegion = aFrame->GetVisualOverflowRectRelativeToSelf();
2619 } else {
2620 visibleRegion = displayport;
2621 }
2622 } else {
2623 visibleRegion = aDirtyRegion;
2624 }
2626 // If we're going to display something different from what we'd normally
2627 // paint in a window then we will flush out any retained layer trees before
2628 // *and after* we draw.
2629 bool willFlushRetainedLayers = (aFlags & PAINT_HIDE_CARET) != 0;
2631 nsDisplayList list;
2632 if (aFlags & PAINT_IN_TRANSFORM) {
2633 builder.SetInTransform(true);
2634 }
2635 if (aFlags & PAINT_SYNC_DECODE_IMAGES) {
2636 builder.SetSyncDecodeImages(true);
2637 }
2638 if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) {
2639 builder.SetPaintingToWindow(true);
2640 }
2641 if (aFlags & PAINT_IGNORE_SUPPRESSION) {
2642 builder.IgnorePaintSuppression();
2643 }
2644 // Windowed plugins aren't allowed in popups
2645 if ((aFlags & PAINT_WIDGET_LAYERS) &&
2646 !willFlushRetainedLayers &&
2647 !(aFlags & PAINT_DOCUMENT_RELATIVE) &&
2648 rootPresContext->NeedToComputePluginGeometryUpdates()) {
2649 builder.SetWillComputePluginGeometry(true);
2650 }
2651 nsRect canvasArea(nsPoint(0, 0), aFrame->GetSize());
2653 bool ignoreViewportScrolling =
2654 aFrame->GetParent() ? false : presShell->IgnoringViewportScrolling();
2655 if (ignoreViewportScrolling && rootScrollFrame) {
2656 nsIScrollableFrame* rootScrollableFrame =
2657 presShell->GetRootScrollFrameAsScrollable();
2658 if (aFlags & PAINT_DOCUMENT_RELATIVE) {
2659 // Make visibleRegion and aRenderingContext relative to the
2660 // scrolled frame instead of the root frame.
2661 nsPoint pos = rootScrollableFrame->GetScrollPosition();
2662 visibleRegion.MoveBy(-pos);
2663 if (aRenderingContext) {
2664 aRenderingContext->Translate(pos);
2665 }
2666 }
2667 builder.SetIgnoreScrollFrame(rootScrollFrame);
2669 nsCanvasFrame* canvasFrame =
2670 do_QueryFrame(rootScrollableFrame->GetScrolledFrame());
2671 if (canvasFrame) {
2672 // Use UnionRect here to ensure that areas where the scrollbars
2673 // were are still filled with the background color.
2674 canvasArea.UnionRect(canvasArea,
2675 canvasFrame->CanvasArea() + builder.ToReferenceFrame(canvasFrame));
2676 }
2677 }
2679 nsRect dirtyRect = visibleRegion.GetBounds();
2680 builder.EnterPresShell(aFrame, dirtyRect);
2681 {
2682 PROFILER_LABEL("nsLayoutUtils","PaintFrame::BuildDisplayList");
2683 aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
2684 }
2685 const bool paintAllContinuations = aFlags & PAINT_ALL_CONTINUATIONS;
2686 NS_ASSERTION(!paintAllContinuations || !aFrame->GetPrevContinuation(),
2687 "If painting all continuations, the frame must be "
2688 "first-continuation");
2690 nsIAtom* frameType = aFrame->GetType();
2692 if (paintAllContinuations) {
2693 nsIFrame* currentFrame = aFrame;
2694 while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) {
2695 PROFILER_LABEL("nsLayoutUtils","PaintFrame::ContinuationsBuildDisplayList");
2696 nsRect frameDirty = dirtyRect - builder.ToReferenceFrame(currentFrame);
2697 currentFrame->BuildDisplayListForStackingContext(&builder,
2698 frameDirty, &list);
2699 }
2700 }
2702 // For the viewport frame in print preview/page layout we want to paint
2703 // the grey background behind the page, not the canvas color.
2704 if (frameType == nsGkAtoms::viewportFrame &&
2705 nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
2706 nsRect bounds = nsRect(builder.ToReferenceFrame(aFrame),
2707 aFrame->GetSize());
2708 presShell->AddPrintPreviewBackgroundItem(builder, list, aFrame, bounds);
2709 } else if (frameType != nsGkAtoms::pageFrame) {
2710 // For printing, this function is first called on an nsPageFrame, which
2711 // creates a display list with a PageContent item. The PageContent item's
2712 // paint function calls this function on the nsPageFrame's child which is
2713 // an nsPageContentFrame. We only want to add the canvas background color
2714 // item once, for the nsPageContentFrame.
2716 // Add the canvas background color to the bottom of the list. This
2717 // happens after we've built the list so that AddCanvasBackgroundColorItem
2718 // can monkey with the contents if necessary.
2719 canvasArea.IntersectRect(canvasArea, visibleRegion.GetBounds());
2720 presShell->AddCanvasBackgroundColorItem(
2721 builder, list, aFrame, canvasArea, aBackstop);
2723 // If the passed in backstop color makes us draw something different from
2724 // normal, we need to flush layers.
2725 if ((aFlags & PAINT_WIDGET_LAYERS) && !willFlushRetainedLayers) {
2726 nsView* view = aFrame->GetView();
2727 if (view) {
2728 nscolor backstop = presShell->ComputeBackstopColor(view);
2729 // The PresShell's canvas background color doesn't get updated until
2730 // EnterPresShell, so this check has to be done after that.
2731 nscolor canvasColor = presShell->GetCanvasBackground();
2732 if (NS_ComposeColors(aBackstop, canvasColor) !=
2733 NS_ComposeColors(backstop, canvasColor)) {
2734 willFlushRetainedLayers = true;
2735 }
2736 }
2737 }
2738 }
2740 builder.LeavePresShell(aFrame, dirtyRect);
2742 if (builder.GetHadToIgnorePaintSuppression()) {
2743 willFlushRetainedLayers = true;
2744 }
2746 #ifdef MOZ_DUMP_PAINTING
2747 FILE* savedDumpFile = gfxUtils::sDumpPaintFile;
2748 if (gfxUtils::sDumpPaintList || gfxUtils::sDumpPainting) {
2749 if (gfxUtils::sDumpPaintingToFile) {
2750 nsCString string("dump-");
2751 string.AppendInt(gPaintCount);
2752 string.Append(".html");
2753 gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w");
2754 } else {
2755 gfxUtils::sDumpPaintFile = stderr;
2756 }
2757 if (gfxUtils::sDumpPaintingToFile) {
2758 fprintf_stderr(gfxUtils::sDumpPaintFile, "<html><head><script>var array = {}; function ViewImage(index) { window.location = array[index]; }</script></head><body>");
2759 }
2760 fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- before optimization (dirty %d,%d,%d,%d):\n",
2761 dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
2762 nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile);
2763 if (gfxUtils::sDumpPaintingToFile) {
2764 fprintf_stderr(gfxUtils::sDumpPaintFile, "<script>");
2765 }
2766 }
2767 #endif
2769 list.ComputeVisibilityForRoot(&builder, &visibleRegion,
2770 usingDisplayPort ? rootScrollFrame : nullptr);
2772 uint32_t flags = nsDisplayList::PAINT_DEFAULT;
2773 if (aFlags & PAINT_WIDGET_LAYERS) {
2774 flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS;
2775 if (willFlushRetainedLayers) {
2776 // The caller wanted to paint from retained layers, but set up
2777 // the paint in such a way that we can't use them. We're going
2778 // to display something different from what we'd normally paint
2779 // in a window, so make sure we flush out any retained layer
2780 // trees before *and after* we draw. Callers should be fixed to
2781 // not do this.
2782 NS_WARNING("Flushing retained layers!");
2783 flags |= nsDisplayList::PAINT_FLUSH_LAYERS;
2784 } else if (!(aFlags & PAINT_DOCUMENT_RELATIVE)) {
2785 nsIWidget *widget = aFrame->GetNearestWidget();
2786 if (widget) {
2787 builder.SetFinalTransparentRegion(visibleRegion);
2788 // If we're finished building display list items for painting of the outermost
2789 // pres shell, notify the widget about any toolbars we've encountered.
2790 widget->UpdateThemeGeometries(builder.GetThemeGeometries());
2791 }
2792 }
2793 }
2794 if (aFlags & PAINT_EXISTING_TRANSACTION) {
2795 flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
2796 }
2797 if (aFlags & PAINT_NO_COMPOSITE) {
2798 flags |= nsDisplayList::PAINT_NO_COMPOSITE;
2799 }
2800 if (aFlags & PAINT_COMPRESSED) {
2801 flags |= nsDisplayList::PAINT_COMPRESSED;
2802 }
2804 list.PaintRoot(&builder, aRenderingContext, flags);
2806 #ifdef MOZ_DUMP_PAINTING
2807 if (gfxUtils::sDumpPaintList || gfxUtils::sDumpPainting) {
2808 if (gfxUtils::sDumpPaintingToFile) {
2809 fprintf_stderr(gfxUtils::sDumpPaintFile, "</script>");
2810 }
2811 fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- after optimization:\n");
2812 nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile);
2814 fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- retained layer tree:\n");
2815 nsIWidget* widget = aFrame->GetNearestWidget();
2816 if (widget) {
2817 nsRefPtr<LayerManager> layerManager = widget->GetLayerManager();
2818 if (layerManager) {
2819 FrameLayerBuilder::DumpRetainedLayerTree(layerManager, gfxUtils::sDumpPaintFile,
2820 gfxUtils::sDumpPaintingToFile);
2821 }
2822 }
2823 if (gfxUtils::sDumpPaintingToFile) {
2824 fprintf(gfxUtils::sDumpPaintFile, "</body></html>");
2825 fclose(gfxUtils::sDumpPaintFile);
2826 }
2827 gfxUtils::sDumpPaintFile = savedDumpFile;
2828 gPaintCount++;
2829 }
2830 #endif
2832 // Update the widget's opaque region information. This sets
2833 // glass boundaries on Windows. Also set up plugin clip regions and bounds.
2834 if ((aFlags & PAINT_WIDGET_LAYERS) &&
2835 !willFlushRetainedLayers &&
2836 !(aFlags & PAINT_DOCUMENT_RELATIVE)) {
2837 nsIWidget *widget = aFrame->GetNearestWidget();
2838 if (widget) {
2839 nsRegion excludedRegion = builder.GetExcludedGlassRegion();
2840 excludedRegion.Sub(excludedRegion, visibleRegion);
2841 nsIntRegion windowRegion(excludedRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel()));
2842 widget->UpdateOpaqueRegion(windowRegion);
2843 }
2844 }
2846 if (builder.WillComputePluginGeometry()) {
2847 nsRefPtr<LayerManager> layerManager;
2848 nsIWidget* widget = aFrame->GetNearestWidget();
2849 if (widget) {
2850 layerManager = widget->GetLayerManager();
2851 }
2853 rootPresContext->ComputePluginGeometryUpdates(aFrame, &builder, &list);
2855 // We're not going to get a WillPaintWindow event here if we didn't do
2856 // widget invalidation, so just apply the plugin geometry update here instead.
2857 // We could instead have the compositor send back an equivalent to WillPaintWindow,
2858 // but it should be close enough to now not to matter.
2859 if (layerManager && !layerManager->NeedsWidgetInvalidation()) {
2860 rootPresContext->ApplyPluginGeometryUpdates();
2861 }
2863 // We told the compositor thread not to composite when it received the transaction because
2864 // we wanted to update plugins first. Schedule the composite now.
2865 if (layerManager) {
2866 layerManager->Composite();
2867 }
2868 }
2871 // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
2872 list.DeleteAll();
2873 return NS_OK;
2874 }
2876 /**
2877 * Uses a binary search for find where the cursor falls in the line of text
2878 * It also keeps track of the part of the string that has already been measured
2879 * so it doesn't have to keep measuring the same text over and over
2880 *
2881 * @param "aBaseWidth" contains the width in twips of the portion
2882 * of the text that has already been measured, and aBaseInx contains
2883 * the index of the text that has already been measured.
2884 *
2885 * @param aTextWidth returns the (in twips) the length of the text that falls
2886 * before the cursor aIndex contains the index of the text where the cursor falls
2887 */
2888 bool
2889 nsLayoutUtils::BinarySearchForPosition(nsRenderingContext* aRendContext,
2890 const char16_t* aText,
2891 int32_t aBaseWidth,
2892 int32_t aBaseInx,
2893 int32_t aStartInx,
2894 int32_t aEndInx,
2895 int32_t aCursorPos,
2896 int32_t& aIndex,
2897 int32_t& aTextWidth)
2898 {
2899 int32_t range = aEndInx - aStartInx;
2900 if ((range == 1) || (range == 2 && NS_IS_HIGH_SURROGATE(aText[aStartInx]))) {
2901 aIndex = aStartInx + aBaseInx;
2902 aTextWidth = aRendContext->GetWidth(aText, aIndex);
2903 return true;
2904 }
2906 int32_t inx = aStartInx + (range / 2);
2908 // Make sure we don't leave a dangling low surrogate
2909 if (NS_IS_HIGH_SURROGATE(aText[inx-1]))
2910 inx++;
2912 int32_t textWidth = aRendContext->GetWidth(aText, inx);
2914 int32_t fullWidth = aBaseWidth + textWidth;
2915 if (fullWidth == aCursorPos) {
2916 aTextWidth = textWidth;
2917 aIndex = inx;
2918 return true;
2919 } else if (aCursorPos < fullWidth) {
2920 aTextWidth = aBaseWidth;
2921 if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, aStartInx, inx, aCursorPos, aIndex, aTextWidth)) {
2922 return true;
2923 }
2924 } else {
2925 aTextWidth = fullWidth;
2926 if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) {
2927 return true;
2928 }
2929 }
2930 return false;
2931 }
2933 static void
2934 AddBoxesForFrame(nsIFrame* aFrame,
2935 nsLayoutUtils::BoxCallback* aCallback)
2936 {
2937 nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
2939 if (pseudoType == nsCSSAnonBoxes::tableOuter) {
2940 AddBoxesForFrame(aFrame->GetFirstPrincipalChild(), aCallback);
2941 nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList);
2942 if (kid) {
2943 AddBoxesForFrame(kid, aCallback);
2944 }
2945 } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock ||
2946 pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock ||
2947 pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock ||
2948 pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
2949 for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
2950 AddBoxesForFrame(kid, aCallback);
2951 }
2952 } else {
2953 aCallback->AddBox(aFrame);
2954 }
2955 }
2957 void
2958 nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback)
2959 {
2960 while (aFrame) {
2961 AddBoxesForFrame(aFrame, aCallback);
2962 aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
2963 }
2964 }
2966 nsIFrame*
2967 nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame)
2968 {
2969 while (aFrame) {
2970 nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
2972 if (pseudoType == nsCSSAnonBoxes::tableOuter) {
2973 nsIFrame* f = GetFirstNonAnonymousFrame(aFrame->GetFirstPrincipalChild());
2974 if (f) {
2975 return f;
2976 }
2977 nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList);
2978 if (kid) {
2979 f = GetFirstNonAnonymousFrame(kid);
2980 if (f) {
2981 return f;
2982 }
2983 }
2984 } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock ||
2985 pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock ||
2986 pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock ||
2987 pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
2988 for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
2989 nsIFrame* f = GetFirstNonAnonymousFrame(kid);
2990 if (f) {
2991 return f;
2992 }
2993 }
2994 } else {
2995 return aFrame;
2996 }
2998 aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
2999 }
3000 return nullptr;
3001 }
3003 struct BoxToRect : public nsLayoutUtils::BoxCallback {
3004 nsIFrame* mRelativeTo;
3005 nsLayoutUtils::RectCallback* mCallback;
3006 uint32_t mFlags;
3008 BoxToRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
3009 uint32_t aFlags)
3010 : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {}
3012 virtual void AddBox(nsIFrame* aFrame) MOZ_OVERRIDE {
3013 nsRect r;
3014 nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r);
3015 if (!outer) {
3016 outer = aFrame;
3017 switch (mFlags & nsLayoutUtils::RECTS_WHICH_BOX_MASK) {
3018 case nsLayoutUtils::RECTS_USE_CONTENT_BOX:
3019 r = aFrame->GetContentRectRelativeToSelf();
3020 break;
3021 case nsLayoutUtils::RECTS_USE_PADDING_BOX:
3022 r = aFrame->GetPaddingRectRelativeToSelf();
3023 break;
3024 case nsLayoutUtils::RECTS_USE_MARGIN_BOX:
3025 r = aFrame->GetMarginRectRelativeToSelf();
3026 break;
3027 default: // Use the border box
3028 r = aFrame->GetRectRelativeToSelf();
3029 }
3030 }
3031 if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) {
3032 r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo);
3033 } else {
3034 r += outer->GetOffsetTo(mRelativeTo);
3035 }
3036 mCallback->AddRect(r);
3037 }
3038 };
3040 void
3041 nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
3042 RectCallback* aCallback, uint32_t aFlags)
3043 {
3044 BoxToRect converter(aRelativeTo, aCallback, aFlags);
3045 GetAllInFlowBoxes(aFrame, &converter);
3046 }
3048 nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
3050 void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) {
3051 mResultRect.UnionRect(mResultRect, aRect);
3052 if (!mSeenFirstRect) {
3053 mSeenFirstRect = true;
3054 mFirstRect = aRect;
3055 }
3056 }
3058 nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList* aList)
3059 : mRectList(aList)
3060 {
3061 }
3063 void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) {
3064 nsRefPtr<DOMRect> rect = new DOMRect(mRectList);
3066 rect->SetLayoutRect(aRect);
3067 mRectList->Append(rect);
3068 }
3070 nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame)
3071 {
3072 return aFrame->PresContext()->PresShell()->GetRootFrame();
3073 }
3075 nsRect
3076 nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo,
3077 uint32_t aFlags) {
3078 RectAccumulator accumulator;
3079 GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags);
3080 return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
3081 : accumulator.mResultRect;
3082 }
3084 nsRect
3085 nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
3086 nsIFrame* aFrame,
3087 uint32_t aFlags)
3088 {
3089 const nsStyleText* textStyle = aFrame->StyleText();
3090 if (!textStyle->HasTextShadow())
3091 return aTextAndDecorationsRect;
3093 nsRect resultRect = aTextAndDecorationsRect;
3094 int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
3095 for (uint32_t i = 0; i < textStyle->mTextShadow->Length(); ++i) {
3096 nsCSSShadowItem* shadow = textStyle->mTextShadow->ShadowAt(i);
3097 nsMargin blur = nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D);
3098 if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0))
3099 continue;
3101 nsRect tmpRect(aTextAndDecorationsRect);
3103 tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
3104 tmpRect.Inflate(blur);
3106 resultRect.UnionRect(resultRect, tmpRect);
3107 }
3108 return resultRect;
3109 }
3111 nsresult
3112 nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame,
3113 nsFontMetrics** aFontMetrics,
3114 float aInflation)
3115 {
3116 return nsLayoutUtils::GetFontMetricsForStyleContext(aFrame->StyleContext(),
3117 aFontMetrics,
3118 aInflation);
3119 }
3121 nsresult
3122 nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext,
3123 nsFontMetrics** aFontMetrics,
3124 float aInflation)
3125 {
3126 // pass the user font set object into the device context to pass along to CreateFontGroup
3127 nsPresContext* pc = aStyleContext->PresContext();
3128 gfxUserFontSet* fs = pc->GetUserFontSet();
3129 gfxTextPerfMetrics* tp = pc->GetTextPerfMetrics();
3131 nsFont font = aStyleContext->StyleFont()->mFont;
3132 // We need to not run font.size through floats when it's large since
3133 // doing so would be lossy. Fortunately, in such cases, aInflation is
3134 // guaranteed to be 1.0f.
3135 if (aInflation != 1.0f) {
3136 font.size = NSToCoordRound(font.size * aInflation);
3137 }
3138 return pc->DeviceContext()->GetMetricsFor(
3139 font, aStyleContext->StyleFont()->mLanguage,
3140 fs, tp, *aFontMetrics);
3141 }
3143 nsIFrame*
3144 nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame)
3145 {
3146 nsIFrame* result = aDescendantFrame;
3148 while (result) {
3149 nsIFrame* parent = result->GetParent();
3150 if (parent == aParent) {
3151 break;
3152 }
3154 // The frame is not an immediate child of aParent so walk up another level
3155 result = parent;
3156 }
3158 return result;
3159 }
3161 nsBlockFrame*
3162 nsLayoutUtils::GetAsBlock(nsIFrame* aFrame)
3163 {
3164 nsBlockFrame* block = do_QueryFrame(aFrame);
3165 return block;
3166 }
3168 nsBlockFrame*
3169 nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame)
3170 {
3171 nsIFrame* nextAncestor;
3172 for (nextAncestor = aFrame->GetParent(); nextAncestor;
3173 nextAncestor = nextAncestor->GetParent()) {
3174 nsBlockFrame* block = GetAsBlock(nextAncestor);
3175 if (block)
3176 return block;
3177 }
3178 return nullptr;
3179 }
3181 nsIFrame*
3182 nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame* aFrame)
3183 {
3184 if (!(aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT))
3185 return aFrame;
3187 nsIFrame* f = aFrame;
3188 do {
3189 f = GetParentOrPlaceholderFor(f);
3190 } while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT);
3191 return f;
3192 }
3194 nsIFrame*
3195 nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame)
3196 {
3197 if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
3198 && !aFrame->GetPrevInFlow()) {
3199 return aFrame->PresContext()->PresShell()->FrameManager()->
3200 GetPlaceholderFrameFor(aFrame);
3201 }
3202 return aFrame->GetParent();
3203 }
3205 nsIFrame*
3206 nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(nsIFrame* aFrame)
3207 {
3208 nsIFrame* f = GetParentOrPlaceholderFor(aFrame);
3209 if (f)
3210 return f;
3211 return GetCrossDocParentFrame(aFrame);
3212 }
3214 nsIFrame*
3215 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame *aFrame)
3216 {
3217 nsIFrame *result = aFrame->GetNextContinuation();
3218 if (result)
3219 return result;
3221 if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0) {
3222 // We only store the ib-split sibling annotation with the first
3223 // frame in the continuation chain. Walk back to find that frame now.
3224 aFrame = aFrame->FirstContinuation();
3226 void* value = aFrame->Properties().Get(nsIFrame::IBSplitSibling());
3227 return static_cast<nsIFrame*>(value);
3228 }
3230 return nullptr;
3231 }
3233 nsIFrame*
3234 nsLayoutUtils::FirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
3235 {
3236 nsIFrame *result = aFrame->FirstContinuation();
3237 if (result->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
3238 while (true) {
3239 nsIFrame *f = static_cast<nsIFrame*>
3240 (result->Properties().Get(nsIFrame::IBSplitPrevSibling()));
3241 if (!f)
3242 break;
3243 result = f;
3244 }
3245 }
3247 return result;
3248 }
3250 bool
3251 nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
3252 {
3253 if (aFrame->GetPrevContinuation()) {
3254 return false;
3255 }
3256 if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
3257 aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())) {
3258 return false;
3259 }
3261 return true;
3262 }
3264 bool
3265 nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame)
3266 {
3267 if (!aFrame)
3268 return false;
3270 nsIFrame* rootScrollFrame =
3271 aFrame->PresContext()->PresShell()->GetRootScrollFrame();
3272 if (!rootScrollFrame)
3273 return false;
3275 nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame);
3276 NS_ASSERTION(rootScrollableFrame, "The root scorollable frame is null");
3278 if (!IsProperAncestorFrame(rootScrollFrame, aFrame))
3279 return false;
3281 nsIFrame* rootScrolledFrame = rootScrollableFrame->GetScrolledFrame();
3282 return !(rootScrolledFrame == aFrame ||
3283 IsProperAncestorFrame(rootScrolledFrame, aFrame));
3284 }
3286 static nscoord AddPercents(nsLayoutUtils::IntrinsicWidthType aType,
3287 nscoord aCurrent, float aPercent)
3288 {
3289 nscoord result = aCurrent;
3290 if (aPercent > 0.0f && aType == nsLayoutUtils::PREF_WIDTH) {
3291 // XXX Should we also consider percentages for min widths, up to a
3292 // limit?
3293 if (aPercent >= 1.0f)
3294 result = nscoord_MAX;
3295 else
3296 result = NSToCoordRound(float(result) / (1.0f - aPercent));
3297 }
3298 return result;
3299 }
3301 // Use only for widths/heights (or their min/max), since it clamps
3302 // negative calc() results to 0.
3303 static bool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult)
3304 {
3305 if (aStyle.IsCalcUnit()) {
3306 if (aStyle.CalcHasPercent()) {
3307 return false;
3308 }
3309 // If it has no percents, we can pass 0 for the percentage basis.
3310 aResult = nsRuleNode::ComputeComputedCalc(aStyle, 0);
3311 if (aResult < 0)
3312 aResult = 0;
3313 return true;
3314 }
3316 if (eStyleUnit_Coord != aStyle.GetUnit())
3317 return false;
3319 aResult = aStyle.GetCoordValue();
3320 NS_ASSERTION(aResult >= 0, "negative widths not allowed");
3321 return true;
3322 }
3324 // Only call on style coords for which GetAbsoluteCoord returned false.
3325 static bool
3326 GetPercentHeight(const nsStyleCoord& aStyle,
3327 nsIFrame* aFrame,
3328 nscoord& aResult)
3329 {
3330 if (eStyleUnit_Percent != aStyle.GetUnit() &&
3331 !aStyle.IsCalcUnit())
3332 return false;
3334 MOZ_ASSERT(!aStyle.IsCalcUnit() || aStyle.CalcHasPercent(),
3335 "GetAbsoluteCoord should have handled this");
3337 nsIFrame *f = aFrame->GetContainingBlock();
3338 if (!f) {
3339 NS_NOTREACHED("top of frame tree not a containing block");
3340 return false;
3341 }
3343 // During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses
3344 // SetComputedHeight on the reflow state for its child to propagate its
3345 // computed height to the scrolled content. So here we skip to the scroll
3346 // frame that contains this scrolled content in order to get the same
3347 // behavior as layout when computing percentage heights.
3348 if (f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::scrolledContent) {
3349 f = f->GetParent();
3350 }
3352 const nsStylePosition *pos = f->StylePosition();
3353 nscoord h;
3354 if (!GetAbsoluteCoord(pos->mHeight, h) &&
3355 !GetPercentHeight(pos->mHeight, f, h)) {
3356 NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto ||
3357 pos->mHeight.HasPercent(),
3358 "unknown height unit");
3359 nsIAtom* fType = f->GetType();
3360 if (fType != nsGkAtoms::viewportFrame && fType != nsGkAtoms::canvasFrame &&
3361 fType != nsGkAtoms::pageContentFrame) {
3362 // There's no basis for the percentage height, so it acts like auto.
3363 // Should we consider a max-height < min-height pair a basis for
3364 // percentage heights? The spec is somewhat unclear, and not doing
3365 // so is simpler and avoids troubling discontinuities in behavior,
3366 // so I'll choose not to. -LDB
3367 return false;
3368 }
3370 NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto,
3371 "Unexpected height unit for viewport or canvas or page-content");
3372 // For the viewport, canvas, and page-content kids, the percentage
3373 // basis is just the parent height.
3374 h = f->GetSize().height;
3375 if (h == NS_UNCONSTRAINEDSIZE) {
3376 // We don't have a percentage basis after all
3377 return false;
3378 }
3379 }
3381 nscoord maxh;
3382 if (GetAbsoluteCoord(pos->mMaxHeight, maxh) ||
3383 GetPercentHeight(pos->mMaxHeight, f, maxh)) {
3384 if (maxh < h)
3385 h = maxh;
3386 } else {
3387 NS_ASSERTION(pos->mMaxHeight.GetUnit() == eStyleUnit_None ||
3388 pos->mMaxHeight.HasPercent(),
3389 "unknown max-height unit");
3390 }
3392 nscoord minh;
3393 if (GetAbsoluteCoord(pos->mMinHeight, minh) ||
3394 GetPercentHeight(pos->mMinHeight, f, minh)) {
3395 if (minh > h)
3396 h = minh;
3397 } else {
3398 NS_ASSERTION(pos->mMinHeight.HasPercent(),
3399 "unknown min-height unit");
3400 }
3402 if (aStyle.IsCalcUnit()) {
3403 aResult = std::max(nsRuleNode::ComputeComputedCalc(aStyle, h), 0);
3404 return true;
3405 }
3407 aResult = NSToCoordRound(aStyle.GetPercentValue() * h);
3408 return true;
3409 }
3411 // Handles only -moz-max-content and -moz-min-content, and
3412 // -moz-fit-content for min-width and max-width, since the others
3413 // (-moz-fit-content for width, and -moz-available) have no effect on
3414 // intrinsic widths.
3415 enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH };
3416 static bool
3417 GetIntrinsicCoord(const nsStyleCoord& aStyle,
3418 nsRenderingContext* aRenderingContext,
3419 nsIFrame* aFrame,
3420 eWidthProperty aProperty,
3421 nscoord& aResult)
3422 {
3423 NS_PRECONDITION(aProperty == PROP_WIDTH || aProperty == PROP_MAX_WIDTH ||
3424 aProperty == PROP_MIN_WIDTH, "unexpected property");
3425 if (aStyle.GetUnit() != eStyleUnit_Enumerated)
3426 return false;
3427 int32_t val = aStyle.GetIntValue();
3428 NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT ||
3429 val == NS_STYLE_WIDTH_MIN_CONTENT ||
3430 val == NS_STYLE_WIDTH_FIT_CONTENT ||
3431 val == NS_STYLE_WIDTH_AVAILABLE,
3432 "unexpected enumerated value for width property");
3433 if (val == NS_STYLE_WIDTH_AVAILABLE)
3434 return false;
3435 if (val == NS_STYLE_WIDTH_FIT_CONTENT) {
3436 if (aProperty == PROP_WIDTH)
3437 return false; // handle like 'width: auto'
3438 if (aProperty == PROP_MAX_WIDTH)
3439 // constrain large 'width' values down to -moz-max-content
3440 val = NS_STYLE_WIDTH_MAX_CONTENT;
3441 else
3442 // constrain small 'width' or 'max-width' values up to -moz-min-content
3443 val = NS_STYLE_WIDTH_MIN_CONTENT;
3444 }
3446 NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT ||
3447 val == NS_STYLE_WIDTH_MIN_CONTENT,
3448 "should have reduced everything remaining to one of these");
3450 // If aFrame is a container for font size inflation, then shrink
3451 // wrapping inside of it should not apply font size inflation.
3452 AutoMaybeDisableFontInflation an(aFrame);
3454 if (val == NS_STYLE_WIDTH_MAX_CONTENT)
3455 aResult = aFrame->GetPrefWidth(aRenderingContext);
3456 else
3457 aResult = aFrame->GetMinWidth(aRenderingContext);
3458 return true;
3459 }
3461 #undef DEBUG_INTRINSIC_WIDTH
3463 #ifdef DEBUG_INTRINSIC_WIDTH
3464 static int32_t gNoiseIndent = 0;
3465 #endif
3467 /* static */ nscoord
3468 nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext,
3469 nsIFrame *aFrame,
3470 IntrinsicWidthType aType,
3471 uint32_t aFlags)
3472 {
3473 NS_PRECONDITION(aFrame, "null frame");
3474 NS_PRECONDITION(aType == MIN_WIDTH || aType == PREF_WIDTH, "bad type");
3476 #ifdef DEBUG_INTRINSIC_WIDTH
3477 nsFrame::IndentBy(stderr, gNoiseIndent);
3478 static_cast<nsFrame*>(aFrame)->ListTag(stderr);
3479 printf_stderr(" %s intrinsic width for container:\n",
3480 aType == MIN_WIDTH ? "min" : "pref");
3481 #endif
3483 // If aFrame is a container for font size inflation, then shrink
3484 // wrapping inside of it should not apply font size inflation.
3485 AutoMaybeDisableFontInflation an(aFrame);
3487 nsIFrame::IntrinsicWidthOffsetData offsets =
3488 aFrame->IntrinsicWidthOffsets(aRenderingContext);
3490 const nsStylePosition *stylePos = aFrame->StylePosition();
3491 uint8_t boxSizing = stylePos->mBoxSizing;
3492 const nsStyleCoord &styleWidth = stylePos->mWidth;
3493 const nsStyleCoord &styleMinWidth = stylePos->mMinWidth;
3494 const nsStyleCoord &styleMaxWidth = stylePos->mMaxWidth;
3496 // We build up two values starting with the content box, and then
3497 // adding padding, border and margin. The result is normally
3498 // |result|. Then, when we handle 'width', 'min-width', and
3499 // 'max-width', we use the results we've been building in |min| as a
3500 // minimum, overriding 'min-width'. This ensures two things:
3501 // * that we don't let a value of 'box-sizing' specifying a width
3502 // smaller than the padding/border inside the box-sizing box give
3503 // a content width less than zero
3504 // * that we prevent tables from becoming smaller than their
3505 // intrinsic minimum width
3506 nscoord result = 0, min = 0;
3508 nscoord maxw;
3509 bool haveFixedMaxWidth = GetAbsoluteCoord(styleMaxWidth, maxw);
3510 nscoord minw;
3511 bool haveFixedMinWidth = GetAbsoluteCoord(styleMinWidth, minw);
3513 // If we have a specified width (or a specified 'min-width' greater
3514 // than the specified 'max-width', which works out to the same thing),
3515 // don't even bother getting the frame's intrinsic width, because in
3516 // this case GetAbsoluteCoord(styleWidth, w) will always succeed, so
3517 // we'll never need the intrinsic dimensions.
3518 if (styleWidth.GetUnit() == eStyleUnit_Enumerated &&
3519 (styleWidth.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT ||
3520 styleWidth.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) {
3521 // -moz-fit-content and -moz-available enumerated widths compute intrinsic
3522 // widths just like auto.
3523 // For -moz-max-content and -moz-min-content, we handle them like
3524 // specified widths, but ignore box-sizing.
3525 boxSizing = NS_STYLE_BOX_SIZING_CONTENT;
3526 } else if (!styleWidth.ConvertsToLength() &&
3527 !(haveFixedMinWidth && haveFixedMaxWidth && maxw <= minw)) {
3528 #ifdef DEBUG_INTRINSIC_WIDTH
3529 ++gNoiseIndent;
3530 #endif
3531 if (aType == MIN_WIDTH)
3532 result = aFrame->GetMinWidth(aRenderingContext);
3533 else
3534 result = aFrame->GetPrefWidth(aRenderingContext);
3535 #ifdef DEBUG_INTRINSIC_WIDTH
3536 --gNoiseIndent;
3537 nsFrame::IndentBy(stderr, gNoiseIndent);
3538 static_cast<nsFrame*>(aFrame)->ListTag(stderr);
3539 printf_stderr(" %s intrinsic width from frame is %d.\n",
3540 aType == MIN_WIDTH ? "min" : "pref", result);
3541 #endif
3543 // Handle elements with an intrinsic ratio (or size) and a specified
3544 // height, min-height, or max-height.
3545 const nsStyleCoord &styleHeight = stylePos->mHeight;
3546 const nsStyleCoord &styleMinHeight = stylePos->mMinHeight;
3547 const nsStyleCoord &styleMaxHeight = stylePos->mMaxHeight;
3548 if (styleHeight.GetUnit() != eStyleUnit_Auto ||
3549 !(styleMinHeight.GetUnit() == eStyleUnit_Coord &&
3550 styleMinHeight.GetCoordValue() == 0) ||
3551 styleMaxHeight.GetUnit() != eStyleUnit_None) {
3553 nsSize ratio = aFrame->GetIntrinsicRatio();
3555 if (ratio.height != 0) {
3556 nscoord heightTakenByBoxSizing = 0;
3557 switch (boxSizing) {
3558 case NS_STYLE_BOX_SIZING_BORDER: {
3559 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
3560 heightTakenByBoxSizing +=
3561 styleBorder->GetComputedBorder().TopBottom();
3562 // fall through
3563 }
3564 case NS_STYLE_BOX_SIZING_PADDING: {
3565 if (!(aFlags & IGNORE_PADDING)) {
3566 const nsStylePadding* stylePadding = aFrame->StylePadding();
3567 nscoord pad;
3568 if (GetAbsoluteCoord(stylePadding->mPadding.GetTop(), pad) ||
3569 GetPercentHeight(stylePadding->mPadding.GetTop(), aFrame, pad)) {
3570 heightTakenByBoxSizing += pad;
3571 }
3572 if (GetAbsoluteCoord(stylePadding->mPadding.GetBottom(), pad) ||
3573 GetPercentHeight(stylePadding->mPadding.GetBottom(), aFrame, pad)) {
3574 heightTakenByBoxSizing += pad;
3575 }
3576 }
3577 // fall through
3578 }
3579 case NS_STYLE_BOX_SIZING_CONTENT:
3580 default:
3581 break;
3582 }
3584 nscoord h;
3585 if (GetAbsoluteCoord(styleHeight, h) ||
3586 GetPercentHeight(styleHeight, aFrame, h)) {
3587 h = std::max(0, h - heightTakenByBoxSizing);
3588 result =
3589 NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
3590 }
3592 if (GetAbsoluteCoord(styleMaxHeight, h) ||
3593 GetPercentHeight(styleMaxHeight, aFrame, h)) {
3594 h = std::max(0, h - heightTakenByBoxSizing);
3595 nscoord maxWidth =
3596 NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
3597 if (maxWidth < result)
3598 result = maxWidth;
3599 }
3601 if (GetAbsoluteCoord(styleMinHeight, h) ||
3602 GetPercentHeight(styleMinHeight, aFrame, h)) {
3603 h = std::max(0, h - heightTakenByBoxSizing);
3604 nscoord minWidth =
3605 NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
3606 if (minWidth > result)
3607 result = minWidth;
3608 }
3609 }
3610 }
3611 }
3613 if (aFrame->GetType() == nsGkAtoms::tableFrame) {
3614 // Tables can't shrink smaller than their intrinsic minimum width,
3615 // no matter what.
3616 min = aFrame->GetMinWidth(aRenderingContext);
3617 }
3619 // We also need to track what has been added on outside of the box
3620 // (controlled by 'box-sizing') where 'width', 'min-width' and
3621 // 'max-width' are applied. We have to account for these properties
3622 // after getting all the offsets (margin, border, padding) because
3623 // percentages do not operate linearly.
3624 // Doing this is ok because although percentages aren't handled
3625 // linearly, they are handled monotonically.
3626 nscoord coordOutsideWidth = 0;
3627 float pctOutsideWidth = 0;
3628 float pctTotal = 0.0f;
3630 if (!(aFlags & IGNORE_PADDING)) {
3631 coordOutsideWidth += offsets.hPadding;
3632 pctOutsideWidth += offsets.hPctPadding;
3634 if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) {
3635 min += coordOutsideWidth;
3636 result = NSCoordSaturatingAdd(result, coordOutsideWidth);
3637 pctTotal += pctOutsideWidth;
3639 coordOutsideWidth = 0;
3640 pctOutsideWidth = 0.0f;
3641 }
3642 }
3644 coordOutsideWidth += offsets.hBorder;
3646 if (boxSizing == NS_STYLE_BOX_SIZING_BORDER) {
3647 min += coordOutsideWidth;
3648 result = NSCoordSaturatingAdd(result, coordOutsideWidth);
3649 pctTotal += pctOutsideWidth;
3651 coordOutsideWidth = 0;
3652 pctOutsideWidth = 0.0f;
3653 }
3655 coordOutsideWidth += offsets.hMargin;
3656 pctOutsideWidth += offsets.hPctMargin;
3658 min += coordOutsideWidth;
3659 result = NSCoordSaturatingAdd(result, coordOutsideWidth);
3660 pctTotal += pctOutsideWidth;
3662 nscoord w;
3663 if (GetAbsoluteCoord(styleWidth, w) ||
3664 GetIntrinsicCoord(styleWidth, aRenderingContext, aFrame,
3665 PROP_WIDTH, w)) {
3666 result = AddPercents(aType, w + coordOutsideWidth, pctOutsideWidth);
3667 }
3668 else if (aType == MIN_WIDTH &&
3669 // The only cases of coord-percent-calc() units that
3670 // GetAbsoluteCoord didn't handle are percent and calc()s
3671 // containing percent.
3672 styleWidth.IsCoordPercentCalcUnit() &&
3673 aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
3674 // A percentage width on replaced elements means they can shrink to 0.
3675 result = 0; // let |min| handle padding/border/margin
3676 }
3677 else {
3678 // NOTE: We could really do a lot better for percents and for some
3679 // cases of calc() containing percent (certainly including any where
3680 // the coefficient on the percent is positive and there are no max()
3681 // expressions). However, doing better for percents wouldn't be
3682 // backwards compatible.
3683 result = AddPercents(aType, result, pctTotal);
3684 }
3686 if (haveFixedMaxWidth ||
3687 GetIntrinsicCoord(styleMaxWidth, aRenderingContext, aFrame,
3688 PROP_MAX_WIDTH, maxw)) {
3689 maxw = AddPercents(aType, maxw + coordOutsideWidth, pctOutsideWidth);
3690 if (result > maxw)
3691 result = maxw;
3692 }
3694 if (haveFixedMinWidth ||
3695 GetIntrinsicCoord(styleMinWidth, aRenderingContext, aFrame,
3696 PROP_MIN_WIDTH, minw)) {
3697 minw = AddPercents(aType, minw + coordOutsideWidth, pctOutsideWidth);
3698 if (result < minw)
3699 result = minw;
3700 }
3702 min = AddPercents(aType, min, pctTotal);
3703 if (result < min)
3704 result = min;
3706 const nsStyleDisplay *disp = aFrame->StyleDisplay();
3707 if (aFrame->IsThemed(disp)) {
3708 nsIntSize size(0, 0);
3709 bool canOverride = true;
3710 nsPresContext *presContext = aFrame->PresContext();
3711 presContext->GetTheme()->
3712 GetMinimumWidgetSize(aRenderingContext, aFrame, disp->mAppearance,
3713 &size, &canOverride);
3715 nscoord themeWidth = presContext->DevPixelsToAppUnits(size.width);
3717 // GMWS() returns a border-box width
3718 themeWidth += offsets.hMargin;
3719 themeWidth = AddPercents(aType, themeWidth, offsets.hPctMargin);
3721 if (themeWidth > result || !canOverride)
3722 result = themeWidth;
3723 }
3725 #ifdef DEBUG_INTRINSIC_WIDTH
3726 nsFrame::IndentBy(stderr, gNoiseIndent);
3727 static_cast<nsFrame*>(aFrame)->ListTag(stderr);
3728 printf_stderr(" %s intrinsic width for container is %d twips.\n",
3729 aType == MIN_WIDTH ? "min" : "pref", result);
3730 #endif
3732 return result;
3733 }
3735 /* static */ nscoord
3736 nsLayoutUtils::ComputeCBDependentValue(nscoord aPercentBasis,
3737 const nsStyleCoord& aCoord)
3738 {
3739 NS_WARN_IF_FALSE(aPercentBasis != NS_UNCONSTRAINEDSIZE,
3740 "have unconstrained width or height; this should only "
3741 "result from very large sizes, not attempts at intrinsic "
3742 "size calculation");
3744 if (aCoord.IsCoordPercentCalcUnit()) {
3745 return nsRuleNode::ComputeCoordPercentCalc(aCoord, aPercentBasis);
3746 }
3747 NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
3748 aCoord.GetUnit() == eStyleUnit_Auto,
3749 "unexpected width value");
3750 return 0;
3751 }
3753 /* static */ nscoord
3754 nsLayoutUtils::ComputeWidthValue(
3755 nsRenderingContext* aRenderingContext,
3756 nsIFrame* aFrame,
3757 nscoord aContainingBlockWidth,
3758 nscoord aContentEdgeToBoxSizing,
3759 nscoord aBoxSizingToMarginEdge,
3760 const nsStyleCoord& aCoord)
3761 {
3762 NS_PRECONDITION(aFrame, "non-null frame expected");
3763 NS_PRECONDITION(aRenderingContext, "non-null rendering context expected");
3764 NS_WARN_IF_FALSE(aContainingBlockWidth != NS_UNCONSTRAINEDSIZE,
3765 "have unconstrained width; this should only result from "
3766 "very large sizes, not attempts at intrinsic width "
3767 "calculation");
3768 NS_PRECONDITION(aContainingBlockWidth >= 0,
3769 "width less than zero");
3771 nscoord result;
3772 if (aCoord.IsCoordPercentCalcUnit()) {
3773 result = nsRuleNode::ComputeCoordPercentCalc(aCoord,
3774 aContainingBlockWidth);
3775 // The result of a calc() expression might be less than 0; we
3776 // should clamp at runtime (below). (Percentages and coords that
3777 // are less than 0 have already been dropped by the parser.)
3778 result -= aContentEdgeToBoxSizing;
3779 } else {
3780 MOZ_ASSERT(eStyleUnit_Enumerated == aCoord.GetUnit());
3781 // If aFrame is a container for font size inflation, then shrink
3782 // wrapping inside of it should not apply font size inflation.
3783 AutoMaybeDisableFontInflation an(aFrame);
3785 int32_t val = aCoord.GetIntValue();
3786 switch (val) {
3787 case NS_STYLE_WIDTH_MAX_CONTENT:
3788 result = aFrame->GetPrefWidth(aRenderingContext);
3789 NS_ASSERTION(result >= 0, "width less than zero");
3790 break;
3791 case NS_STYLE_WIDTH_MIN_CONTENT:
3792 result = aFrame->GetMinWidth(aRenderingContext);
3793 NS_ASSERTION(result >= 0, "width less than zero");
3794 break;
3795 case NS_STYLE_WIDTH_FIT_CONTENT:
3796 {
3797 nscoord pref = aFrame->GetPrefWidth(aRenderingContext),
3798 min = aFrame->GetMinWidth(aRenderingContext),
3799 fill = aContainingBlockWidth -
3800 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
3801 result = std::max(min, std::min(pref, fill));
3802 NS_ASSERTION(result >= 0, "width less than zero");
3803 }
3804 break;
3805 case NS_STYLE_WIDTH_AVAILABLE:
3806 result = aContainingBlockWidth -
3807 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
3808 }
3809 }
3811 return std::max(0, result);
3812 }
3814 /* static */ nscoord
3815 nsLayoutUtils::ComputeHeightDependentValue(
3816 nscoord aContainingBlockHeight,
3817 const nsStyleCoord& aCoord)
3818 {
3819 // XXXldb Some callers explicitly check aContainingBlockHeight
3820 // against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or
3821 // calc()s containing percents before calling this function.
3822 // However, it would be much more likely to catch problems without
3823 // the unit conditions.
3824 // XXXldb Many callers pass a non-'auto' containing block height when
3825 // according to CSS2.1 they should be passing 'auto'.
3826 NS_PRECONDITION(NS_AUTOHEIGHT != aContainingBlockHeight ||
3827 !aCoord.HasPercent(),
3828 "unexpected containing block height");
3830 if (aCoord.IsCoordPercentCalcUnit()) {
3831 return nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockHeight);
3832 }
3834 NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
3835 aCoord.GetUnit() == eStyleUnit_Auto,
3836 "unexpected height value");
3837 return 0;
3838 }
3840 /* static */ void
3841 nsLayoutUtils::MarkDescendantsDirty(nsIFrame *aSubtreeRoot)
3842 {
3843 nsAutoTArray<nsIFrame*, 4> subtrees;
3844 subtrees.AppendElement(aSubtreeRoot);
3846 // dirty descendants, iterating over subtrees that may include
3847 // additional subtrees associated with placeholders
3848 do {
3849 nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
3850 subtrees.RemoveElementAt(subtrees.Length() - 1);
3852 // Mark all descendants dirty (using an nsTArray stack rather than
3853 // recursion).
3854 // Note that nsHTMLReflowState::InitResizeFlags has some similar
3855 // code; see comments there for how and why it differs.
3856 nsAutoTArray<nsIFrame*, 32> stack;
3857 stack.AppendElement(subtreeRoot);
3859 do {
3860 nsIFrame *f = stack.ElementAt(stack.Length() - 1);
3861 stack.RemoveElementAt(stack.Length() - 1);
3863 f->MarkIntrinsicWidthsDirty();
3865 if (f->GetType() == nsGkAtoms::placeholderFrame) {
3866 nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
3867 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
3868 // We have another distinct subtree we need to mark.
3869 subtrees.AppendElement(oof);
3870 }
3871 }
3873 nsIFrame::ChildListIterator lists(f);
3874 for (; !lists.IsDone(); lists.Next()) {
3875 nsFrameList::Enumerator childFrames(lists.CurrentList());
3876 for (; !childFrames.AtEnd(); childFrames.Next()) {
3877 nsIFrame* kid = childFrames.get();
3878 stack.AppendElement(kid);
3879 }
3880 }
3881 } while (stack.Length() != 0);
3882 } while (subtrees.Length() != 0);
3883 }
3885 #define MULDIV(a,b,c) (nscoord(int64_t(a) * int64_t(b) / int64_t(c)))
3887 /* static */ nsSize
3888 nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
3889 nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
3890 const IntrinsicSize& aIntrinsicSize,
3891 nsSize aIntrinsicRatio, nsSize aCBSize,
3892 nsSize aMargin, nsSize aBorder, nsSize aPadding)
3893 {
3894 const nsStylePosition* stylePos = aFrame->StylePosition();
3896 // If we're a flex item, we'll compute our size a bit differently.
3897 const nsStyleCoord* widthStyleCoord = &(stylePos->mWidth);
3898 const nsStyleCoord* heightStyleCoord = &(stylePos->mHeight);
3900 bool isFlexItem = aFrame->IsFlexItem();
3901 bool isHorizontalFlexItem = false;
3903 if (isFlexItem) {
3904 // Flex items use their "flex-basis" property in place of their main-size
3905 // property (e.g. "width") for sizing purposes, *unless* they have
3906 // "flex-basis:auto", in which case they use their main-size property after
3907 // all.
3908 uint32_t flexDirection =
3909 aFrame->GetParent()->StylePosition()->mFlexDirection;
3910 isHorizontalFlexItem =
3911 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW ||
3912 flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE;
3914 // NOTE: The logic here should match the similar chunk for determining
3915 // widthStyleCoord and heightStyleCoord in nsFrame::ComputeSize().
3916 const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
3917 if (flexBasis->GetUnit() != eStyleUnit_Auto) {
3918 if (isHorizontalFlexItem) {
3919 widthStyleCoord = flexBasis;
3920 } else {
3921 // One caveat for vertical flex items: We don't support enumerated
3922 // values (e.g. "max-content") for height properties yet. So, if our
3923 // computed flex-basis is an enumerated value, we'll just behave as if
3924 // it were "auto", which means "use the main-size property after all"
3925 // (which is "height", in this case).
3926 // NOTE: Once we support intrinsic sizing keywords for "height",
3927 // we should remove this check.
3928 if (flexBasis->GetUnit() != eStyleUnit_Enumerated) {
3929 heightStyleCoord = flexBasis;
3930 }
3931 }
3932 }
3933 }
3935 // Handle intrinsic sizes and their interaction with
3936 // {min-,max-,}{width,height} according to the rules in
3937 // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
3939 // Note: throughout the following section of the function, I avoid
3940 // a * (b / c) because of its reduced accuracy relative to a * b / c
3941 // or (a * b) / c (which are equivalent).
3943 const bool isAutoWidth = widthStyleCoord->GetUnit() == eStyleUnit_Auto;
3944 const bool isAutoHeight = IsAutoHeight(*heightStyleCoord, aCBSize.height);
3946 nsSize boxSizingAdjust(0,0);
3947 switch (stylePos->mBoxSizing) {
3948 case NS_STYLE_BOX_SIZING_BORDER:
3949 boxSizingAdjust += aBorder;
3950 // fall through
3951 case NS_STYLE_BOX_SIZING_PADDING:
3952 boxSizingAdjust += aPadding;
3953 }
3954 nscoord boxSizingToMarginEdgeWidth =
3955 aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width;
3957 nscoord width, minWidth, maxWidth, height, minHeight, maxHeight;
3959 if (!isAutoWidth) {
3960 width = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
3961 aFrame, aCBSize.width, boxSizingAdjust.width,
3962 boxSizingToMarginEdgeWidth, *widthStyleCoord);
3963 }
3965 if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None &&
3966 !(isFlexItem && isHorizontalFlexItem)) {
3967 maxWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
3968 aFrame, aCBSize.width, boxSizingAdjust.width,
3969 boxSizingToMarginEdgeWidth, stylePos->mMaxWidth);
3970 } else {
3971 // NOTE: Flex items ignore their min & max sizing properties in their
3972 // flex container's main-axis. (Those properties get applied later in
3973 // the flexbox algorithm.)
3974 maxWidth = nscoord_MAX;
3975 }
3977 if (!(isFlexItem && isHorizontalFlexItem)) {
3978 minWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
3979 aFrame, aCBSize.width, boxSizingAdjust.width,
3980 boxSizingToMarginEdgeWidth, stylePos->mMinWidth);
3981 } else {
3982 // NOTE: Flex items ignore their min & max sizing properties in their
3983 // flex container's main-axis. (Those properties get applied later in
3984 // the flexbox algorithm.)
3985 minWidth = 0;
3986 }
3988 if (!isAutoHeight) {
3989 height = nsLayoutUtils::ComputeHeightValue(aCBSize.height,
3990 boxSizingAdjust.height,
3991 *heightStyleCoord);
3992 }
3994 if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.height) &&
3995 !(isFlexItem && !isHorizontalFlexItem)) {
3996 maxHeight = nsLayoutUtils::ComputeHeightValue(aCBSize.height,
3997 boxSizingAdjust.height,
3998 stylePos->mMaxHeight);
3999 } else {
4000 maxHeight = nscoord_MAX;
4001 }
4003 if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.height) &&
4004 !(isFlexItem && !isHorizontalFlexItem)) {
4005 minHeight = nsLayoutUtils::ComputeHeightValue(aCBSize.height,
4006 boxSizingAdjust.height,
4007 stylePos->mMinHeight);
4008 } else {
4009 minHeight = 0;
4010 }
4012 // Resolve percentage intrinsic width/height as necessary:
4014 NS_ASSERTION(aCBSize.width != NS_UNCONSTRAINEDSIZE,
4015 "Our containing block must not have unconstrained width!");
4017 bool hasIntrinsicWidth, hasIntrinsicHeight;
4018 nscoord intrinsicWidth, intrinsicHeight;
4020 if (aIntrinsicSize.width.GetUnit() == eStyleUnit_Coord) {
4021 hasIntrinsicWidth = true;
4022 intrinsicWidth = aIntrinsicSize.width.GetCoordValue();
4023 if (intrinsicWidth < 0)
4024 intrinsicWidth = 0;
4025 } else {
4026 NS_ASSERTION(aIntrinsicSize.width.GetUnit() == eStyleUnit_None,
4027 "unexpected unit");
4028 hasIntrinsicWidth = false;
4029 intrinsicWidth = 0;
4030 }
4032 if (aIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
4033 hasIntrinsicHeight = true;
4034 intrinsicHeight = aIntrinsicSize.height.GetCoordValue();
4035 if (intrinsicHeight < 0)
4036 intrinsicHeight = 0;
4037 } else {
4038 NS_ASSERTION(aIntrinsicSize.height.GetUnit() == eStyleUnit_None,
4039 "unexpected unit");
4040 hasIntrinsicHeight = false;
4041 intrinsicHeight = 0;
4042 }
4044 NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0,
4045 "Intrinsic ratio has a negative component!");
4047 // Now calculate the used values for width and height:
4049 if (isAutoWidth) {
4050 if (isAutoHeight) {
4052 // 'auto' width, 'auto' height
4054 // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
4056 nscoord tentWidth, tentHeight;
4058 if (hasIntrinsicWidth) {
4059 tentWidth = intrinsicWidth;
4060 } else if (hasIntrinsicHeight && aIntrinsicRatio.height > 0) {
4061 tentWidth = MULDIV(intrinsicHeight, aIntrinsicRatio.width, aIntrinsicRatio.height);
4062 } else if (aIntrinsicRatio.width > 0) {
4063 tentWidth = aCBSize.width - boxSizingToMarginEdgeWidth; // XXX scrollbar?
4064 if (tentWidth < 0) tentWidth = 0;
4065 } else {
4066 tentWidth = nsPresContext::CSSPixelsToAppUnits(300);
4067 }
4069 if (hasIntrinsicHeight) {
4070 tentHeight = intrinsicHeight;
4071 } else if (aIntrinsicRatio.width > 0) {
4072 tentHeight = MULDIV(tentWidth, aIntrinsicRatio.height, aIntrinsicRatio.width);
4073 } else {
4074 tentHeight = nsPresContext::CSSPixelsToAppUnits(150);
4075 }
4077 return ComputeAutoSizeWithIntrinsicDimensions(minWidth, minHeight,
4078 maxWidth, maxHeight,
4079 tentWidth, tentHeight);
4080 } else {
4082 // 'auto' width, non-'auto' height
4083 height = NS_CSS_MINMAX(height, minHeight, maxHeight);
4084 if (aIntrinsicRatio.height > 0) {
4085 width = MULDIV(height, aIntrinsicRatio.width, aIntrinsicRatio.height);
4086 } else if (hasIntrinsicWidth) {
4087 width = intrinsicWidth;
4088 } else {
4089 width = nsPresContext::CSSPixelsToAppUnits(300);
4090 }
4091 width = NS_CSS_MINMAX(width, minWidth, maxWidth);
4093 }
4094 } else {
4095 if (isAutoHeight) {
4097 // non-'auto' width, 'auto' height
4098 width = NS_CSS_MINMAX(width, minWidth, maxWidth);
4099 if (aIntrinsicRatio.width > 0) {
4100 height = MULDIV(width, aIntrinsicRatio.height, aIntrinsicRatio.width);
4101 } else if (hasIntrinsicHeight) {
4102 height = intrinsicHeight;
4103 } else {
4104 height = nsPresContext::CSSPixelsToAppUnits(150);
4105 }
4106 height = NS_CSS_MINMAX(height, minHeight, maxHeight);
4108 } else {
4110 // non-'auto' width, non-'auto' height
4111 width = NS_CSS_MINMAX(width, minWidth, maxWidth);
4112 height = NS_CSS_MINMAX(height, minHeight, maxHeight);
4114 }
4115 }
4117 return nsSize(width, height);
4118 }
4120 nsSize
4121 nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth, nscoord minHeight,
4122 nscoord maxWidth, nscoord maxHeight,
4123 nscoord tentWidth, nscoord tentHeight)
4124 {
4125 // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:
4127 if (minWidth > maxWidth)
4128 maxWidth = minWidth;
4129 if (minHeight > maxHeight)
4130 maxHeight = minHeight;
4132 nscoord heightAtMaxWidth, heightAtMinWidth,
4133 widthAtMaxHeight, widthAtMinHeight;
4135 if (tentWidth > 0) {
4136 heightAtMaxWidth = MULDIV(maxWidth, tentHeight, tentWidth);
4137 if (heightAtMaxWidth < minHeight)
4138 heightAtMaxWidth = minHeight;
4139 heightAtMinWidth = MULDIV(minWidth, tentHeight, tentWidth);
4140 if (heightAtMinWidth > maxHeight)
4141 heightAtMinWidth = maxHeight;
4142 } else {
4143 heightAtMaxWidth = heightAtMinWidth = NS_CSS_MINMAX(tentHeight, minHeight, maxHeight);
4144 }
4146 if (tentHeight > 0) {
4147 widthAtMaxHeight = MULDIV(maxHeight, tentWidth, tentHeight);
4148 if (widthAtMaxHeight < minWidth)
4149 widthAtMaxHeight = minWidth;
4150 widthAtMinHeight = MULDIV(minHeight, tentWidth, tentHeight);
4151 if (widthAtMinHeight > maxWidth)
4152 widthAtMinHeight = maxWidth;
4153 } else {
4154 widthAtMaxHeight = widthAtMinHeight = NS_CSS_MINMAX(tentWidth, minWidth, maxWidth);
4155 }
4157 // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :
4159 nscoord width, height;
4161 if (tentWidth > maxWidth) {
4162 if (tentHeight > maxHeight) {
4163 if (int64_t(maxWidth) * int64_t(tentHeight) <=
4164 int64_t(maxHeight) * int64_t(tentWidth)) {
4165 width = maxWidth;
4166 height = heightAtMaxWidth;
4167 } else {
4168 width = widthAtMaxHeight;
4169 height = maxHeight;
4170 }
4171 } else {
4172 // This also covers "(w > max-width) and (h < min-height)" since in
4173 // that case (max-width/w < 1), and with (h < min-height):
4174 // max(max-width * h/w, min-height) == min-height
4175 width = maxWidth;
4176 height = heightAtMaxWidth;
4177 }
4178 } else if (tentWidth < minWidth) {
4179 if (tentHeight < minHeight) {
4180 if (int64_t(minWidth) * int64_t(tentHeight) <=
4181 int64_t(minHeight) * int64_t(tentWidth)) {
4182 width = widthAtMinHeight;
4183 height = minHeight;
4184 } else {
4185 width = minWidth;
4186 height = heightAtMinWidth;
4187 }
4188 } else {
4189 // This also covers "(w < min-width) and (h > max-height)" since in
4190 // that case (min-width/w > 1), and with (h > max-height):
4191 // min(min-width * h/w, max-height) == max-height
4192 width = minWidth;
4193 height = heightAtMinWidth;
4194 }
4195 } else {
4196 if (tentHeight > maxHeight) {
4197 width = widthAtMaxHeight;
4198 height = maxHeight;
4199 } else if (tentHeight < minHeight) {
4200 width = widthAtMinHeight;
4201 height = minHeight;
4202 } else {
4203 width = tentWidth;
4204 height = tentHeight;
4205 }
4206 }
4208 return nsSize(width, height);
4209 }
4211 /* static */ nscoord
4212 nsLayoutUtils::MinWidthFromInline(nsIFrame* aFrame,
4213 nsRenderingContext* aRenderingContext)
4214 {
4215 NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),
4216 "should not be container for font size inflation");
4218 nsIFrame::InlineMinWidthData data;
4219 DISPLAY_MIN_WIDTH(aFrame, data.prevLines);
4220 aFrame->AddInlineMinWidth(aRenderingContext, &data);
4221 data.ForceBreak(aRenderingContext);
4222 return data.prevLines;
4223 }
4225 /* static */ nscoord
4226 nsLayoutUtils::PrefWidthFromInline(nsIFrame* aFrame,
4227 nsRenderingContext* aRenderingContext)
4228 {
4229 NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),
4230 "should not be container for font size inflation");
4232 nsIFrame::InlinePrefWidthData data;
4233 DISPLAY_PREF_WIDTH(aFrame, data.prevLines);
4234 aFrame->AddInlinePrefWidth(aRenderingContext, &data);
4235 data.ForceBreak(aRenderingContext);
4236 return data.prevLines;
4237 }
4239 static nscolor
4240 DarkenColor(nscolor aColor)
4241 {
4242 uint16_t hue, sat, value;
4243 uint8_t alpha;
4245 // convert the RBG to HSV so we can get the lightness (which is the v)
4246 NS_RGB2HSV(aColor, hue, sat, value, alpha);
4248 // The goal here is to send white to black while letting colored
4249 // stuff stay colored... So we adopt the following approach.
4250 // Something with sat = 0 should end up with value = 0. Something
4251 // with a high sat can end up with a high value and it's ok.... At
4252 // the same time, we don't want to make things lighter. Do
4253 // something simple, since it seems to work.
4254 if (value > sat) {
4255 value = sat;
4256 // convert this color back into the RGB color space.
4257 NS_HSV2RGB(aColor, hue, sat, value, alpha);
4258 }
4259 return aColor;
4260 }
4262 // Check whether we should darken text/decoration colors. We need to do this if
4263 // background images and colors are being suppressed, because that means
4264 // light text will not be visible against the (presumed light-colored) background.
4265 static bool
4266 ShouldDarkenColors(nsPresContext* aPresContext)
4267 {
4268 return !aPresContext->GetBackgroundColorDraw() &&
4269 !aPresContext->GetBackgroundImageDraw();
4270 }
4272 nscolor
4273 nsLayoutUtils::GetColor(nsIFrame* aFrame, nsCSSProperty aProperty)
4274 {
4275 if (aProperty == eCSSProperty_color)
4276 {
4277 nscolor nativeColor = NS_RGB(0, 0, 0);
4278 if (GetNativeTextColor(aFrame, nativeColor))
4279 return nativeColor;
4280 }
4282 nscolor color = aFrame->GetVisitedDependentColor(aProperty);
4283 if (ShouldDarkenColors(aFrame->PresContext())) {
4284 color = DarkenColor(color);
4285 }
4287 return color;
4288 }
4290 bool
4291 nsLayoutUtils::GetNativeTextColor(nsIFrame* aFrame, nscolor& aColor)
4292 {
4293 nsPresContext *presContext = aFrame->PresContext();
4294 if (!presContext->IsChrome()) {
4295 // If native appearance was used to draw the background of the containing
4296 // frame, return a contrasting native foreground color instead of the
4297 // color from the element's style. This avoids a problem where black
4298 // text was displayed on a black background when a Windows theme such as
4299 // "High Contrast Black" was used. The background is drawn inside
4300 // nsNativeThemeWin::ClassicDrawWidgetBackground().
4301 //
4302 // Because both the background color and this foreground color are used
4303 // directly without exposing the colors via CSS computed styles, the
4304 // native colors are not leaked to content.
4305 nsIFrame* bgFrame =
4306 nsCSSRendering::FindNonTransparentBackgroundFrame(aFrame);
4307 if (bgFrame) {
4308 const nsStyleDisplay* displayData = bgFrame->StyleDisplay();
4309 uint8_t widgetType = displayData->mAppearance;
4310 nsITheme *theme = presContext->GetTheme();
4311 if (theme && widgetType && theme->ThemeSupportsWidget(presContext,
4312 bgFrame,
4313 widgetType)) {
4314 bool isDisabled = false;
4315 nsIContent* frameContent = bgFrame->GetContent();
4316 if (frameContent && frameContent->IsElement()) {
4317 EventStates es = frameContent->AsElement()->State();
4318 isDisabled = es.HasState(NS_EVENT_STATE_DISABLED);
4319 }
4321 if (NS_SUCCEEDED(LookAndFeel::GetColorForNativeAppearance(widgetType,
4322 isDisabled, &aColor))) {
4323 return true;
4324 }
4325 }
4326 }
4327 }
4329 return false;
4330 }
4332 gfxFloat
4333 nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
4334 nscoord aY, nscoord aAscent)
4335 {
4336 gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
4337 gfxFloat baseline = gfxFloat(aY) + aAscent;
4338 gfxRect putativeRect(0, baseline/appUnitsPerDevUnit, 1, 1);
4339 if (!aContext->UserToDevicePixelSnapped(putativeRect, true))
4340 return baseline;
4341 return aContext->DeviceToUser(putativeRect.TopLeft()).y * appUnitsPerDevUnit;
4342 }
4344 void
4345 nsLayoutUtils::DrawString(const nsIFrame* aFrame,
4346 nsRenderingContext* aContext,
4347 const char16_t* aString,
4348 int32_t aLength,
4349 nsPoint aPoint,
4350 nsStyleContext* aStyleContext)
4351 {
4352 nsresult rv = NS_ERROR_FAILURE;
4353 nsPresContext* presContext = aFrame->PresContext();
4354 if (presContext->BidiEnabled()) {
4355 nsBidiLevel level =
4356 nsBidiPresUtils::BidiLevelFromStyle(aStyleContext ?
4357 aStyleContext : aFrame->StyleContext());
4358 rv = nsBidiPresUtils::RenderText(aString, aLength, level,
4359 presContext, *aContext, *aContext,
4360 aPoint.x, aPoint.y);
4361 }
4362 if (NS_FAILED(rv))
4363 {
4364 aContext->SetTextRunRTL(false);
4365 aContext->DrawString(aString, aLength, aPoint.x, aPoint.y);
4366 }
4367 }
4369 nscoord
4370 nsLayoutUtils::GetStringWidth(const nsIFrame* aFrame,
4371 nsRenderingContext* aContext,
4372 const char16_t* aString,
4373 int32_t aLength)
4374 {
4375 nsPresContext* presContext = aFrame->PresContext();
4376 if (presContext->BidiEnabled()) {
4377 nsBidiLevel level =
4378 nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());
4379 return nsBidiPresUtils::MeasureTextWidth(aString, aLength,
4380 level, presContext, *aContext);
4381 }
4382 aContext->SetTextRunRTL(false);
4383 return aContext->GetWidth(aString, aLength);
4384 }
4386 /* static */ void
4387 nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
4388 nsRenderingContext* aContext,
4389 const nsRect& aTextRect,
4390 const nsRect& aDirtyRect,
4391 const nscolor& aForegroundColor,
4392 TextShadowCallback aCallback,
4393 void* aCallbackData)
4394 {
4395 const nsStyleText* textStyle = aFrame->StyleText();
4396 if (!textStyle->HasTextShadow())
4397 return;
4399 // Text shadow happens with the last value being painted at the back,
4400 // ie. it is painted first.
4401 gfxContext* aDestCtx = aContext->ThebesContext();
4402 for (uint32_t i = textStyle->mTextShadow->Length(); i > 0; --i) {
4403 nsCSSShadowItem* shadowDetails = textStyle->mTextShadow->ShadowAt(i - 1);
4404 nsPoint shadowOffset(shadowDetails->mXOffset,
4405 shadowDetails->mYOffset);
4406 nscoord blurRadius = std::max(shadowDetails->mRadius, 0);
4408 nsRect shadowRect(aTextRect);
4409 shadowRect.MoveBy(shadowOffset);
4411 nsPresContext* presCtx = aFrame->PresContext();
4412 nsContextBoxBlur contextBoxBlur;
4413 gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
4414 presCtx->AppUnitsPerDevPixel(),
4415 aDestCtx, aDirtyRect, nullptr);
4416 if (!shadowContext)
4417 continue;
4419 nscolor shadowColor;
4420 if (shadowDetails->mHasColor)
4421 shadowColor = shadowDetails->mColor;
4422 else
4423 shadowColor = aForegroundColor;
4425 // Conjure an nsRenderingContext from a gfxContext for drawing the text
4426 // to blur.
4427 nsRefPtr<nsRenderingContext> renderingContext = new nsRenderingContext();
4428 renderingContext->Init(presCtx->DeviceContext(), shadowContext);
4430 aDestCtx->Save();
4431 aDestCtx->NewPath();
4432 aDestCtx->SetColor(gfxRGBA(shadowColor));
4434 // The callback will draw whatever we want to blur as a shadow.
4435 aCallback(renderingContext, shadowOffset, shadowColor, aCallbackData);
4437 contextBoxBlur.DoPaint();
4438 aDestCtx->Restore();
4439 }
4440 }
4442 /* static */ nscoord
4443 nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
4444 nscoord aLineHeight)
4445 {
4446 nscoord fontAscent = aFontMetrics->MaxAscent();
4447 nscoord fontHeight = aFontMetrics->MaxHeight();
4449 nscoord leading = aLineHeight - fontHeight;
4450 return fontAscent + leading/2;
4451 }
4454 /* static */ bool
4455 nsLayoutUtils::GetFirstLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
4456 {
4457 LinePosition position;
4458 if (!GetFirstLinePosition(aFrame, &position))
4459 return false;
4460 *aResult = position.mBaseline;
4461 return true;
4462 }
4464 /* static */ bool
4465 nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
4466 LinePosition* aResult)
4467 {
4468 const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
4469 if (!block) {
4470 // For the first-line baseline we also have to check for a table, and if
4471 // so, use the baseline of its first row.
4472 nsIAtom* fType = aFrame->GetType();
4473 if (fType == nsGkAtoms::tableOuterFrame) {
4474 aResult->mTop = 0;
4475 aResult->mBaseline = aFrame->GetBaseline();
4476 // This is what we want for the list bullet caller; not sure if
4477 // other future callers will want the same.
4478 aResult->mBottom = aFrame->GetSize().height;
4479 return true;
4480 }
4482 // For first-line baselines, we have to consider scroll frames.
4483 if (fType == nsGkAtoms::scrollFrame) {
4484 nsIScrollableFrame *sFrame = do_QueryFrame(const_cast<nsIFrame*>(aFrame));
4485 if (!sFrame) {
4486 NS_NOTREACHED("not scroll frame");
4487 }
4488 LinePosition kidPosition;
4489 if (GetFirstLinePosition(sFrame->GetScrolledFrame(), &kidPosition)) {
4490 // Consider only the border and padding that contributes to the
4491 // kid's position, not the scrolling, so we get the initial
4492 // position.
4493 *aResult = kidPosition + aFrame->GetUsedBorderAndPadding().top;
4494 return true;
4495 }
4496 return false;
4497 }
4499 if (fType == nsGkAtoms::fieldSetFrame) {
4500 LinePosition kidPosition;
4501 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
4502 // kid might be a legend frame here, but that's ok.
4503 if (GetFirstLinePosition(kid, &kidPosition)) {
4504 *aResult = kidPosition + kid->GetNormalPosition().y;
4505 return true;
4506 }
4507 return false;
4508 }
4510 // No baseline.
4511 return false;
4512 }
4514 for (nsBlockFrame::const_line_iterator line = block->begin_lines(),
4515 line_end = block->end_lines();
4516 line != line_end; ++line) {
4517 if (line->IsBlock()) {
4518 nsIFrame *kid = line->mFirstChild;
4519 LinePosition kidPosition;
4520 if (GetFirstLinePosition(kid, &kidPosition)) {
4521 *aResult = kidPosition + kid->GetNormalPosition().y;
4522 return true;
4523 }
4524 } else {
4525 // XXX Is this the right test? We have some bogus empty lines
4526 // floating around, but IsEmpty is perhaps too weak.
4527 if (line->BSize() != 0 || !line->IsEmpty()) {
4528 nscoord top = line->BStart();
4529 aResult->mTop = top;
4530 aResult->mBaseline = top + line->GetAscent();
4531 aResult->mBottom = top + line->BSize();
4532 return true;
4533 }
4534 }
4535 }
4536 return false;
4537 }
4539 /* static */ bool
4540 nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
4541 {
4542 const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
4543 if (!block)
4544 // No baseline. (We intentionally don't descend into scroll frames.)
4545 return false;
4547 for (nsBlockFrame::const_reverse_line_iterator line = block->rbegin_lines(),
4548 line_end = block->rend_lines();
4549 line != line_end; ++line) {
4550 if (line->IsBlock()) {
4551 nsIFrame *kid = line->mFirstChild;
4552 nscoord kidBaseline;
4553 if (GetLastLineBaseline(kid, &kidBaseline)) {
4554 // Ignore relative positioning for baseline calculations
4555 *aResult = kidBaseline + kid->GetNormalPosition().y;
4556 return true;
4557 } else if (kid->GetType() == nsGkAtoms::scrollFrame) {
4558 // Use the bottom of the scroll frame.
4559 // XXX CSS2.1 really doesn't say what to do here.
4560 *aResult = kid->GetNormalPosition().y + kid->GetRect().height;
4561 return true;
4562 }
4563 } else {
4564 // XXX Is this the right test? We have some bogus empty lines
4565 // floating around, but IsEmpty is perhaps too weak.
4566 if (line->BSize() != 0 || !line->IsEmpty()) {
4567 *aResult = line->BStart() + line->GetAscent();
4568 return true;
4569 }
4570 }
4571 }
4572 return false;
4573 }
4575 static nscoord
4576 CalculateBlockContentBottom(nsBlockFrame* aFrame)
4577 {
4578 NS_PRECONDITION(aFrame, "null ptr");
4580 nscoord contentBottom = 0;
4582 for (nsBlockFrame::line_iterator line = aFrame->begin_lines(),
4583 line_end = aFrame->end_lines();
4584 line != line_end; ++line) {
4585 if (line->IsBlock()) {
4586 nsIFrame* child = line->mFirstChild;
4587 nscoord offset = child->GetNormalPosition().y;
4588 contentBottom = std::max(contentBottom,
4589 nsLayoutUtils::CalculateContentBottom(child) + offset);
4590 }
4591 else {
4592 contentBottom = std::max(contentBottom, line->BEnd());
4593 }
4594 }
4595 return contentBottom;
4596 }
4598 /* static */ nscoord
4599 nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame)
4600 {
4601 NS_PRECONDITION(aFrame, "null ptr");
4603 nscoord contentBottom = aFrame->GetRect().height;
4605 // We want scrollable overflow rather than visual because this
4606 // calculation is intended to affect layout.
4607 if (aFrame->GetScrollableOverflowRect().height > contentBottom) {
4608 nsIFrame::ChildListIDs skip(nsIFrame::kOverflowList |
4609 nsIFrame::kExcessOverflowContainersList |
4610 nsIFrame::kOverflowOutOfFlowList);
4611 nsBlockFrame* blockFrame = GetAsBlock(aFrame);
4612 if (blockFrame) {
4613 contentBottom =
4614 std::max(contentBottom, CalculateBlockContentBottom(blockFrame));
4615 skip |= nsIFrame::kPrincipalList;
4616 }
4617 nsIFrame::ChildListIterator lists(aFrame);
4618 for (; !lists.IsDone(); lists.Next()) {
4619 if (!skip.Contains(lists.CurrentID())) {
4620 nsFrameList::Enumerator childFrames(lists.CurrentList());
4621 for (; !childFrames.AtEnd(); childFrames.Next()) {
4622 nsIFrame* child = childFrames.get();
4623 nscoord offset = child->GetNormalPosition().y;
4624 contentBottom = std::max(contentBottom,
4625 CalculateContentBottom(child) + offset);
4626 }
4627 }
4628 }
4629 }
4630 return contentBottom;
4631 }
4633 /* static */ nsIFrame*
4634 nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame)
4635 {
4636 nsIFrame* layer;
4637 for (layer = aFrame; layer; layer = layer->GetParent()) {
4638 if (layer->IsPositioned() ||
4639 (layer->GetParent() &&
4640 layer->GetParent()->GetType() == nsGkAtoms::scrollFrame))
4641 break;
4642 }
4643 if (layer)
4644 return layer;
4645 return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();
4646 }
4648 GraphicsFilter
4649 nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame)
4650 {
4651 GraphicsFilter defaultFilter = GraphicsFilter::FILTER_GOOD;
4652 nsStyleContext *sc;
4653 if (nsCSSRendering::IsCanvasFrame(aForFrame)) {
4654 nsCSSRendering::FindBackground(aForFrame, &sc);
4655 } else {
4656 sc = aForFrame->StyleContext();
4657 }
4659 switch (sc->StyleSVG()->mImageRendering) {
4660 case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED:
4661 return GraphicsFilter::FILTER_FAST;
4662 case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY:
4663 return GraphicsFilter::FILTER_BEST;
4664 case NS_STYLE_IMAGE_RENDERING_CRISPEDGES:
4665 return GraphicsFilter::FILTER_NEAREST;
4666 default:
4667 return defaultFilter;
4668 }
4669 }
4671 /**
4672 * Given an image being drawn into an appunit coordinate system, and
4673 * a point in that coordinate system, map the point back into image
4674 * pixel space.
4675 * @param aSize the size of the image, in pixels
4676 * @param aDest the rectangle that the image is being mapped into
4677 * @param aPt a point in the same coordinate system as the rectangle
4678 */
4679 static gfxPoint
4680 MapToFloatImagePixels(const gfxSize& aSize,
4681 const gfxRect& aDest, const gfxPoint& aPt)
4682 {
4683 return gfxPoint(((aPt.x - aDest.X())*aSize.width)/aDest.Width(),
4684 ((aPt.y - aDest.Y())*aSize.height)/aDest.Height());
4685 }
4687 /**
4688 * Given an image being drawn into an pixel-based coordinate system, and
4689 * a point in image space, map the point into the pixel-based coordinate
4690 * system.
4691 * @param aSize the size of the image, in pixels
4692 * @param aDest the rectangle that the image is being mapped into
4693 * @param aPt a point in image space
4694 */
4695 static gfxPoint
4696 MapToFloatUserPixels(const gfxSize& aSize,
4697 const gfxRect& aDest, const gfxPoint& aPt)
4698 {
4699 return gfxPoint(aPt.x*aDest.Width()/aSize.width + aDest.X(),
4700 aPt.y*aDest.Height()/aSize.height + aDest.Y());
4701 }
4703 /* static */ gfxRect
4704 nsLayoutUtils::RectToGfxRect(const nsRect& aRect, int32_t aAppUnitsPerDevPixel)
4705 {
4706 return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel,
4707 gfxFloat(aRect.y) / aAppUnitsPerDevPixel,
4708 gfxFloat(aRect.width) / aAppUnitsPerDevPixel,
4709 gfxFloat(aRect.height) / aAppUnitsPerDevPixel);
4710 }
4712 struct SnappedImageDrawingParameters {
4713 // A transform from either device space or user space (depending on mResetCTM)
4714 // to image space
4715 gfxMatrix mUserSpaceToImageSpace;
4716 // A device-space, pixel-aligned rectangle to fill
4717 gfxRect mFillRect;
4718 // A pixel rectangle in tiled image space outside of which gfx should not
4719 // sample (using EXTEND_PAD as necessary)
4720 nsIntRect mSubimage;
4721 // Whether there's anything to draw at all
4722 bool mShouldDraw;
4723 // true iff the CTM of the rendering context needs to be reset to the
4724 // identity matrix before drawing
4725 bool mResetCTM;
4727 SnappedImageDrawingParameters()
4728 : mShouldDraw(false)
4729 , mResetCTM(false)
4730 {}
4732 SnappedImageDrawingParameters(const gfxMatrix& aUserSpaceToImageSpace,
4733 const gfxRect& aFillRect,
4734 const nsIntRect& aSubimage,
4735 bool aResetCTM)
4736 : mUserSpaceToImageSpace(aUserSpaceToImageSpace)
4737 , mFillRect(aFillRect)
4738 , mSubimage(aSubimage)
4739 , mShouldDraw(true)
4740 , mResetCTM(aResetCTM)
4741 {}
4742 };
4744 /**
4745 * Given a set of input parameters, compute certain output parameters
4746 * for drawing an image with the image snapping algorithm.
4747 * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
4748 *
4749 * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters
4750 */
4751 static SnappedImageDrawingParameters
4752 ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
4753 int32_t aAppUnitsPerDevPixel,
4754 const nsRect aDest,
4755 const nsRect aFill,
4756 const nsPoint aAnchor,
4757 const nsRect aDirty,
4758 const nsIntSize aImageSize)
4760 {
4761 if (aDest.IsEmpty() || aFill.IsEmpty() || !aImageSize.width || !aImageSize.height)
4762 return SnappedImageDrawingParameters();
4764 gfxRect devPixelDest =
4765 nsLayoutUtils::RectToGfxRect(aDest, aAppUnitsPerDevPixel);
4766 gfxRect devPixelFill =
4767 nsLayoutUtils::RectToGfxRect(aFill, aAppUnitsPerDevPixel);
4768 gfxRect devPixelDirty =
4769 nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel);
4771 gfxMatrix currentMatrix = aCtx->CurrentMatrix();
4772 gfxRect fill = devPixelFill;
4773 bool didSnap;
4774 // Snap even if we have a scale in the context. But don't snap if
4775 // we have something that's not translation+scale, or if the scale flips in
4776 // the X or Y direction, because snapped image drawing can't handle that yet.
4777 if (!currentMatrix.HasNonAxisAlignedTransform() &&
4778 currentMatrix.xx > 0.0 && currentMatrix.yy > 0.0 &&
4779 aCtx->UserToDevicePixelSnapped(fill, true)) {
4780 didSnap = true;
4781 if (fill.IsEmpty()) {
4782 return SnappedImageDrawingParameters();
4783 }
4784 } else {
4785 didSnap = false;
4786 fill = devPixelFill;
4787 }
4789 gfxSize imageSize(aImageSize.width, aImageSize.height);
4791 // Compute the set of pixels that would be sampled by an ideal rendering
4792 gfxPoint subimageTopLeft =
4793 MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft());
4794 gfxPoint subimageBottomRight =
4795 MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight());
4796 nsIntRect intSubimage;
4797 intSubimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
4798 NSToIntFloor(subimageTopLeft.y));
4799 intSubimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - intSubimage.x,
4800 NSToIntCeil(subimageBottomRight.y) - intSubimage.y);
4802 // Compute the anchor point and compute final fill rect.
4803 // This code assumes that pixel-based devices have one pixel per
4804 // device unit!
4805 gfxPoint anchorPoint(gfxFloat(aAnchor.x)/aAppUnitsPerDevPixel,
4806 gfxFloat(aAnchor.y)/aAppUnitsPerDevPixel);
4807 gfxPoint imageSpaceAnchorPoint =
4808 MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint);
4810 if (didSnap) {
4811 imageSpaceAnchorPoint.Round();
4812 anchorPoint = imageSpaceAnchorPoint;
4813 anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint);
4814 anchorPoint = currentMatrix.Transform(anchorPoint);
4815 anchorPoint.Round();
4817 // This form of Transform is safe to call since non-axis-aligned
4818 // transforms wouldn't be snapped.
4819 devPixelDirty = currentMatrix.Transform(devPixelDirty);
4820 }
4822 gfxFloat scaleX = imageSize.width*aAppUnitsPerDevPixel/aDest.width;
4823 gfxFloat scaleY = imageSize.height*aAppUnitsPerDevPixel/aDest.height;
4824 if (didSnap) {
4825 // We'll reset aCTX to the identity matrix before drawing, so we need to
4826 // adjust our scales to match.
4827 scaleX /= currentMatrix.xx;
4828 scaleY /= currentMatrix.yy;
4829 }
4830 gfxFloat translateX = imageSpaceAnchorPoint.x - anchorPoint.x*scaleX;
4831 gfxFloat translateY = imageSpaceAnchorPoint.y - anchorPoint.y*scaleY;
4832 gfxMatrix transform(scaleX, 0, 0, scaleY, translateX, translateY);
4834 gfxRect finalFillRect = fill;
4835 // If the user-space-to-image-space transform is not a straight
4836 // translation by integers, then filtering will occur, and
4837 // restricting the fill rect to the dirty rect would change the values
4838 // computed for edge pixels, which we can't allow.
4839 // Also, if didSnap is false then rounding out 'devPixelDirty' might not
4840 // produce pixel-aligned coordinates, which would also break the values
4841 // computed for edge pixels.
4842 if (didSnap && !transform.HasNonIntegerTranslation()) {
4843 devPixelDirty.RoundOut();
4844 finalFillRect = fill.Intersect(devPixelDirty);
4845 }
4846 if (finalFillRect.IsEmpty())
4847 return SnappedImageDrawingParameters();
4849 return SnappedImageDrawingParameters(transform, finalFillRect, intSubimage,
4850 didSnap);
4851 }
4854 static nsresult
4855 DrawImageInternal(nsRenderingContext* aRenderingContext,
4856 imgIContainer* aImage,
4857 GraphicsFilter aGraphicsFilter,
4858 const nsRect& aDest,
4859 const nsRect& aFill,
4860 const nsPoint& aAnchor,
4861 const nsRect& aDirty,
4862 const nsIntSize& aImageSize,
4863 const SVGImageContext* aSVGContext,
4864 uint32_t aImageFlags)
4865 {
4866 if (aDest.Contains(aFill)) {
4867 aImageFlags |= imgIContainer::FLAG_CLAMP;
4868 }
4869 int32_t appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel();
4870 gfxContext* ctx = aRenderingContext->ThebesContext();
4872 SnappedImageDrawingParameters drawingParams =
4873 ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill,
4874 aAnchor, aDirty, aImageSize);
4876 if (!drawingParams.mShouldDraw)
4877 return NS_OK;
4879 gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
4880 if (drawingParams.mResetCTM) {
4881 ctx->IdentityMatrix();
4882 }
4884 aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace,
4885 drawingParams.mFillRect, drawingParams.mSubimage, aImageSize,
4886 aSVGContext, imgIContainer::FRAME_CURRENT, aImageFlags);
4887 return NS_OK;
4888 }
4890 /* static */ void
4891 nsLayoutUtils::DrawPixelSnapped(nsRenderingContext* aRenderingContext,
4892 gfxDrawable* aDrawable,
4893 GraphicsFilter aFilter,
4894 const nsRect& aDest,
4895 const nsRect& aFill,
4896 const nsPoint& aAnchor,
4897 const nsRect& aDirty)
4898 {
4899 int32_t appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel();
4900 gfxContext* ctx = aRenderingContext->ThebesContext();
4901 gfxIntSize drawableSize = aDrawable->Size();
4902 nsIntSize imageSize(drawableSize.width, drawableSize.height);
4904 SnappedImageDrawingParameters drawingParams =
4905 ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill,
4906 aAnchor, aDirty, imageSize);
4908 if (!drawingParams.mShouldDraw)
4909 return;
4911 gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
4912 if (drawingParams.mResetCTM) {
4913 ctx->IdentityMatrix();
4914 }
4916 gfxRect sourceRect =
4917 drawingParams.mUserSpaceToImageSpace.Transform(drawingParams.mFillRect);
4918 gfxRect imageRect(0, 0, imageSize.width, imageSize.height);
4919 gfxRect subimage(drawingParams.mSubimage.x, drawingParams.mSubimage.y,
4920 drawingParams.mSubimage.width, drawingParams.mSubimage.height);
4922 NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
4923 "We must be allowed to sample *some* source pixels!");
4925 gfxUtils::DrawPixelSnapped(ctx, aDrawable,
4926 drawingParams.mUserSpaceToImageSpace, subimage,
4927 sourceRect, imageRect, drawingParams.mFillRect,
4928 gfxImageFormat::ARGB32, aFilter);
4929 }
4931 /* static */ nsresult
4932 nsLayoutUtils::DrawSingleUnscaledImage(nsRenderingContext* aRenderingContext,
4933 imgIContainer* aImage,
4934 GraphicsFilter aGraphicsFilter,
4935 const nsPoint& aDest,
4936 const nsRect* aDirty,
4937 uint32_t aImageFlags,
4938 const nsRect* aSourceArea)
4939 {
4940 nsIntSize imageSize;
4941 aImage->GetWidth(&imageSize.width);
4942 aImage->GetHeight(&imageSize.height);
4943 NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
4945 nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
4946 nsSize size(imageSize.width*appUnitsPerCSSPixel,
4947 imageSize.height*appUnitsPerCSSPixel);
4949 nsRect source;
4950 if (aSourceArea) {
4951 source = *aSourceArea;
4952 } else {
4953 source.SizeTo(size);
4954 }
4956 nsRect dest(aDest - source.TopLeft(), size);
4957 nsRect fill(aDest, source.Size());
4958 // Ensure that only a single image tile is drawn. If aSourceArea extends
4959 // outside the image bounds, we want to honor the aSourceArea-to-aDest
4960 // translation but we don't want to actually tile the image.
4961 fill.IntersectRect(fill, dest);
4962 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
4963 dest, fill, aDest, aDirty ? *aDirty : dest,
4964 imageSize, nullptr, aImageFlags);
4965 }
4967 /* static */ nsresult
4968 nsLayoutUtils::DrawSingleImage(nsRenderingContext* aRenderingContext,
4969 imgIContainer* aImage,
4970 GraphicsFilter aGraphicsFilter,
4971 const nsRect& aDest,
4972 const nsRect& aDirty,
4973 const SVGImageContext* aSVGContext,
4974 uint32_t aImageFlags,
4975 const nsRect* aSourceArea)
4976 {
4977 nsIntSize imageSize;
4978 if (aImage->GetType() == imgIContainer::TYPE_VECTOR) {
4979 // We choose a size for vector images that emulates a raster image which
4980 // is perfectly sized for the destination rect: each pixel in the image
4981 // maps exactly to a single pixel on-screen.
4982 nscoord appUnitsPerDevPx = aRenderingContext->AppUnitsPerDevPixel();
4983 imageSize.width = NSAppUnitsToIntPixels(aDest.width, appUnitsPerDevPx);
4984 imageSize.height = NSAppUnitsToIntPixels(aDest.height, appUnitsPerDevPx);
4985 } else {
4986 // Raster images have an intrinsic size, so we just use that.
4987 aImage->GetWidth(&imageSize.width);
4988 aImage->GetHeight(&imageSize.height);
4989 }
4990 NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE);
4992 nsRect source;
4993 if (aSourceArea) {
4994 source = *aSourceArea;
4995 } else {
4996 nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
4997 source.SizeTo(imageSize.width*appUnitsPerCSSPixel,
4998 imageSize.height*appUnitsPerCSSPixel);
4999 }
5001 nsRect dest = nsLayoutUtils::GetWholeImageDestination(imageSize, source,
5002 aDest);
5003 // Ensure that only a single image tile is drawn. If aSourceArea extends
5004 // outside the image bounds, we want to honor the aSourceArea-to-aDest
5005 // transform but we don't want to actually tile the image.
5006 nsRect fill;
5007 fill.IntersectRect(aDest, dest);
5008 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, dest, fill,
5009 fill.TopLeft(), aDirty, imageSize, aSVGContext, aImageFlags);
5010 }
5012 /* static */ void
5013 nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage,
5014 nsIntSize& aImageSize, /*outparam*/
5015 nsSize& aIntrinsicRatio, /*outparam*/
5016 bool& aGotWidth, /*outparam*/
5017 bool& aGotHeight /*outparam*/)
5018 {
5019 aGotWidth = NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width));
5020 aGotHeight = NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height));
5021 bool gotRatio = NS_SUCCEEDED(aImage->GetIntrinsicRatio(&aIntrinsicRatio));
5023 if (!(aGotWidth && aGotHeight) && !gotRatio) {
5024 // We hit an error (say, because the image failed to load or couldn't be
5025 // decoded) and should return zero size.
5026 aGotWidth = aGotHeight = true;
5027 aImageSize = nsIntSize(0, 0);
5028 aIntrinsicRatio = nsSize(0, 0);
5029 }
5030 }
5033 /* static */ nsresult
5034 nsLayoutUtils::DrawBackgroundImage(nsRenderingContext* aRenderingContext,
5035 imgIContainer* aImage,
5036 const nsIntSize& aImageSize,
5037 GraphicsFilter aGraphicsFilter,
5038 const nsRect& aDest,
5039 const nsRect& aFill,
5040 const nsPoint& aAnchor,
5041 const nsRect& aDirty,
5042 uint32_t aImageFlags)
5043 {
5044 PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage");
5046 if (UseBackgroundNearestFiltering()) {
5047 aGraphicsFilter = GraphicsFilter::FILTER_NEAREST;
5048 }
5050 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
5051 aDest, aFill, aAnchor, aDirty,
5052 aImageSize, nullptr, aImageFlags);
5053 }
5055 /* static */ nsresult
5056 nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext,
5057 imgIContainer* aImage,
5058 GraphicsFilter aGraphicsFilter,
5059 const nsRect& aDest,
5060 const nsRect& aFill,
5061 const nsPoint& aAnchor,
5062 const nsRect& aDirty,
5063 uint32_t aImageFlags)
5064 {
5065 nsIntSize imageSize;
5066 nsSize imageRatio;
5067 bool gotHeight, gotWidth;
5068 ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight);
5070 // XXX Dimensionless images shouldn't fall back to filled-area size -- the
5071 // caller should provide the image size, a la DrawBackgroundImage.
5072 if (gotWidth != gotHeight) {
5073 if (!gotWidth) {
5074 if (imageRatio.height != 0) {
5075 imageSize.width =
5076 NSCoordSaturatingNonnegativeMultiply(imageSize.height,
5077 float(imageRatio.width) /
5078 float(imageRatio.height));
5079 gotWidth = true;
5080 }
5081 } else {
5082 if (imageRatio.width != 0) {
5083 imageSize.height =
5084 NSCoordSaturatingNonnegativeMultiply(imageSize.width,
5085 float(imageRatio.height) /
5086 float(imageRatio.width));
5087 gotHeight = true;
5088 }
5089 }
5090 }
5092 if (!gotWidth) {
5093 imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFill.width);
5094 }
5095 if (!gotHeight) {
5096 imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFill.height);
5097 }
5099 return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
5100 aDest, aFill, aAnchor, aDirty,
5101 imageSize, nullptr, aImageFlags);
5102 }
5104 /* static */ nsRect
5105 nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize,
5106 const nsRect& aImageSourceArea,
5107 const nsRect& aDestArea)
5108 {
5109 double scaleX = double(aDestArea.width)/aImageSourceArea.width;
5110 double scaleY = double(aDestArea.height)/aImageSourceArea.height;
5111 nscoord destOffsetX = NSToCoordRound(aImageSourceArea.x*scaleX);
5112 nscoord destOffsetY = NSToCoordRound(aImageSourceArea.y*scaleY);
5113 nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
5114 nscoord wholeSizeX = NSToCoordRound(aWholeImageSize.width*appUnitsPerCSSPixel*scaleX);
5115 nscoord wholeSizeY = NSToCoordRound(aWholeImageSize.height*appUnitsPerCSSPixel*scaleY);
5116 return nsRect(aDestArea.TopLeft() - nsPoint(destOffsetX, destOffsetY),
5117 nsSize(wholeSizeX, wholeSizeY));
5118 }
5120 /* static */ already_AddRefed<imgIContainer>
5121 nsLayoutUtils::OrientImage(imgIContainer* aContainer,
5122 const nsStyleImageOrientation& aOrientation)
5123 {
5124 MOZ_ASSERT(aContainer, "Should have an image container");
5125 nsCOMPtr<imgIContainer> img(aContainer);
5127 if (aOrientation.IsFromImage()) {
5128 img = ImageOps::Orient(img, img->GetOrientation());
5129 } else if (!aOrientation.IsDefault()) {
5130 Angle angle = aOrientation.Angle();
5131 Flip flip = aOrientation.IsFlipped() ? Flip::Horizontal
5132 : Flip::Unflipped;
5133 img = ImageOps::Orient(img, Orientation(angle, flip));
5134 }
5136 return img.forget();
5137 }
5139 static bool NonZeroStyleCoord(const nsStyleCoord& aCoord)
5140 {
5141 if (aCoord.IsCoordPercentCalcUnit()) {
5142 // Since negative results are clamped to 0, check > 0.
5143 return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
5144 nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
5145 }
5147 return true;
5148 }
5150 /* static */ bool
5151 nsLayoutUtils::HasNonZeroCorner(const nsStyleCorners& aCorners)
5152 {
5153 NS_FOR_CSS_HALF_CORNERS(corner) {
5154 if (NonZeroStyleCoord(aCorners.Get(corner)))
5155 return true;
5156 }
5157 return false;
5158 }
5160 // aCorner is a "full corner" value, i.e. NS_CORNER_TOP_LEFT etc
5161 static bool IsCornerAdjacentToSide(uint8_t aCorner, css::Side aSide)
5162 {
5163 PR_STATIC_ASSERT((int)NS_SIDE_TOP == NS_CORNER_TOP_LEFT);
5164 PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == NS_CORNER_TOP_RIGHT);
5165 PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == NS_CORNER_BOTTOM_RIGHT);
5166 PR_STATIC_ASSERT((int)NS_SIDE_LEFT == NS_CORNER_BOTTOM_LEFT);
5167 PR_STATIC_ASSERT((int)NS_SIDE_TOP == ((NS_CORNER_TOP_RIGHT - 1)&3));
5168 PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == ((NS_CORNER_BOTTOM_RIGHT - 1)&3));
5169 PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == ((NS_CORNER_BOTTOM_LEFT - 1)&3));
5170 PR_STATIC_ASSERT((int)NS_SIDE_LEFT == ((NS_CORNER_TOP_LEFT - 1)&3));
5172 return aSide == aCorner || aSide == ((aCorner - 1)&3);
5173 }
5175 /* static */ bool
5176 nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners& aCorners,
5177 css::Side aSide)
5178 {
5179 PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_X/2 == NS_CORNER_TOP_LEFT);
5180 PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_Y/2 == NS_CORNER_TOP_LEFT);
5181 PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_X/2 == NS_CORNER_TOP_RIGHT);
5182 PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_Y/2 == NS_CORNER_TOP_RIGHT);
5183 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_X/2 == NS_CORNER_BOTTOM_RIGHT);
5184 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_Y/2 == NS_CORNER_BOTTOM_RIGHT);
5185 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_X/2 == NS_CORNER_BOTTOM_LEFT);
5186 PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_Y/2 == NS_CORNER_BOTTOM_LEFT);
5188 NS_FOR_CSS_HALF_CORNERS(corner) {
5189 // corner is a "half corner" value, so dividing by two gives us a
5190 // "full corner" value.
5191 if (NonZeroStyleCoord(aCorners.Get(corner)) &&
5192 IsCornerAdjacentToSide(corner/2, aSide))
5193 return true;
5194 }
5195 return false;
5196 }
5198 /* static */ nsTransparencyMode
5199 nsLayoutUtils::GetFrameTransparency(nsIFrame* aBackgroundFrame,
5200 nsIFrame* aCSSRootFrame) {
5201 if (aCSSRootFrame->StyleDisplay()->mOpacity < 1.0f)
5202 return eTransparencyTransparent;
5204 if (HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius))
5205 return eTransparencyTransparent;
5207 if (aCSSRootFrame->StyleDisplay()->mAppearance == NS_THEME_WIN_GLASS)
5208 return eTransparencyGlass;
5210 if (aCSSRootFrame->StyleDisplay()->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS)
5211 return eTransparencyBorderlessGlass;
5213 nsITheme::Transparency transparency;
5214 if (aCSSRootFrame->IsThemed(&transparency))
5215 return transparency == nsITheme::eTransparent
5216 ? eTransparencyTransparent
5217 : eTransparencyOpaque;
5219 // We need an uninitialized window to be treated as opaque because
5220 // doing otherwise breaks window display effects on some platforms,
5221 // specifically Vista. (bug 450322)
5222 if (aBackgroundFrame->GetType() == nsGkAtoms::viewportFrame &&
5223 !aBackgroundFrame->GetFirstPrincipalChild()) {
5224 return eTransparencyOpaque;
5225 }
5227 nsStyleContext* bgSC;
5228 if (!nsCSSRendering::FindBackground(aBackgroundFrame, &bgSC)) {
5229 return eTransparencyTransparent;
5230 }
5231 const nsStyleBackground* bg = bgSC->StyleBackground();
5232 if (NS_GET_A(bg->mBackgroundColor) < 255 ||
5233 // bottom layer's clip is used for the color
5234 bg->BottomLayer().mClip != NS_STYLE_BG_CLIP_BORDER)
5235 return eTransparencyTransparent;
5236 return eTransparencyOpaque;
5237 }
5239 static bool IsPopupFrame(nsIFrame* aFrame)
5240 {
5241 // aFrame is a popup it's the list control frame dropdown for a combobox.
5242 nsIAtom* frameType = aFrame->GetType();
5243 if (frameType == nsGkAtoms::listControlFrame) {
5244 nsListControlFrame* lcf = static_cast<nsListControlFrame*>(aFrame);
5245 return lcf->IsInDropDownMode();
5246 }
5248 // ... or if it's a XUL menupopup frame.
5249 return frameType == nsGkAtoms::menuPopupFrame;
5250 }
5252 /* static */ bool
5253 nsLayoutUtils::IsPopup(nsIFrame* aFrame)
5254 {
5255 // Optimization: the frame can't possibly be a popup if it has no view.
5256 if (!aFrame->HasView()) {
5257 NS_ASSERTION(!IsPopupFrame(aFrame), "popup frame must have a view");
5258 return false;
5259 }
5260 return IsPopupFrame(aFrame);
5261 }
5263 /* static */ nsIFrame*
5264 nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame)
5265 {
5266 // We could use GetRootPresContext() here if the
5267 // NS_FRAME_IN_POPUP frame bit is set.
5268 nsIFrame* f = aFrame;
5269 for (;;) {
5270 if (!f->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
5271 f = f->PresContext()->FrameManager()->GetRootFrame();
5272 } else if (IsPopup(f)) {
5273 return f;
5274 }
5275 nsIFrame* parent = GetCrossDocParentFrame(f);
5276 if (!parent)
5277 return f;
5278 f = parent;
5279 }
5280 }
5282 /* static */ nsIFrame*
5283 nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame)
5284 {
5285 nsIFrame *f = aFrame;
5286 for (;;) {
5287 if (f->IsTransformed() || IsPopup(f)) {
5288 return f;
5289 }
5290 nsIFrame* parent = GetCrossDocParentFrame(f);
5291 if (!parent) {
5292 return f;
5293 }
5294 f = parent;
5295 }
5296 }
5298 /* static */ nsIFrame*
5299 nsLayoutUtils::GetTransformRootFrame(nsIFrame* aFrame)
5300 {
5301 nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
5302 while (parent && parent->Preserves3DChildren()) {
5303 parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
5304 }
5305 return parent;
5306 }
5308 /* static */ uint32_t
5309 nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
5310 const nsStyleFont* aStyleFont,
5311 const nsStyleText* aStyleText,
5312 nscoord aLetterSpacing)
5313 {
5314 uint32_t result = 0;
5315 if (aLetterSpacing != 0) {
5316 result |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
5317 }
5318 if (aStyleText->mControlCharacterVisibility == NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN) {
5319 result |= gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS;
5320 }
5321 switch (aStyleContext->StyleSVG()->mTextRendering) {
5322 case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
5323 result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
5324 break;
5325 case NS_STYLE_TEXT_RENDERING_AUTO:
5326 if (aStyleFont->mFont.size <
5327 aStyleContext->PresContext()->GetAutoQualityMinFontSize()) {
5328 result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
5329 }
5330 break;
5331 default:
5332 break;
5333 }
5334 return result;
5335 }
5337 /* static */ void
5338 nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
5339 nsRect* aHStrip, nsRect* aVStrip) {
5340 NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
5341 "expected rects at the same position");
5342 nsRect unionRect(aR1.x, aR1.y, std::max(aR1.width, aR2.width),
5343 std::max(aR1.height, aR2.height));
5344 nscoord VStripStart = std::min(aR1.width, aR2.width);
5345 nscoord HStripStart = std::min(aR1.height, aR2.height);
5346 *aVStrip = unionRect;
5347 aVStrip->x += VStripStart;
5348 aVStrip->width -= VStripStart;
5349 *aHStrip = unionRect;
5350 aHStrip->y += HStripStart;
5351 aHStrip->height -= HStripStart;
5352 }
5354 nsDeviceContext*
5355 nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindow* aWindow)
5356 {
5357 if (!aWindow) {
5358 return nullptr;
5359 }
5361 nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
5362 while (docShell) {
5363 // Now make sure our size is up to date. That will mean that the device
5364 // context does the right thing on multi-monitor systems when we return it to
5365 // the caller. It will also make sure that our prescontext has been created,
5366 // if we're supposed to have one.
5367 nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(docShell);
5368 if (!win) {
5369 // No reason to go on
5370 return nullptr;
5371 }
5373 win->EnsureSizeUpToDate();
5375 nsRefPtr<nsPresContext> presContext;
5376 docShell->GetPresContext(getter_AddRefs(presContext));
5377 if (presContext) {
5378 nsDeviceContext* context = presContext->DeviceContext();
5379 if (context) {
5380 return context;
5381 }
5382 }
5384 nsCOMPtr<nsIDocShellTreeItem> parentItem;
5385 docShell->GetParent(getter_AddRefs(parentItem));
5386 docShell = do_QueryInterface(parentItem);
5387 }
5389 return nullptr;
5390 }
5392 /* static */ bool
5393 nsLayoutUtils::IsReallyFixedPos(nsIFrame* aFrame)
5394 {
5395 NS_PRECONDITION(aFrame->GetParent(),
5396 "IsReallyFixedPos called on frame not in tree");
5397 NS_PRECONDITION(aFrame->StyleDisplay()->mPosition ==
5398 NS_STYLE_POSITION_FIXED,
5399 "IsReallyFixedPos called on non-'position:fixed' frame");
5401 nsIAtom *parentType = aFrame->GetParent()->GetType();
5402 return parentType == nsGkAtoms::viewportFrame ||
5403 parentType == nsGkAtoms::pageContentFrame;
5404 }
5406 nsLayoutUtils::SurfaceFromElementResult
5407 nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement,
5408 uint32_t aSurfaceFlags,
5409 DrawTarget* aTarget)
5410 {
5411 SurfaceFromElementResult result;
5412 nsresult rv;
5414 nsCOMPtr<imgIRequest> imgRequest;
5415 rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
5416 getter_AddRefs(imgRequest));
5417 if (NS_FAILED(rv) || !imgRequest)
5418 return result;
5420 uint32_t status;
5421 imgRequest->GetImageStatus(&status);
5422 if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) {
5423 // Spec says to use GetComplete, but that only works on
5424 // nsIDOMHTMLImageElement, and we support all sorts of other stuff
5425 // here. Do this for now pending spec clarification.
5426 result.mIsStillLoading = (status & imgIRequest::STATUS_ERROR) == 0;
5427 return result;
5428 }
5430 nsCOMPtr<nsIPrincipal> principal;
5431 rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal));
5432 if (NS_FAILED(rv))
5433 return result;
5435 nsCOMPtr<imgIContainer> imgContainer;
5436 rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
5437 if (NS_FAILED(rv))
5438 return result;
5440 uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
5442 uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME)
5443 ? (uint32_t) imgIContainer::FRAME_FIRST
5444 : (uint32_t) imgIContainer::FRAME_CURRENT;
5445 uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE;
5446 if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION)
5447 frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
5448 if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
5449 frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
5450 result.mIsPremultiplied = false;
5451 }
5453 int32_t imgWidth, imgHeight;
5454 rv = imgContainer->GetWidth(&imgWidth);
5455 nsresult rv2 = imgContainer->GetHeight(&imgHeight);
5456 if (NS_FAILED(rv) || NS_FAILED(rv2))
5457 return result;
5459 if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
5460 if (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) {
5461 frameFlags |= imgIContainer::FLAG_WANT_DATA_SURFACE;
5462 }
5463 result.mSourceSurface = imgContainer->GetFrame(whichFrame, frameFlags);
5464 if (!result.mSourceSurface) {
5465 return result;
5466 }
5467 // The surface we return is likely to be cached. We don't want to have to
5468 // convert to a surface that's compatible with aTarget each time it's used
5469 // (that would result in terrible performance), so we convert once here
5470 // upfront if aTarget is specified.
5471 if (aTarget) {
5472 RefPtr<SourceSurface> optSurface =
5473 aTarget->OptimizeSourceSurface(result.mSourceSurface);
5474 if (optSurface) {
5475 result.mSourceSurface = optSurface;
5476 }
5477 }
5478 } else {
5479 result.mDrawInfo.mImgContainer = imgContainer;
5480 result.mDrawInfo.mWhichFrame = whichFrame;
5481 result.mDrawInfo.mDrawingFlags = frameFlags;
5482 }
5484 int32_t corsmode;
5485 if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
5486 result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE);
5487 }
5489 result.mSize = gfxIntSize(imgWidth, imgHeight);
5490 result.mPrincipal = principal.forget();
5491 // no images, including SVG images, can load content from another domain.
5492 result.mIsWriteOnly = false;
5493 result.mImageRequest = imgRequest.forget();
5495 return result;
5496 }
5498 nsLayoutUtils::SurfaceFromElementResult
5499 nsLayoutUtils::SurfaceFromElement(HTMLImageElement *aElement,
5500 uint32_t aSurfaceFlags,
5501 DrawTarget* aTarget)
5502 {
5503 return SurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement),
5504 aSurfaceFlags, aTarget);
5505 }
5507 nsLayoutUtils::SurfaceFromElementResult
5508 nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement,
5509 uint32_t aSurfaceFlags,
5510 DrawTarget* aTarget)
5511 {
5512 SurfaceFromElementResult result;
5514 bool* isPremultiplied = nullptr;
5515 if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
5516 isPremultiplied = &result.mIsPremultiplied;
5517 }
5519 gfxIntSize size = aElement->GetSize();
5521 result.mSourceSurface = aElement->GetSurfaceSnapshot(isPremultiplied);
5522 if (!result.mSourceSurface) {
5523 // 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
5524 // draw nothing, so return an empty surface.
5525 DrawTarget *ref = aTarget ? aTarget : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
5526 RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
5527 SurfaceFormat::B8G8R8A8);
5528 if (dt) {
5529 result.mSourceSurface = dt->Snapshot();
5530 }
5531 } else if (aTarget) {
5532 RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
5533 if (opt) {
5534 result.mSourceSurface = opt;
5535 }
5536 }
5538 // Ensure that any future changes to the canvas trigger proper invalidation,
5539 // in case this is being used by -moz-element()
5540 aElement->MarkContextClean();
5542 result.mSize = size;
5543 result.mPrincipal = aElement->NodePrincipal();
5544 result.mIsWriteOnly = aElement->IsWriteOnly();
5546 return result;
5547 }
5549 nsLayoutUtils::SurfaceFromElementResult
5550 nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
5551 uint32_t aSurfaceFlags,
5552 DrawTarget* aTarget)
5553 {
5554 SurfaceFromElementResult result;
5556 NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!");
5558 uint16_t readyState;
5559 if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
5560 (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
5561 readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) {
5562 result.mIsStillLoading = true;
5563 return result;
5564 }
5566 // If it doesn't have a principal, just bail
5567 nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentPrincipal();
5568 if (!principal)
5569 return result;
5571 ImageContainer *container = aElement->GetImageContainer();
5572 if (!container)
5573 return result;
5575 mozilla::gfx::IntSize size;
5576 result.mSourceSurface = container->GetCurrentAsSourceSurface(&size);
5577 if (!result.mSourceSurface)
5578 return result;
5580 if (aTarget) {
5581 RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
5582 if (opt) {
5583 result.mSourceSurface = opt;
5584 }
5585 }
5587 result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE;
5588 result.mSize = ThebesIntSize(size);
5589 result.mPrincipal = principal.forget();
5590 result.mIsWriteOnly = false;
5592 return result;
5593 }
5595 nsLayoutUtils::SurfaceFromElementResult
5596 nsLayoutUtils::SurfaceFromElement(dom::Element* aElement,
5597 uint32_t aSurfaceFlags,
5598 DrawTarget* aTarget)
5599 {
5600 // If it's a <canvas>, we may be able to just grab its internal surface
5601 if (HTMLCanvasElement* canvas =
5602 HTMLCanvasElement::FromContentOrNull(aElement)) {
5603 return SurfaceFromElement(canvas, aSurfaceFlags, aTarget);
5604 }
5606 // Maybe it's <video>?
5607 if (HTMLVideoElement* video =
5608 HTMLVideoElement::FromContentOrNull(aElement)) {
5609 return SurfaceFromElement(video, aSurfaceFlags, aTarget);
5610 }
5612 // Finally, check if it's a normal image
5613 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
5615 if (!imageLoader) {
5616 return SurfaceFromElementResult();
5617 }
5619 return SurfaceFromElement(imageLoader, aSurfaceFlags, aTarget);
5620 }
5622 /* static */
5623 nsIContent*
5624 nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument* aDocument)
5625 {
5626 // If the document is in designMode we should return nullptr.
5627 if (!aDocument || aDocument->HasFlag(NODE_IS_EDITABLE)) {
5628 return nullptr;
5629 }
5631 // contenteditable only works with HTML document.
5632 // Note: Use nsIDOMHTMLDocument rather than nsIHTMLDocument for getting the
5633 // body node because nsIDOMHTMLDocument::GetBody() does something
5634 // additional work for some cases and nsEditor uses them.
5635 nsCOMPtr<nsIDOMHTMLDocument> domHTMLDoc = do_QueryInterface(aDocument);
5636 if (!domHTMLDoc) {
5637 return nullptr;
5638 }
5640 Element* rootElement = aDocument->GetRootElement();
5641 if (rootElement && rootElement->IsEditable()) {
5642 return rootElement;
5643 }
5645 // If there are no editable root element, check its <body> element.
5646 // Note that the body element could be <frameset> element.
5647 nsCOMPtr<nsIDOMHTMLElement> body;
5648 nsresult rv = domHTMLDoc->GetBody(getter_AddRefs(body));
5649 nsCOMPtr<nsIContent> content = do_QueryInterface(body);
5650 if (NS_SUCCEEDED(rv) && content && content->IsEditable()) {
5651 return content;
5652 }
5653 return nullptr;
5654 }
5656 #ifdef DEBUG
5657 /* static */ void
5658 nsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame* aContainer,
5659 const nsFrameList& aFrameList)
5660 {
5661 for (nsIFrame* f = aFrameList.FirstChild(); f ; f = f->GetNextSibling()) {
5662 // Check only later continuations of f; we deal with checking the
5663 // earlier continuations when we hit those earlier continuations in
5664 // the frame list.
5665 for (nsIFrame *c = f; (c = c->GetNextInFlow());) {
5666 NS_ASSERTION(c->GetParent() != aContainer ||
5667 !aFrameList.ContainsFrame(c),
5668 "Two continuations of the same frame in the same "
5669 "frame list");
5670 }
5671 }
5672 }
5674 // Is one of aFrame's ancestors a letter frame?
5675 static bool
5676 IsInLetterFrame(nsIFrame *aFrame)
5677 {
5678 for (nsIFrame *f = aFrame->GetParent(); f; f = f->GetParent()) {
5679 if (f->GetType() == nsGkAtoms::letterFrame) {
5680 return true;
5681 }
5682 }
5683 return false;
5684 }
5686 /* static */ void
5687 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot)
5688 {
5689 NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(),
5690 "frame tree not empty, but caller reported complete status");
5692 // Also assert that text frames map no text.
5693 int32_t start, end;
5694 nsresult rv = aSubtreeRoot->GetOffsets(start, end);
5695 NS_ASSERTION(NS_SUCCEEDED(rv), "GetOffsets failed");
5696 // In some cases involving :first-letter, we'll partially unlink a
5697 // continuation in the middle of a continuation chain from its
5698 // previous and next continuations before destroying it, presumably so
5699 // that we don't also destroy the later continuations. Once we've
5700 // done this, GetOffsets returns incorrect values.
5701 // For examples, see list of tests in
5702 // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29
5703 NS_ASSERTION(start == end || IsInLetterFrame(aSubtreeRoot),
5704 "frame tree not empty, but caller reported complete status");
5706 nsIFrame::ChildListIterator lists(aSubtreeRoot);
5707 for (; !lists.IsDone(); lists.Next()) {
5708 nsFrameList::Enumerator childFrames(lists.CurrentList());
5709 for (; !childFrames.AtEnd(); childFrames.Next()) {
5710 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames.get());
5711 }
5712 }
5713 }
5714 #endif
5716 static void
5717 GetFontFacesForFramesInner(nsIFrame* aFrame, nsFontFaceList* aFontFaceList)
5718 {
5719 NS_PRECONDITION(aFrame, "NULL frame pointer");
5721 if (aFrame->GetType() == nsGkAtoms::textFrame) {
5722 if (!aFrame->GetPrevContinuation()) {
5723 nsLayoutUtils::GetFontFacesForText(aFrame, 0, INT32_MAX, true,
5724 aFontFaceList);
5725 }
5726 return;
5727 }
5729 nsIFrame::ChildListID childLists[] = { nsIFrame::kPrincipalList,
5730 nsIFrame::kPopupList };
5731 for (size_t i = 0; i < ArrayLength(childLists); ++i) {
5732 nsFrameList children(aFrame->GetChildList(childLists[i]));
5733 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
5734 nsIFrame* child = e.get();
5735 child = nsPlaceholderFrame::GetRealFrameFor(child);
5736 GetFontFacesForFramesInner(child, aFontFaceList);
5737 }
5738 }
5739 }
5741 /* static */
5742 nsresult
5743 nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame,
5744 nsFontFaceList* aFontFaceList)
5745 {
5746 NS_PRECONDITION(aFrame, "NULL frame pointer");
5748 while (aFrame) {
5749 GetFontFacesForFramesInner(aFrame, aFontFaceList);
5750 aFrame = GetNextContinuationOrIBSplitSibling(aFrame);
5751 }
5753 return NS_OK;
5754 }
5756 /* static */
5757 nsresult
5758 nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame,
5759 int32_t aStartOffset, int32_t aEndOffset,
5760 bool aFollowContinuations,
5761 nsFontFaceList* aFontFaceList)
5762 {
5763 NS_PRECONDITION(aFrame, "NULL frame pointer");
5765 if (aFrame->GetType() != nsGkAtoms::textFrame) {
5766 return NS_OK;
5767 }
5769 nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame);
5770 do {
5771 int32_t fstart = std::max(curr->GetContentOffset(), aStartOffset);
5772 int32_t fend = std::min(curr->GetContentEnd(), aEndOffset);
5773 if (fstart >= fend) {
5774 curr = static_cast<nsTextFrame*>(curr->GetNextContinuation());
5775 continue;
5776 }
5778 // curr is overlapping with the offset we want
5779 gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
5780 gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
5781 NS_ENSURE_TRUE(textRun, NS_ERROR_OUT_OF_MEMORY);
5783 // include continuations in the range that share the same textrun
5784 nsTextFrame* next = nullptr;
5785 if (aFollowContinuations && fend < aEndOffset) {
5786 next = static_cast<nsTextFrame*>(curr->GetNextContinuation());
5787 while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
5788 fend = std::min(next->GetContentEnd(), aEndOffset);
5789 next = fend < aEndOffset ?
5790 static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr;
5791 }
5792 }
5794 uint32_t skipStart = iter.ConvertOriginalToSkipped(fstart);
5795 uint32_t skipEnd = iter.ConvertOriginalToSkipped(fend);
5796 aFontFaceList->AddFontsFromTextRun(textRun, skipStart, skipEnd - skipStart);
5797 curr = next;
5798 } while (aFollowContinuations && curr);
5800 return NS_OK;
5801 }
5803 /* static */
5804 size_t
5805 nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame,
5806 MallocSizeOf aMallocSizeOf,
5807 bool clear)
5808 {
5809 NS_PRECONDITION(aFrame, "NULL frame pointer");
5811 size_t total = 0;
5813 if (aFrame->GetType() == nsGkAtoms::textFrame) {
5814 nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
5815 for (uint32_t i = 0; i < 2; ++i) {
5816 gfxTextRun *run = textFrame->GetTextRun(
5817 (i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated);
5818 if (run) {
5819 if (clear) {
5820 run->ResetSizeOfAccountingFlags();
5821 } else {
5822 total += run->MaybeSizeOfIncludingThis(aMallocSizeOf);
5823 }
5824 }
5825 }
5826 return total;
5827 }
5829 nsAutoTArray<nsIFrame::ChildList,4> childListArray;
5830 aFrame->GetChildLists(&childListArray);
5832 for (nsIFrame::ChildListArrayIterator childLists(childListArray);
5833 !childLists.IsDone(); childLists.Next()) {
5834 for (nsFrameList::Enumerator e(childLists.CurrentList());
5835 !e.AtEnd(); e.Next()) {
5836 total += SizeOfTextRunsForFrames(e.get(), aMallocSizeOf, clear);
5837 }
5838 }
5839 return total;
5840 }
5842 /* static */
5843 void
5844 nsLayoutUtils::Initialize()
5845 {
5846 Preferences::AddUintVarCache(&sFontSizeInflationMaxRatio,
5847 "font.size.inflation.maxRatio");
5848 Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine,
5849 "font.size.inflation.emPerLine");
5850 Preferences::AddUintVarCache(&sFontSizeInflationMinTwips,
5851 "font.size.inflation.minTwips");
5852 Preferences::AddUintVarCache(&sFontSizeInflationLineThreshold,
5853 "font.size.inflation.lineThreshold");
5854 Preferences::AddIntVarCache(&sFontSizeInflationMappingIntercept,
5855 "font.size.inflation.mappingIntercept");
5856 Preferences::AddBoolVarCache(&sFontSizeInflationForceEnabled,
5857 "font.size.inflation.forceEnabled");
5858 Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess,
5859 "font.size.inflation.disabledInMasterProcess");
5860 Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled,
5861 "nglayout.debug.invalidation");
5862 Preferences::AddBoolVarCache(&sCSSVariablesEnabled,
5863 "layout.css.variables.enabled");
5864 Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled,
5865 "layout.interruptible-reflow.enabled");
5867 Preferences::RegisterCallback(GridEnabledPrefChangeCallback,
5868 GRID_ENABLED_PREF_NAME);
5869 GridEnabledPrefChangeCallback(GRID_ENABLED_PREF_NAME, nullptr);
5870 Preferences::RegisterCallback(StickyEnabledPrefChangeCallback,
5871 STICKY_ENABLED_PREF_NAME);
5872 StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME, nullptr);
5873 Preferences::RegisterCallback(TextAlignTrueEnabledPrefChangeCallback,
5874 TEXT_ALIGN_TRUE_ENABLED_PREF_NAME);
5875 TextAlignTrueEnabledPrefChangeCallback(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME,
5876 nullptr);
5878 nsComputedDOMStyle::RegisterPrefChangeCallbacks();
5879 }
5881 /* static */
5882 void
5883 nsLayoutUtils::Shutdown()
5884 {
5885 if (sContentMap) {
5886 delete sContentMap;
5887 sContentMap = nullptr;
5888 }
5890 Preferences::UnregisterCallback(GridEnabledPrefChangeCallback,
5891 GRID_ENABLED_PREF_NAME);
5892 Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback,
5893 STICKY_ENABLED_PREF_NAME);
5895 nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
5896 }
5898 /* static */
5899 void
5900 nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
5901 imgIRequest* aRequest,
5902 bool* aRequestRegistered)
5903 {
5904 if (!aPresContext) {
5905 return;
5906 }
5908 if (aRequestRegistered && *aRequestRegistered) {
5909 // Our request is already registered with the refresh driver, so
5910 // no need to register it again.
5911 return;
5912 }
5914 if (aRequest) {
5915 if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
5916 NS_WARNING("Unable to add image request");
5917 return;
5918 }
5920 if (aRequestRegistered) {
5921 *aRequestRegistered = true;
5922 }
5923 }
5924 }
5926 /* static */
5927 void
5928 nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
5929 imgIRequest* aRequest,
5930 bool* aRequestRegistered)
5931 {
5932 if (!aPresContext) {
5933 return;
5934 }
5936 if (aRequestRegistered && *aRequestRegistered) {
5937 // Our request is already registered with the refresh driver, so
5938 // no need to register it again.
5939 return;
5940 }
5942 if (aRequest) {
5943 nsCOMPtr<imgIContainer> image;
5944 if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
5945 // Check to verify that the image is animated. If so, then add it to the
5946 // list of images tracked by the refresh driver.
5947 bool isAnimated = false;
5948 nsresult rv = image->GetAnimated(&isAnimated);
5949 if (NS_SUCCEEDED(rv) && isAnimated) {
5950 if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
5951 NS_WARNING("Unable to add image request");
5952 return;
5953 }
5955 if (aRequestRegistered) {
5956 *aRequestRegistered = true;
5957 }
5958 }
5959 }
5960 }
5961 }
5963 /* static */
5964 void
5965 nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
5966 imgIRequest* aRequest,
5967 bool* aRequestRegistered)
5968 {
5969 if (!aPresContext) {
5970 return;
5971 }
5973 // Deregister our imgIRequest with the refresh driver to
5974 // complete tear-down, but only if it has been registered
5975 if (aRequestRegistered && !*aRequestRegistered) {
5976 return;
5977 }
5979 if (aRequest) {
5980 nsCOMPtr<imgIContainer> image;
5981 if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
5982 aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);
5984 if (aRequestRegistered) {
5985 *aRequestRegistered = false;
5986 }
5987 }
5988 }
5989 }
5991 /* static */
5992 void
5993 nsLayoutUtils::PostRestyleEvent(Element* aElement,
5994 nsRestyleHint aRestyleHint,
5995 nsChangeHint aMinChangeHint)
5996 {
5997 nsIDocument* doc = aElement->GetCurrentDoc();
5998 if (doc) {
5999 nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
6000 if (presShell) {
6001 presShell->GetPresContext()->RestyleManager()->PostRestyleEvent(
6002 aElement, aRestyleHint, aMinChangeHint);
6003 }
6004 }
6005 }
6007 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
6008 const nsAString& aValue)
6009 : mContent(aContent),
6010 mAttrName(aAttrName),
6011 mValue(aValue)
6012 {
6013 NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
6014 }
6016 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
6017 int32_t aValue)
6018 : mContent(aContent),
6019 mAttrName(aAttrName)
6020 {
6021 NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
6022 mValue.AppendInt(aValue);
6023 }
6025 NS_IMETHODIMP
6026 nsSetAttrRunnable::Run()
6027 {
6028 return mContent->SetAttr(kNameSpaceID_None, mAttrName, mValue, true);
6029 }
6031 nsUnsetAttrRunnable::nsUnsetAttrRunnable(nsIContent* aContent,
6032 nsIAtom* aAttrName)
6033 : mContent(aContent),
6034 mAttrName(aAttrName)
6035 {
6036 NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
6037 }
6039 NS_IMETHODIMP
6040 nsUnsetAttrRunnable::Run()
6041 {
6042 return mContent->UnsetAttr(kNameSpaceID_None, mAttrName, true);
6043 }
6045 /**
6046 * Compute the minimum font size inside of a container with the given
6047 * width, such that **when the user zooms the container to fill the full
6048 * width of the device**, the fonts satisfy our minima.
6049 */
6050 static nscoord
6051 MinimumFontSizeFor(nsPresContext* aPresContext, nscoord aContainerWidth)
6052 {
6053 nsIPresShell* presShell = aPresContext->PresShell();
6055 uint32_t emPerLine = presShell->FontSizeInflationEmPerLine();
6056 uint32_t minTwips = presShell->FontSizeInflationMinTwips();
6057 if (emPerLine == 0 && minTwips == 0) {
6058 return 0;
6059 }
6061 // Clamp the container width to the device dimensions
6062 nscoord iFrameWidth = aPresContext->GetVisibleArea().width;
6063 nscoord effectiveContainerWidth = std::min(iFrameWidth, aContainerWidth);
6065 nscoord byLine = 0, byInch = 0;
6066 if (emPerLine != 0) {
6067 byLine = effectiveContainerWidth / emPerLine;
6068 }
6069 if (minTwips != 0) {
6070 // REVIEW: Is this giving us app units and sizes *not* counting
6071 // viewport scaling?
6072 float deviceWidthInches =
6073 aPresContext->ScreenWidthInchesForFontInflation();
6074 byInch = NSToCoordRound(effectiveContainerWidth /
6075 (deviceWidthInches * 1440 /
6076 minTwips ));
6077 }
6078 return std::max(byLine, byInch);
6079 }
6081 /* static */ float
6082 nsLayoutUtils::FontSizeInflationInner(const nsIFrame *aFrame,
6083 nscoord aMinFontSize)
6084 {
6085 // Note that line heights should be inflated by the same ratio as the
6086 // font size of the same text; thus we operate only on the font size
6087 // even when we're scaling a line height.
6088 nscoord styleFontSize = aFrame->StyleFont()->mFont.size;
6089 if (styleFontSize <= 0) {
6090 // Never scale zero font size.
6091 return 1.0;
6092 }
6094 if (aMinFontSize <= 0) {
6095 // No need to scale.
6096 return 1.0;
6097 }
6099 // If between this current frame and its font inflation container there is a
6100 // non-inline element with fixed width or height, then we should not inflate
6101 // fonts for this frame.
6102 for (const nsIFrame* f = aFrame;
6103 f && !f->IsContainerForFontSizeInflation();
6104 f = f->GetParent()) {
6105 nsIContent* content = f->GetContent();
6106 nsIAtom* fType = f->GetType();
6107 // Also, if there is more than one frame corresponding to a single
6108 // content node, we want the outermost one.
6109 if (!(f->GetParent() && f->GetParent()->GetContent() == content) &&
6110 // ignore width/height on inlines since they don't apply
6111 fType != nsGkAtoms::inlineFrame &&
6112 // ignore width on radios and checkboxes since we enlarge them and
6113 // they have width/height in ua.css
6114 fType != nsGkAtoms::formControlFrame) {
6115 nsStyleCoord stylePosWidth = f->StylePosition()->mWidth;
6116 nsStyleCoord stylePosHeight = f->StylePosition()->mHeight;
6117 if (stylePosWidth.GetUnit() != eStyleUnit_Auto ||
6118 stylePosHeight.GetUnit() != eStyleUnit_Auto) {
6120 return 1.0;
6121 }
6122 }
6123 }
6125 int32_t interceptParam = nsLayoutUtils::FontSizeInflationMappingIntercept();
6126 float maxRatio = (float)nsLayoutUtils::FontSizeInflationMaxRatio() / 100.0f;
6128 float ratio = float(styleFontSize) / float(aMinFontSize);
6129 float inflationRatio;
6131 // Given a minimum inflated font size m, a specified font size s, we want to
6132 // find the inflated font size i and then return the ratio of i to s (i/s).
6133 if (interceptParam >= 0) {
6134 // Since the mapping intercept parameter P is greater than zero, we use it
6135 // to determine the point where our mapping function intersects the i=s
6136 // line. This means that we have an equation of the form:
6137 //
6138 // i = m + s·(P/2)/(1 + P/2), if s <= (1 + P/2)·m
6139 // i = s, if s >= (1 + P/2)·m
6141 float intercept = 1 + float(interceptParam)/2.0f;
6142 if (ratio >= intercept) {
6143 // If we're already at 1+P/2 or more times the minimum, don't scale.
6144 return 1.0;
6145 }
6147 // The point (intercept, intercept) is where the part of the i vs. s graph
6148 // that's not slope 1 meets the i=s line. (This part of the
6149 // graph is a line from (0, m), to that point). We calculate the
6150 // intersection point to be ((1+P/2)m, (1+P/2)m), where P is the
6151 // intercept parameter above. We then need to return i/s.
6152 inflationRatio = (1.0f + (ratio * (intercept - 1) / intercept)) / ratio;
6153 } else {
6154 // This is the case where P is negative. We essentially want to implement
6155 // the case for P=infinity here, so we make i = s + m, which means that
6156 // i/s = s/s + m/s = 1 + 1/ratio
6157 inflationRatio = 1 + 1.0f / ratio;
6158 }
6160 if (maxRatio > 1.0 && inflationRatio > maxRatio) {
6161 return maxRatio;
6162 } else {
6163 return inflationRatio;
6164 }
6165 }
6167 static bool
6168 ShouldInflateFontsForContainer(const nsIFrame *aFrame)
6169 {
6170 // We only want to inflate fonts for text that is in a place
6171 // with room to expand. The question is what the best heuristic for
6172 // that is...
6173 // For now, we're going to use NS_FRAME_IN_CONSTRAINED_HEIGHT, which
6174 // indicates whether the frame is inside something with a constrained
6175 // height (propagating down the tree), but the propagation stops when
6176 // we hit overflow-y: scroll or auto.
6177 const nsStyleText* styleText = aFrame->StyleText();
6179 return styleText->mTextSizeAdjust != NS_STYLE_TEXT_SIZE_ADJUST_NONE &&
6180 !(aFrame->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) &&
6181 // We also want to disable font inflation for containers that have
6182 // preformatted text.
6183 styleText->WhiteSpaceCanWrap(aFrame);
6184 }
6186 nscoord
6187 nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame *aFrame)
6188 {
6189 nsPresContext *presContext = aFrame->PresContext();
6190 if (!FontSizeInflationEnabled(presContext) ||
6191 presContext->mInflationDisabledForShrinkWrap) {
6192 return 0;
6193 }
6195 for (const nsIFrame *f = aFrame; f; f = f->GetParent()) {
6196 if (f->IsContainerForFontSizeInflation()) {
6197 if (!ShouldInflateFontsForContainer(f)) {
6198 return 0;
6199 }
6201 nsFontInflationData *data =
6202 nsFontInflationData::FindFontInflationDataFor(aFrame);
6203 // FIXME: The need to null-check here is sort of a bug, and might
6204 // lead to incorrect results.
6205 if (!data || !data->InflationEnabled()) {
6206 return 0;
6207 }
6209 return MinimumFontSizeFor(aFrame->PresContext(),
6210 data->EffectiveWidth());
6211 }
6212 }
6214 NS_ABORT_IF_FALSE(false, "root should always be container");
6216 return 0;
6217 }
6219 float
6220 nsLayoutUtils::FontSizeInflationFor(const nsIFrame *aFrame)
6221 {
6222 if (aFrame->IsSVGText()) {
6223 const nsIFrame* container = aFrame;
6224 while (container->GetType() != nsGkAtoms::svgTextFrame) {
6225 container = container->GetParent();
6226 }
6227 NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
6228 return
6229 static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
6230 }
6232 if (!FontSizeInflationEnabled(aFrame->PresContext())) {
6233 return 1.0f;
6234 }
6236 return FontSizeInflationInner(aFrame, InflationMinFontSizeFor(aFrame));
6237 }
6239 /* static */ bool
6240 nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext)
6241 {
6242 nsIPresShell* presShell = aPresContext->GetPresShell();
6244 if (!presShell) {
6245 return false;
6246 }
6248 return presShell->FontSizeInflationEnabled();
6249 }
6251 /* static */ nsRect
6252 nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame,
6253 const nsSize& aFrameSize)
6254 {
6255 nsCSSShadowArray* boxShadows = aFrame->StyleBorder()->mBoxShadow;
6256 if (!boxShadows) {
6257 return nsRect();
6258 }
6260 nsRect shadows;
6261 int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
6262 for (uint32_t i = 0; i < boxShadows->Length(); ++i) {
6263 nsRect tmpRect(nsPoint(0, 0), aFrameSize);
6264 nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
6266 // inset shadows are never painted outside the frame
6267 if (shadow->mInset)
6268 continue;
6270 tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
6271 tmpRect.Inflate(shadow->mSpread);
6272 tmpRect.Inflate(
6273 nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D));
6274 shadows.UnionRect(shadows, tmpRect);
6275 }
6276 return shadows;
6277 }
6279 /* static */ void
6280 nsLayoutUtils::UpdateImageVisibilityForFrame(nsIFrame* aImageFrame)
6281 {
6282 #ifdef DEBUG
6283 nsIAtom* type = aImageFrame->GetType();
6284 MOZ_ASSERT(type == nsGkAtoms::imageFrame ||
6285 type == nsGkAtoms::imageControlFrame ||
6286 type == nsGkAtoms::svgImageFrame, "wrong type of frame");
6287 #endif
6289 nsCOMPtr<nsIImageLoadingContent> content = do_QueryInterface(aImageFrame->GetContent());
6290 if (!content) {
6291 return;
6292 }
6294 nsIPresShell* presShell = aImageFrame->PresContext()->PresShell();
6295 if (presShell->AssumeAllImagesVisible()) {
6296 presShell->EnsureImageInVisibleList(content);
6297 return;
6298 }
6300 bool visible = true;
6301 nsIFrame* f = aImageFrame->GetParent();
6302 nsRect rect = aImageFrame->GetContentRectRelativeToSelf();
6303 nsIFrame* rectFrame = aImageFrame;
6304 while (f) {
6305 nsIScrollableFrame* sf = do_QueryFrame(f);
6306 if (sf) {
6307 nsRect transformedRect =
6308 nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
6309 if (!sf->IsRectNearlyVisible(transformedRect)) {
6310 visible = false;
6311 break;
6312 }
6313 // Move transformedRect to be contained in the scrollport as best we can
6314 // (it might not fit) to pretend that it was scrolled into view.
6315 nsRect scrollPort = sf->GetScrollPortRect();
6316 if (transformedRect.XMost() > scrollPort.XMost()) {
6317 transformedRect.x -= transformedRect.XMost() - scrollPort.XMost();
6318 }
6319 if (transformedRect.x < scrollPort.x) {
6320 transformedRect.x = scrollPort.x;
6321 }
6322 if (transformedRect.YMost() > scrollPort.YMost()) {
6323 transformedRect.y -= transformedRect.YMost() - scrollPort.YMost();
6324 }
6325 if (transformedRect.y < scrollPort.y) {
6326 transformedRect.y = scrollPort.y;
6327 }
6328 transformedRect.width = std::min(transformedRect.width, scrollPort.width);
6329 transformedRect.height = std::min(transformedRect.height, scrollPort.height);
6330 rect = transformedRect;
6331 rectFrame = f;
6332 }
6333 nsIFrame* parent = f->GetParent();
6334 if (!parent) {
6335 parent = nsLayoutUtils::GetCrossDocParentFrame(f);
6336 if (parent && parent->PresContext()->IsChrome()) {
6337 break;
6338 }
6339 }
6340 f = parent;
6341 }
6343 if (visible) {
6344 presShell->EnsureImageInVisibleList(content);
6345 } else {
6346 presShell->RemoveImageFromVisibleList(content);
6347 }
6348 }
6350 /* static */ nsSize
6351 nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame)
6352 {
6353 nsSize size(aFrame->GetSize());
6355 nsPresContext* presContext = aFrame->PresContext();
6356 nsIPresShell* presShell = presContext->PresShell();
6358 // See the comments in the code that calculates the root
6359 // composition bounds in RecordFrameMetrics.
6360 // TODO: Reuse that code here.
6361 bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument()
6362 && aFrame == presShell->GetRootScrollFrame();
6363 if (isRootContentDocRootScrollFrame) {
6364 if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
6365 if (nsView* view = rootFrame->GetView()) {
6366 nsSize viewSize = view->GetBounds().Size();
6367 nsIWidget* widget =
6368 #ifdef MOZ_WIDGET_ANDROID
6369 rootFrame->GetNearestWidget();
6370 #else
6371 view->GetWidget();
6372 #endif
6373 if (widget) {
6374 nsIntRect widgetBounds;
6375 widget->GetBounds(widgetBounds);
6376 int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
6377 size = nsSize(widgetBounds.width * auPerDevPixel,
6378 widgetBounds.height * auPerDevPixel);
6379 #ifdef MOZ_WIDGET_ANDROID
6380 if (viewSize.height < size.height) {
6381 size.height = viewSize.height;
6382 }
6383 #endif
6384 } else {
6385 size = viewSize;
6386 }
6387 }
6388 }
6389 }
6391 // Adjust composition bounds for the size of scroll bars.
6392 nsIScrollableFrame* scrollableFrame = aFrame->GetScrollTargetFrame();
6393 if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
6394 nsMargin margins = scrollableFrame->GetActualScrollbarSizes();
6395 size.width -= margins.LeftRight();
6396 size.height -= margins.TopBottom();
6397 }
6399 return size;
6400 }
6401 /* static */ CSSSize
6402 nsLayoutUtils::CalculateRootCompositionSize(nsIFrame* aFrame,
6403 bool aIsRootContentDocRootScrollFrame,
6404 const FrameMetrics& aMetrics)
6405 {
6407 if (aIsRootContentDocRootScrollFrame) {
6408 return ViewAs<LayerPixel>(ParentLayerSize(aMetrics.mCompositionBounds.Size()),
6409 PixelCastJustification::ParentLayerToLayerForRootComposition)
6410 / aMetrics.LayersPixelsPerCSSPixel();
6411 }
6412 nsPresContext* presContext = aFrame->PresContext();
6413 LayerSize rootCompositionSize;
6414 nsPresContext* rootPresContext =
6415 presContext->GetToplevelContentDocumentPresContext();
6416 if (!rootPresContext) {
6417 rootPresContext = presContext->GetRootPresContext();
6418 }
6419 nsIPresShell* rootPresShell = nullptr;
6420 if (rootPresContext) {
6421 // See the comments in the code that calculates the root
6422 // composition bounds in RecordFrameMetrics.
6423 // TODO: Reuse that code here.
6424 nsIPresShell* rootPresShell = rootPresContext->PresShell();
6425 if (nsIFrame* rootFrame = rootPresShell->GetRootFrame()) {
6426 if (nsView* view = rootFrame->GetView()) {
6427 LayoutDeviceToParentLayerScale parentResolution(
6428 rootPresShell->GetCumulativeResolution().width
6429 / rootPresShell->GetResolution().width);
6430 int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel();
6431 nsRect viewBounds = view->GetBounds();
6432 LayerSize viewSize = ViewAs<LayerPixel>(
6433 (LayoutDeviceRect::FromAppUnits(viewBounds, rootAUPerDevPixel)
6434 * parentResolution).Size(), PixelCastJustification::ParentLayerToLayerForRootComposition);
6435 nsIWidget* widget =
6436 #ifdef MOZ_WIDGET_ANDROID
6437 rootFrame->GetNearestWidget();
6438 #else
6439 view->GetWidget();
6440 #endif
6441 if (widget) {
6442 nsIntRect widgetBounds;
6443 widget->GetBounds(widgetBounds);
6444 rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size()));
6445 #ifdef MOZ_WIDGET_ANDROID
6446 if (viewSize.height < rootCompositionSize.height) {
6447 rootCompositionSize.height = viewSize.height;
6448 }
6449 #endif
6450 } else {
6451 rootCompositionSize = viewSize;
6452 }
6453 }
6454 }
6455 } else {
6456 nsIWidget* widget = aFrame->GetNearestWidget();
6457 nsIntRect widgetBounds;
6458 widget->GetBounds(widgetBounds);
6459 rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size()));
6460 }
6462 // Adjust composition size for the size of scroll bars.
6463 nsIFrame* rootRootScrollFrame = rootPresShell ? rootPresShell->GetRootScrollFrame() : nullptr;
6464 nsIScrollableFrame* rootScrollableFrame = nullptr;
6465 if (rootRootScrollFrame) {
6466 rootScrollableFrame = rootRootScrollFrame->GetScrollTargetFrame();
6467 }
6468 if (rootScrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
6469 CSSMargin margins = CSSMargin::FromAppUnits(rootScrollableFrame->GetActualScrollbarSizes());
6470 // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them.
6471 rootCompositionSize.width -= margins.LeftRight();
6472 rootCompositionSize.height -= margins.TopBottom();
6473 }
6475 return rootCompositionSize / aMetrics.LayersPixelsPerCSSPixel();
6476 }
6478 /* static */ nsRect
6479 nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame)
6480 {
6481 nsRect contentBounds;
6482 if (aScrollableFrame) {
6483 contentBounds = aScrollableFrame->GetScrollRange();
6485 // We ifndef the below code for Fennec because it requires special behaviour
6486 // on the APZC side. Because Fennec has it's own PZC implementation which doesn't
6487 // provide the special behaviour, this code will cause it to break. We can remove
6488 // the ifndef once Fennec switches over to APZ or if we add the special handling
6489 // to Fennec
6490 #ifndef MOZ_WIDGET_ANDROID
6491 nsPoint scrollPosition = aScrollableFrame->GetScrollPosition();
6492 if (aScrollableFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
6493 contentBounds.y = scrollPosition.y;
6494 contentBounds.height = 0;
6495 }
6496 if (aScrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
6497 contentBounds.x = scrollPosition.x;
6498 contentBounds.width = 0;
6499 }
6500 #endif
6502 contentBounds.width += aScrollableFrame->GetScrollPortRect().width;
6503 contentBounds.height += aScrollableFrame->GetScrollPortRect().height;
6504 } else {
6505 contentBounds = aRootFrame->GetRect();
6506 }
6507 return contentBounds;
6508 }
6510 /* static */ nsRect
6511 nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame* aFrame)
6512 {
6513 nsRect scrollableRect =
6514 CalculateScrollableRectForFrame(aFrame->GetScrollTargetFrame(),
6515 aFrame->PresContext()->PresShell()->GetRootFrame());
6516 nsSize compSize = CalculateCompositionSizeForFrame(aFrame);
6518 if (aFrame == aFrame->PresContext()->PresShell()->GetRootScrollFrame()) {
6519 // the composition size for the root scroll frame does not include the
6520 // local resolution, so we adjust.
6521 gfxSize res = aFrame->PresContext()->PresShell()->GetResolution();
6522 compSize.width = NSToCoordRound(compSize.width / ((float) res.width));
6523 compSize.height = NSToCoordRound(compSize.height / ((float) res.height));
6524 }
6526 if (scrollableRect.width < compSize.width) {
6527 scrollableRect.x = std::max(0,
6528 scrollableRect.x - (compSize.width - scrollableRect.width));
6529 scrollableRect.width = compSize.width;
6530 }
6532 if (scrollableRect.height < compSize.height) {
6533 scrollableRect.y = std::max(0,
6534 scrollableRect.y - (compSize.height - scrollableRect.height));
6535 scrollableRect.height = compSize.height;
6536 }
6537 return scrollableRect;
6538 }
6540 /* static */ bool
6541 nsLayoutUtils::WantSubAPZC()
6542 {
6543 // TODO Turn this on for inprocess OMTC on all platforms
6544 bool wantSubAPZC = gfxPrefs::APZSubframeEnabled();
6545 #ifdef MOZ_WIDGET_GONK
6546 if (XRE_GetProcessType() != GeckoProcessType_Content) {
6547 wantSubAPZC = false;
6548 }
6549 #endif
6550 return wantSubAPZC;
6551 }
6553 nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()
6554 // Use safe default values here
6555 : mIsWriteOnly(true)
6556 , mIsStillLoading(false)
6557 , mCORSUsed(false)
6558 , mIsPremultiplied(true)
6559 {
6560 }
6562 bool
6563 nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame)
6564 {
6565 return GetAsBlock(aFrame) && !aFrame->IsBlockWrapper();
6566 }
6568 bool
6569 nsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext* aPresContext)
6570 {
6571 return aPresContext->IsRootPaginatedDocument() &&
6572 (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
6573 aPresContext->Type() == nsPresContext::eContext_PageLayout);
6574 }
6576 AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame *aFrame)
6577 {
6578 // FIXME: Now that inflation calculations are based on the flow
6579 // root's NCA's (nearest common ancestor of its inflatable
6580 // descendants) width, we could probably disable inflation in
6581 // fewer cases than we currently do.
6582 if (aFrame->IsContainerForFontSizeInflation()) {
6583 mPresContext = aFrame->PresContext();
6584 mOldValue = mPresContext->mInflationDisabledForShrinkWrap;
6585 mPresContext->mInflationDisabledForShrinkWrap = true;
6586 } else {
6587 // indicate we have nothing to restore
6588 mPresContext = nullptr;
6589 }
6590 }
6592 AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation()
6593 {
6594 if (mPresContext) {
6595 mPresContext->mInflationDisabledForShrinkWrap = mOldValue;
6596 }
6597 }