layout/base/nsLayoutUtils.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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;
  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);
  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);
  1024     // else it's positioned and should have been on the 'id' child list.
  1025     NS_POSTCONDITION(found, "not in child list");
  1027 #endif
  1029   return id;
  1032 // static
  1033 nsIFrame*
  1034 nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame)
  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;
  1048   return nullptr;
  1051 // static
  1052 nsIFrame*
  1053 nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame)
  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;
  1065   return nullptr;
  1068 // static
  1069 nsIFrame*
  1070 nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame, nsIAtom* aFrameType)
  1072   for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
  1073     if (frame->GetType() == aFrameType) {
  1074       return frame;
  1077   return nullptr;
  1080 // static
  1081 nsIFrame*
  1082 nsLayoutUtils::GetStyleFrame(nsIFrame* aFrame)
  1084   if (aFrame->GetType() == nsGkAtoms::tableOuterFrame) {
  1085     nsIFrame* inner = aFrame->GetFirstPrincipalChild();
  1086     NS_ASSERTION(inner, "Outer table must have an inner");
  1087     return inner;
  1090   return aFrame;
  1093 nsIFrame*
  1094 nsLayoutUtils::GetStyleFrame(const nsIContent* aContent)
  1096   nsIFrame *frame = aContent->GetPrimaryFrame();
  1097   if (!frame) {
  1098     return nullptr;
  1101   return nsLayoutUtils::GetStyleFrame(frame);
  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;
  1116   return nullptr;
  1119 // static
  1120 bool
  1121 nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
  1122                                      nsIFrame* aFrame,
  1123                                      nsIAtom* aPseudoElement)
  1125   NS_PRECONDITION(aFrame, "Must have a frame");
  1126   NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
  1128   if (!aFrame->IsGeneratedContentFrame()) {
  1129     return false;
  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;
  1138   if (aContent && parent->GetContent() != aContent) {
  1139     return false;
  1142   return (aFrame->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore) ==
  1143     (aPseudoElement == nsCSSPseudoElements::before);
  1146 // static
  1147 nsIFrame*
  1148 nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame,
  1149                                       nsPoint* aExtraOffset)
  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();
  1164   v = v->GetParent(); // subdocumentframe's view
  1165   return v ? v->GetFrame() : nullptr;
  1168 // static
  1169 bool
  1170 nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
  1171                                              nsIFrame* aCommonAncestor)
  1173   if (aFrame == aAncestorFrame)
  1174     return false;
  1175   return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
  1178 // static
  1179 bool
  1180 nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
  1181                                        const nsIFrame* aCommonAncestor)
  1183   for (const nsIFrame* f = aFrame; f != aCommonAncestor;
  1184        f = GetCrossDocParentFrame(f)) {
  1185     if (f == aAncestorFrame)
  1186       return true;
  1188   return aCommonAncestor == aAncestorFrame;
  1191 // static
  1192 bool
  1193 nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
  1194                                      nsIFrame* aCommonAncestor)
  1196   if (aFrame == aAncestorFrame)
  1197     return false;
  1198   for (nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) {
  1199     if (f == aAncestorFrame)
  1200       return true;
  1202   return aCommonAncestor == aAncestorFrame;
  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)
  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);
  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;
  1227   nsAutoTArray<nsINode*, 32> content2Ancestors;
  1228   nsINode* c2;
  1229   for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetParentNode()) {
  1230     content2Ancestors.AppendElement(c2);
  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);
  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--;
  1250   if (last1 < 0) {
  1251     if (last2 < 0) {
  1252       NS_ASSERTION(aContent1 == aContent2, "internal error?");
  1253       return 0;
  1255     // aContent1 is an ancestor of aContent2
  1256     return aIf1Ancestor;
  1259   if (last2 < 0) {
  1260     // aContent2 is an ancestor of aContent1
  1261     return aIf2Ancestor;
  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;
  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;
  1282   return index1 - index2;
  1285 // static
  1286 nsIFrame*
  1287 nsLayoutUtils::FillAncestors(nsIFrame* aFrame,
  1288                              nsIFrame* aStopAtAncestor,
  1289                              nsTArray<nsIFrame*>* aAncestors)
  1291   while (aFrame && aFrame != aStopAtAncestor) {
  1292     aAncestors->AppendElement(aFrame);
  1293     aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
  1295   return aFrame;
  1298 // Return true if aFrame1 is after aFrame2
  1299 static bool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2)
  1301   nsIFrame* f = aFrame2;
  1302   do {
  1303     f = f->GetNextSibling();
  1304     if (f == aFrame1)
  1305       return true;
  1306   } while (f);
  1307   return false;
  1310 // static
  1311 int32_t
  1312 nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
  1313                                      nsIFrame* aFrame2,
  1314                                      int32_t aIf1Ancestor,
  1315                                      int32_t aIf2Ancestor,
  1316                                      nsIFrame* aCommonAncestor)
  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);
  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)
  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;
  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);
  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--;
  1365   if (last1 < 0) {
  1366     if (last2 < 0) {
  1367       NS_ASSERTION(aFrame1 == aFrame2, "internal error?");
  1368       return 0;
  1370     // aFrame1 is an ancestor of aFrame2
  1371     return aIf1Ancestor;
  1374   if (last2 < 0) {
  1375     // aFrame2 is an ancestor of aFrame1
  1376     return aIf2Ancestor;
  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;
  1390 // static
  1391 nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
  1392   if (!aFrame) {
  1393     return nullptr;
  1396   nsIFrame* next;
  1397   while ((next = aFrame->GetNextSibling()) != nullptr) {
  1398     aFrame = next;
  1400   return aFrame;
  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;
  1420       NS_ASSERTION(f, "Can't find a frame anywhere!");
  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;
  1429   return nullptr;
  1432 //static
  1433 nsIScrollableFrame*
  1434 nsLayoutUtils::GetScrollableFrameFor(const nsIFrame *aScrolledFrame)
  1436   nsIFrame *frame = aScrolledFrame->GetParent();
  1437   nsIScrollableFrame *sf = do_QueryFrame(frame);
  1438   return sf;
  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);
  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();
  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();
  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;
  1512     if (position->mOffset.GetTopUnit() == eStyleUnit_Auto &&
  1513         position->mOffset.GetBottomUnit() == eStyleUnit_Auto) {
  1514       fixedLayerMargins.top = -1;
  1518   aLayer->SetFixedPositionAnchor(anchor);
  1519   aLayer->SetFixedPositionMargins(fixedLayerMargins);
  1522 bool
  1523 nsLayoutUtils::ViewportHasDisplayPort(nsPresContext* aPresContext, nsRect* aDisplayPort)
  1525   nsIFrame* rootScrollFrame =
  1526     aPresContext->PresShell()->GetRootScrollFrame();
  1527   return rootScrollFrame &&
  1528     nsLayoutUtils::GetDisplayPort(rootScrollFrame->GetContent(), aDisplayPort);
  1531 bool
  1532 nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame, nsRect* aDisplayPort)
  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;
  1542   return ViewportHasDisplayPort(aFrame->PresContext(), aDisplayPort);
  1545 static nsIFrame*
  1546 GetAnimatedGeometryRootForFrame(nsIFrame* aFrame,
  1547                                 const nsIFrame* aStopAtAncestor)
  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;
  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;
  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;
  1587         return f;
  1588       } else {
  1589         stickyFrame = nullptr;
  1592     // Fixed-pos frames are parented by the viewport frame, which has no parent
  1593     if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) {
  1594       return f;
  1596     f = parent;
  1598   return f;
  1601 nsIFrame*
  1602 nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem,
  1603                                           nsDisplayListBuilder* aBuilder)
  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));
  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));
  1624   return GetAnimatedGeometryRootForFrame(f, aItem->ReferenceFrame());
  1627 // static
  1628 nsIScrollableFrame*
  1629 nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,
  1630                                                      Direction aDirection)
  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;
  1646   return nullptr;
  1649 // static
  1650 nsIScrollableFrame*
  1651 nsLayoutUtils::GetNearestScrollableFrame(nsIFrame* aFrame, uint32_t aFlags)
  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;
  1665   return nullptr;
  1668 // static
  1669 nsRect
  1670 nsLayoutUtils::GetScrolledRect(nsIFrame* aScrolledFrame,
  1671                                const nsRect& aScrolledFrameOverflowArea,
  1672                                const nsSize& aScrollPortSize,
  1673                                uint8_t aDirection)
  1675   nscoord x1 = aScrolledFrameOverflowArea.x,
  1676           x2 = aScrolledFrameOverflowArea.XMost(),
  1677           y1 = aScrolledFrameOverflowArea.y,
  1678           y2 = aScrolledFrameOverflowArea.YMost();
  1679   if (y1 < 0) {
  1680     y1 = 0;
  1682   if (aDirection != NS_STYLE_DIRECTION_RTL) {
  1683     if (x1 < 0) {
  1684       x1 = 0;
  1686   } else {
  1687     if (x2 > aScrollPortSize.width) {
  1688       x2 = aScrollPortSize.width;
  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;
  1699   return nsRect(x1, y1, x2 - x1, y2 - y1);
  1702 //static
  1703 bool
  1704 nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
  1705                               nsStyleContext* aStyleContext,
  1706                               nsCSSPseudoElements::Type aPseudoElement,
  1707                               nsPresContext* aPresContext)
  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);
  1717   return pseudoContext != nullptr;
  1720 nsPoint
  1721 nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame)
  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);
  1731 nsPoint
  1732 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
  1733                                              nsIFrame* aFrame)
  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);
  1751 nsPoint
  1752 nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
  1753                                              const nsIntPoint aPoint,
  1754                                              nsIFrame* aFrame)
  1756   if (!aFrame) {
  1757     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  1760   nsIWidget* widget = aEvent->AsGUIEvent()->widget;
  1761   if (!widget) {
  1762     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  1765   return GetEventCoordinatesRelativeTo(widget, aPoint, aFrame);
  1768 nsPoint
  1769 nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget,
  1770                                              const nsIntPoint aPoint,
  1771                                              nsIFrame* aFrame)
  1773   if (!aFrame || !aWidget) {
  1774     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  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();
  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;
  1802     rootFrame = f;
  1805   nsView* rootView = rootFrame->GetView();
  1806   if (!rootView) {
  1807     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  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);
  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);
  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);
  1836 nsIFrame*
  1837 nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
  1838                                                 const WidgetEvent* aEvent)
  1840 #ifdef MOZ_XUL
  1841   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1842   if (!pm) {
  1843     return nullptr;
  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;
  1857 #endif
  1858   return nullptr;
  1861 gfx3DMatrix
  1862 nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin,
  1863                                  const gfx3DMatrix &aMatrix)
  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; 
  1876 static void ConstrainToCoordValues(float& aStart, float& aSize)
  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;
  1904 /**
  1905  * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX.
  1907  * @param aVal The value to constrain (in/out)
  1908  */
  1909 static void ConstrainToCoordValues(gfxFloat& aVal)
  1911   if (aVal <= nscoord_MIN)
  1912     aVal = nscoord_MIN;
  1913   else if (aVal >= nscoord_MAX)
  1914     aVal = nscoord_MAX;
  1917 static void ConstrainToCoordValues(gfxFloat& aStart, gfxFloat& aSize)
  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;
  1943 nsRect
  1944 nsLayoutUtils::RoundGfxRectToAppRect(const Rect &aRect, float aFactor)
  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()));
  1959 nsRect
  1960 nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor)
  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()));
  1976 nsRegion
  1977 nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect,
  1978                                         const nscoord aRadii[8],
  1979                                         const nsRect& aContainedRect)
  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;
  2004 // Helper for RoundedRectIntersectsRect.
  2005 static bool
  2006 CheckCorner(nscoord aXOffset, nscoord aYOffset,
  2007             nscoord aXRadius, nscoord aYRadius)
  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;
  2027 bool
  2028 nsLayoutUtils::RoundedRectIntersectsRect(const nsRect& aRoundedRect,
  2029                                          const nscoord aRadii[8],
  2030                                          const nsRect& aTestRect)
  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]);
  2060 nsRect
  2061 nsLayoutUtils::MatrixTransformRectOut(const nsRect &aBounds,
  2062                                       const gfx3DMatrix &aMatrix, float aFactor)
  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);
  2073 nsRect
  2074 nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds,
  2075                                    const gfx3DMatrix &aMatrix, float aFactor)
  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);
  2085 nsPoint
  2086 nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint,
  2087                                     const gfx3DMatrix &aMatrix, float aFactor)
  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));
  2095 gfx3DMatrix
  2096 nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame, const nsIFrame *aAncestor)
  2098   nsIFrame* parent;
  2099   gfx3DMatrix ctm;
  2100   if (aFrame == aAncestor) {
  2101     return ctm;
  2103   ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
  2104   while (parent && parent != aAncestor) {
  2105     if (!parent->Preserves3DChildren()) {
  2106       ctm.ProjectTo2D();
  2108     ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
  2110   return ctm;
  2113 static nsIFrame*
  2114 FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2)
  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();
  2122   for (nsIFrame* f = aFrame1; f != commonAncestor;
  2123        f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
  2124     ancestors1.AppendElement(f);
  2126   for (nsIFrame* f = aFrame2; f != commonAncestor;
  2127        f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
  2128     ancestors2.AppendElement(f);
  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;
  2138   return commonAncestor;
  2141 nsLayoutUtils::TransformResult
  2142 nsLayoutUtils::TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame,
  2143                                uint32_t aPointCount, CSSPoint* aPoints)
  2145   nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
  2146   if (!nearestCommonAncestor) {
  2147     return NO_COMMON_ANCESTOR;
  2149   gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor);
  2150   if (downToDest.IsSingular()) {
  2151     return NONINVERTIBLE_TRANSFORM;
  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;
  2170   return TRANSFORM_SUCCEEDED;
  2173 bool
  2174 nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
  2175                                          gfx3DMatrix* aTransform)
  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;
  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;
  2189   // If the caller doesn't care about the value, early-return to skip
  2190   // overhead below.
  2191   if (!aTransform) {
  2192     return true;
  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;
  2208 static bool
  2209 TransformGfxPointFromAncestor(nsIFrame *aFrame,
  2210                               const gfxPoint &aPoint,
  2211                               nsIFrame *aAncestor,
  2212                               gfxPoint* aOut)
  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);
  2225 static gfxRect
  2226 TransformGfxRectToAncestor(nsIFrame *aFrame,
  2227                            const gfxRect &aRect,
  2228                            const nsIFrame *aAncestor,
  2229                            bool* aPreservesAxisAlignedRectangles = nullptr)
  2231   gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
  2232   if (aPreservesAxisAlignedRectangles) {
  2233     gfxMatrix matrix2d;
  2234     *aPreservesAxisAlignedRectangles =
  2235       ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles();
  2237   return ctm.TransformBounds(aRect);
  2240 static SVGTextFrame*
  2241 GetContainingSVGTextFrame(nsIFrame* aFrame)
  2243   if (!aFrame->IsSVGText()) {
  2244     return nullptr;
  2247   return static_cast<SVGTextFrame*>
  2248     (nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(),
  2249                                           nsGkAtoms::svgTextFrame));
  2252 nsPoint
  2253 nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame,
  2254                                              const nsPoint& aPoint,
  2255                                              nsIFrame* aAncestor)
  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);
  2267         result = text->TransformFramePointToTextChild(result, aFrame);
  2268     } else {
  2269         if (!TransformGfxPointFromAncestor(aFrame, result, nullptr, &result)) {
  2270             return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  2274     return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
  2275                    NSFloatPixelsToAppUnits(float(result.y), factor));
  2278 nsRect
  2279 nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
  2280                                             const nsRect& aRect,
  2281                                             const nsIFrame* aAncestor,
  2282                                             bool* aPreservesAxisAlignedRectangles /* = nullptr */)
  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);
  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));
  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();
  2321   aRootWidget = aWidget;
  2322   return offset;
  2325 nsPoint
  2326 nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext,
  2327                                      nsIWidget* aWidget, nsIntPoint aPt,
  2328                                      nsView* aView)
  2330   nsPoint viewOffset;
  2331   nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset);
  2332   if (!viewWidget) {
  2333     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  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();
  2349   nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x),
  2350                          aPresContext->DevPixelsToAppUnits(widgetPoint.y));
  2351   return widgetAppUnits - viewOffset;
  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)
  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;
  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;
  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;
  2381   return breakType;
  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)
  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);
  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;
  2421 nsIFrame*
  2422 nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt, uint32_t aFlags)
  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;
  2432 nsresult
  2433 nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
  2434                                 nsTArray<nsIFrame*> &aOutFrames,
  2435                                 uint32_t aFlags)
  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();
  2447   if (aFlags & IGNORE_ROOT_SCROLL_FRAME) {
  2448     nsIFrame* rootScrollFrame =
  2449       aFrame->PresContext()->PresShell()->GetRootScrollFrame();
  2450     if (rootScrollFrame) {
  2451       builder.SetIgnoreScrollFrame(rootScrollFrame);
  2454   if (aFlags & IGNORE_CROSS_DOC) {
  2455     builder.SetDescendIntoSubdocuments(false);
  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);
  2467 #endif
  2469   nsDisplayItem::HitTestState hitTestState;
  2470   list.HitTest(&builder, target, &hitTestState, &aOutFrames);
  2471   list.DeleteAll();
  2472   return NS_OK;
  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;
  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;
  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");
  2566     // Record that the we now have a scrollable display port.
  2567     aBuilder.SetHaveScrollableDisplayPort();
  2569 #endif
  2571   return haveDisplayPort;
  2574 nsresult
  2575 nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
  2576                           const nsRegion& aDirtyRegion, nscolor aBackstop,
  2577                           uint32_t aFlags)
  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");
  2588   nsPresContext* presContext = aFrame->PresContext();
  2589   nsIPresShell* presShell = presContext->PresShell();
  2590   nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
  2591   if (!rootPresContext) {
  2592     return NS_OK;
  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);
  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;
  2622   } else {
  2623     visibleRegion = aDirtyRegion;
  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);
  2635   if (aFlags & PAINT_SYNC_DECODE_IMAGES) {
  2636     builder.SetSyncDecodeImages(true);
  2638   if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) {
  2639     builder.SetPaintingToWindow(true);
  2641   if (aFlags & PAINT_IGNORE_SUPPRESSION) {
  2642     builder.IgnorePaintSuppression();
  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);
  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);
  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));
  2679   nsRect dirtyRect = visibleRegion.GetBounds();
  2680   builder.EnterPresShell(aFrame, dirtyRect);
  2682     PROFILER_LABEL("nsLayoutUtils","PaintFrame::BuildDisplayList");
  2683     aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
  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);
  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;
  2740   builder.LeavePresShell(aFrame, dirtyRect);
  2742   if (builder.GetHadToIgnorePaintSuppression()) {
  2743     willFlushRetainedLayers = true;
  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;
  2757     if (gfxUtils::sDumpPaintingToFile) {
  2758       fprintf_stderr(gfxUtils::sDumpPaintFile, "<html><head><script>var array = {}; function ViewImage(index) { window.location = array[index]; }</script></head><body>");
  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>");
  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());
  2794   if (aFlags & PAINT_EXISTING_TRANSACTION) {
  2795     flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
  2797   if (aFlags & PAINT_NO_COMPOSITE) {
  2798     flags |= nsDisplayList::PAINT_NO_COMPOSITE;
  2800   if (aFlags & PAINT_COMPRESSED) {
  2801     flags |= nsDisplayList::PAINT_COMPRESSED;
  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>");
  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);
  2823     if (gfxUtils::sDumpPaintingToFile) {
  2824       fprintf(gfxUtils::sDumpPaintFile, "</body></html>");
  2825       fclose(gfxUtils::sDumpPaintFile);
  2827     gfxUtils::sDumpPaintFile = savedDumpFile;
  2828     gPaintCount++;
  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);
  2846   if (builder.WillComputePluginGeometry()) {
  2847     nsRefPtr<LayerManager> layerManager;
  2848     nsIWidget* widget = aFrame->GetNearestWidget();
  2849     if (widget) {
  2850       layerManager = widget->GetLayerManager();
  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();
  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();
  2871   // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
  2872   list.DeleteAll();
  2873   return NS_OK;
  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
  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.
  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)
  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;
  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;
  2924   } else {
  2925     aTextWidth = fullWidth;
  2926     if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) {
  2927       return true;
  2930   return false;
  2933 static void
  2934 AddBoxesForFrame(nsIFrame* aFrame,
  2935                  nsLayoutUtils::BoxCallback* aCallback)
  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);
  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);
  2952   } else {
  2953     aCallback->AddBox(aFrame);
  2957 void
  2958 nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback)
  2960   while (aFrame) {
  2961     AddBoxesForFrame(aFrame, aCallback);
  2962     aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
  2966 nsIFrame*
  2967 nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame)
  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;
  2977       nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList);
  2978       if (kid) {
  2979         f = GetFirstNonAnonymousFrame(kid);
  2980         if (f) {
  2981           return f;
  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;
  2994     } else {
  2995       return aFrame;
  2998     aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
  3000   return nullptr;
  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();
  3031     if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) {
  3032       r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo);
  3033     } else {
  3034       r += outer->GetOffsetTo(mRelativeTo);
  3036     mCallback->AddRect(r);
  3038 };
  3040 void
  3041 nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
  3042                                  RectCallback* aCallback, uint32_t aFlags)
  3044   BoxToRect converter(aRelativeTo, aCallback, aFlags);
  3045   GetAllInFlowBoxes(aFrame, &converter);
  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;
  3058 nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList* aList)
  3059   : mRectList(aList)
  3063 void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) {
  3064   nsRefPtr<DOMRect> rect = new DOMRect(mRectList);
  3066   rect->SetLayoutRect(aRect);
  3067   mRectList->Append(rect);
  3070 nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame)
  3072   return aFrame->PresContext()->PresShell()->GetRootFrame();
  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;
  3084 nsRect
  3085 nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
  3086                                        nsIFrame* aFrame,
  3087                                        uint32_t aFlags)
  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);
  3108   return resultRect;
  3111 nsresult
  3112 nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame,
  3113                                       nsFontMetrics** aFontMetrics,
  3114                                       float aInflation)
  3116   return nsLayoutUtils::GetFontMetricsForStyleContext(aFrame->StyleContext(),
  3117                                                       aFontMetrics,
  3118                                                       aInflation);
  3121 nsresult
  3122 nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext,
  3123                                              nsFontMetrics** aFontMetrics,
  3124                                              float aInflation)
  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);
  3138   return pc->DeviceContext()->GetMetricsFor(
  3139                   font, aStyleContext->StyleFont()->mLanguage,
  3140                   fs, tp, *aFontMetrics);
  3143 nsIFrame*
  3144 nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame)
  3146   nsIFrame* result = aDescendantFrame;
  3148   while (result) {
  3149     nsIFrame* parent = result->GetParent();
  3150     if (parent == aParent) {
  3151       break;
  3154     // The frame is not an immediate child of aParent so walk up another level
  3155     result = parent;
  3158   return result;
  3161 nsBlockFrame*
  3162 nsLayoutUtils::GetAsBlock(nsIFrame* aFrame)
  3164   nsBlockFrame* block = do_QueryFrame(aFrame);
  3165   return block;
  3168 nsBlockFrame*
  3169 nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame)
  3171   nsIFrame* nextAncestor;
  3172   for (nextAncestor = aFrame->GetParent(); nextAncestor;
  3173        nextAncestor = nextAncestor->GetParent()) {
  3174     nsBlockFrame* block = GetAsBlock(nextAncestor);
  3175     if (block)
  3176       return block;
  3178   return nullptr;
  3181 nsIFrame*
  3182 nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame* aFrame)
  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;
  3194 nsIFrame*
  3195 nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame)
  3197   if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
  3198       && !aFrame->GetPrevInFlow()) {
  3199     return aFrame->PresContext()->PresShell()->FrameManager()->
  3200       GetPlaceholderFrameFor(aFrame);
  3202   return aFrame->GetParent();
  3205 nsIFrame*
  3206 nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(nsIFrame* aFrame)
  3208   nsIFrame* f = GetParentOrPlaceholderFor(aFrame);
  3209   if (f)
  3210     return f;
  3211   return GetCrossDocParentFrame(aFrame);
  3214 nsIFrame*
  3215 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame *aFrame)
  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);
  3230   return nullptr;
  3233 nsIFrame*
  3234 nsLayoutUtils::FirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
  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;
  3247   return result;
  3250 bool
  3251 nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame)
  3253   if (aFrame->GetPrevContinuation()) {
  3254     return false;
  3256   if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
  3257       aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())) {
  3258     return false;
  3261   return true;
  3264 bool
  3265 nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame)
  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));
  3286 static nscoord AddPercents(nsLayoutUtils::IntrinsicWidthType aType,
  3287                            nscoord aCurrent, float aPercent)
  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));
  3298   return result;
  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)
  3305   if (aStyle.IsCalcUnit()) {
  3306     if (aStyle.CalcHasPercent()) {
  3307       return false;
  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;
  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;
  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)
  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;
  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();
  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;
  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;
  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");
  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");
  3402   if (aStyle.IsCalcUnit()) {
  3403     aResult = std::max(nsRuleNode::ComputeComputedCalc(aStyle, h), 0);
  3404     return true;
  3407   aResult = NSToCoordRound(aStyle.GetPercentValue() * h);
  3408   return true;
  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)
  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;
  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;
  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)
  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
  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;
  3572             if (GetAbsoluteCoord(stylePadding->mPadding.GetBottom(), pad) ||
  3573                 GetPercentHeight(stylePadding->mPadding.GetBottom(), aFrame, pad)) {
  3574               heightTakenByBoxSizing += pad;
  3577           // fall through
  3579         case NS_STYLE_BOX_SIZING_CONTENT:
  3580         default:
  3581           break;
  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)));
  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;
  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;
  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);
  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;
  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;
  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);
  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
  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);
  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;
  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;
  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;
  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;
  3735 /* static */ nscoord
  3736 nsLayoutUtils::ComputeCBDependentValue(nscoord aPercentBasis,
  3737                                        const nsStyleCoord& aCoord)
  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);
  3747   NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
  3748                aCoord.GetUnit() == eStyleUnit_Auto,
  3749                "unexpected width value");
  3750   return 0;
  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)
  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:
  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");
  3804         break;
  3805       case NS_STYLE_WIDTH_AVAILABLE:
  3806         result = aContainingBlockWidth -
  3807                  (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
  3811   return std::max(0, result);
  3814 /* static */ nscoord
  3815 nsLayoutUtils::ComputeHeightDependentValue(
  3816                  nscoord              aContainingBlockHeight,
  3817                  const nsStyleCoord&  aCoord)
  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);
  3834   NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
  3835                aCoord.GetUnit() == eStyleUnit_Auto,
  3836                "unexpected height value");
  3837   return 0;
  3840 /* static */ void
  3841 nsLayoutUtils::MarkDescendantsDirty(nsIFrame *aSubtreeRoot)
  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);
  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);
  3881     } while (stack.Length() != 0);
  3882   } while (subtrees.Length() != 0);
  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)
  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;
  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;
  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);
  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;
  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;
  3988   if (!isAutoHeight) {
  3989     height = nsLayoutUtils::ComputeHeightValue(aCBSize.height, 
  3990                 boxSizingAdjust.height, 
  3991                 *heightStyleCoord);
  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;
  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;
  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;
  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;
  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);
  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);
  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);
  4091       width = NS_CSS_MINMAX(width, minWidth, maxWidth);
  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);
  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);
  4117   return nsSize(width, height);
  4120 nsSize
  4121 nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth, nscoord minHeight,
  4122                                                       nscoord maxWidth, nscoord maxHeight,
  4123                                                       nscoord tentWidth, nscoord tentHeight)
  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);
  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);
  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;
  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;
  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;
  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;
  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;
  4208   return nsSize(width, height);
  4211 /* static */ nscoord
  4212 nsLayoutUtils::MinWidthFromInline(nsIFrame* aFrame,
  4213                                   nsRenderingContext* aRenderingContext)
  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;
  4225 /* static */ nscoord
  4226 nsLayoutUtils::PrefWidthFromInline(nsIFrame* aFrame,
  4227                                    nsRenderingContext* aRenderingContext)
  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;
  4239 static nscolor
  4240 DarkenColor(nscolor aColor)
  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);
  4259   return aColor;
  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)
  4268   return !aPresContext->GetBackgroundColorDraw() &&
  4269          !aPresContext->GetBackgroundImageDraw();
  4272 nscolor
  4273 nsLayoutUtils::GetColor(nsIFrame* aFrame, nsCSSProperty aProperty)
  4275   if (aProperty == eCSSProperty_color)
  4277     nscolor nativeColor = NS_RGB(0, 0, 0);
  4278     if (GetNativeTextColor(aFrame, nativeColor))
  4279       return nativeColor;
  4282   nscolor color = aFrame->GetVisitedDependentColor(aProperty);
  4283   if (ShouldDarkenColors(aFrame->PresContext())) {
  4284     color = DarkenColor(color);
  4287   return color;
  4290 bool
  4291 nsLayoutUtils::GetNativeTextColor(nsIFrame* aFrame, nscolor& aColor)
  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); 
  4321         if (NS_SUCCEEDED(LookAndFeel::GetColorForNativeAppearance(widgetType,
  4322                                                        isDisabled, &aColor))) {
  4323             return true;
  4329   return false;
  4332 gfxFloat
  4333 nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext,
  4334                                    nscoord aY, nscoord aAscent)
  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;
  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)
  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);
  4362   if (NS_FAILED(rv))
  4364     aContext->SetTextRunRTL(false);
  4365     aContext->DrawString(aString, aLength, aPoint.x, aPoint.y);
  4369 nscoord
  4370 nsLayoutUtils::GetStringWidth(const nsIFrame*      aFrame,
  4371                               nsRenderingContext* aContext,
  4372                               const char16_t*     aString,
  4373                               int32_t              aLength)
  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);
  4382   aContext->SetTextRunRTL(false);
  4383   return aContext->GetWidth(aString, aLength);
  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)
  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();
  4442 /* static */ nscoord
  4443 nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
  4444                                        nscoord         aLineHeight)
  4446   nscoord fontAscent = aFontMetrics->MaxAscent();
  4447   nscoord fontHeight = aFontMetrics->MaxHeight();
  4449   nscoord leading = aLineHeight - fontHeight;
  4450   return fontAscent + leading/2;
  4454 /* static */ bool
  4455 nsLayoutUtils::GetFirstLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
  4457   LinePosition position;
  4458   if (!GetFirstLinePosition(aFrame, &position))
  4459     return false;
  4460   *aResult = position.mBaseline;
  4461   return true;
  4464 /* static */ bool
  4465 nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame,
  4466                                     LinePosition* aResult)
  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;
  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");
  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;
  4496       return false;
  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;
  4507       return false;
  4510     // No baseline.
  4511     return false;
  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;
  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;
  4536   return false;
  4539 /* static */ bool
  4540 nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult)
  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;
  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;
  4572   return false;
  4575 static nscoord
  4576 CalculateBlockContentBottom(nsBlockFrame* aFrame)
  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);
  4591     else {
  4592       contentBottom = std::max(contentBottom, line->BEnd());
  4595   return contentBottom;
  4598 /* static */ nscoord
  4599 nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame)
  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;
  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);
  4630   return contentBottom;
  4633 /* static */ nsIFrame*
  4634 nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame)
  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;
  4643   if (layer)
  4644     return layer;
  4645   return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();
  4648 GraphicsFilter
  4649 nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame)
  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();
  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;
  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)
  4683   return gfxPoint(((aPt.x - aDest.X())*aSize.width)/aDest.Width(),
  4684                   ((aPt.y - aDest.Y())*aSize.height)/aDest.Height());
  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)
  4699   return gfxPoint(aPt.x*aDest.Width()/aSize.width + aDest.X(),
  4700                   aPt.y*aDest.Height()/aSize.height + aDest.Y());
  4703 /* static */ gfxRect
  4704 nsLayoutUtils::RectToGfxRect(const nsRect& aRect, int32_t aAppUnitsPerDevPixel)
  4706   return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel,
  4707                  gfxFloat(aRect.y) / aAppUnitsPerDevPixel,
  4708                  gfxFloat(aRect.width) / aAppUnitsPerDevPixel,
  4709                  gfxFloat(aRect.height) / aAppUnitsPerDevPixel);
  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
  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)
  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();
  4784   } else {
  4785     didSnap = false;
  4786     fill = devPixelFill;
  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);
  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;
  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);
  4846   if (finalFillRect.IsEmpty())
  4847     return SnappedImageDrawingParameters();
  4849   return SnappedImageDrawingParameters(transform, finalFillRect, intSubimage,
  4850                                        didSnap);
  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)
  4866   if (aDest.Contains(aFill)) {
  4867     aImageFlags |= imgIContainer::FLAG_CLAMP;
  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();
  4884   aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace,
  4885                drawingParams.mFillRect, drawingParams.mSubimage, aImageSize,
  4886                aSVGContext, imgIContainer::FRAME_CURRENT, aImageFlags);
  4887   return NS_OK;
  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)
  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();
  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);
  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)
  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);
  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);
  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)
  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);
  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);
  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);
  5012 /* static */ void
  5013 nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage,
  5014                                      nsIntSize&     aImageSize, /*outparam*/
  5015                                      nsSize&        aIntrinsicRatio, /*outparam*/
  5016                                      bool&          aGotWidth,  /*outparam*/
  5017                                      bool&          aGotHeight  /*outparam*/)
  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);
  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)
  5044   PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage");
  5046   if (UseBackgroundNearestFiltering()) {
  5047     aGraphicsFilter = GraphicsFilter::FILTER_NEAREST;
  5050   return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
  5051                            aDest, aFill, aAnchor, aDirty,
  5052                            aImageSize, nullptr, aImageFlags);
  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)
  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;
  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;
  5092   if (!gotWidth) {
  5093     imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFill.width);
  5095   if (!gotHeight) {
  5096     imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFill.height);
  5099   return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
  5100                            aDest, aFill, aAnchor, aDirty,
  5101                            imageSize, nullptr, aImageFlags);
  5104 /* static */ nsRect
  5105 nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize,
  5106                                         const nsRect& aImageSourceArea,
  5107                                         const nsRect& aDestArea)
  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));
  5120 /* static */ already_AddRefed<imgIContainer>
  5121 nsLayoutUtils::OrientImage(imgIContainer* aContainer,
  5122                            const nsStyleImageOrientation& aOrientation)
  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));
  5136   return img.forget();
  5139 static bool NonZeroStyleCoord(const nsStyleCoord& aCoord)
  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;
  5147   return true;
  5150 /* static */ bool
  5151 nsLayoutUtils::HasNonZeroCorner(const nsStyleCorners& aCorners)
  5153   NS_FOR_CSS_HALF_CORNERS(corner) {
  5154     if (NonZeroStyleCoord(aCorners.Get(corner)))
  5155       return true;
  5157   return false;
  5160 // aCorner is a "full corner" value, i.e. NS_CORNER_TOP_LEFT etc
  5161 static bool IsCornerAdjacentToSide(uint8_t aCorner, css::Side aSide)
  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);
  5175 /* static */ bool
  5176 nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners& aCorners,
  5177                                       css::Side aSide)
  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;
  5195   return false;
  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;
  5227   nsStyleContext* bgSC;
  5228   if (!nsCSSRendering::FindBackground(aBackgroundFrame, &bgSC)) {
  5229     return eTransparencyTransparent;
  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;
  5239 static bool IsPopupFrame(nsIFrame* aFrame)
  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();
  5248   // ... or if it's a XUL menupopup frame.
  5249   return frameType == nsGkAtoms::menuPopupFrame;
  5252 /* static */ bool
  5253 nsLayoutUtils::IsPopup(nsIFrame* aFrame)
  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;
  5260   return IsPopupFrame(aFrame);
  5263 /* static */ nsIFrame*
  5264 nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame)
  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;
  5275     nsIFrame* parent = GetCrossDocParentFrame(f);
  5276     if (!parent)
  5277       return f;
  5278     f = parent;
  5282 /* static */ nsIFrame*
  5283 nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame)
  5285   nsIFrame *f = aFrame;
  5286   for (;;) {
  5287     if (f->IsTransformed() || IsPopup(f)) {
  5288       return f;
  5290     nsIFrame* parent = GetCrossDocParentFrame(f);
  5291     if (!parent) {
  5292       return f;
  5294     f = parent;
  5298 /* static */ nsIFrame*
  5299 nsLayoutUtils::GetTransformRootFrame(nsIFrame* aFrame)
  5301   nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
  5302   while (parent && parent->Preserves3DChildren()) {
  5303     parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
  5305   return parent;
  5308 /* static */ uint32_t
  5309 nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
  5310                                        const nsStyleFont* aStyleFont,
  5311                                        const nsStyleText* aStyleText,
  5312                                        nscoord aLetterSpacing)
  5314   uint32_t result = 0;
  5315   if (aLetterSpacing != 0) {
  5316     result |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
  5318   if (aStyleText->mControlCharacterVisibility == NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN) {
  5319     result |= gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS;
  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;
  5330     break;
  5331   default:
  5332     break;
  5334   return result;
  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;
  5354 nsDeviceContext*
  5355 nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindow* aWindow)
  5357   if (!aWindow) {
  5358     return nullptr;
  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;
  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;
  5384     nsCOMPtr<nsIDocShellTreeItem> parentItem;
  5385     docShell->GetParent(getter_AddRefs(parentItem));
  5386     docShell = do_QueryInterface(parentItem);
  5389   return nullptr;
  5392 /* static */ bool
  5393 nsLayoutUtils::IsReallyFixedPos(nsIFrame* aFrame)
  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;
  5406 nsLayoutUtils::SurfaceFromElementResult
  5407 nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement,
  5408                                   uint32_t aSurfaceFlags,
  5409                                   DrawTarget* aTarget)
  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;
  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;
  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;
  5463     result.mSourceSurface = imgContainer->GetFrame(whichFrame, frameFlags);
  5464     if (!result.mSourceSurface) {
  5465       return result;
  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;
  5478   } else {
  5479     result.mDrawInfo.mImgContainer = imgContainer;
  5480     result.mDrawInfo.mWhichFrame = whichFrame;
  5481     result.mDrawInfo.mDrawingFlags = frameFlags;
  5484   int32_t corsmode;
  5485   if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) {
  5486     result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE);
  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;
  5498 nsLayoutUtils::SurfaceFromElementResult
  5499 nsLayoutUtils::SurfaceFromElement(HTMLImageElement *aElement,
  5500                                   uint32_t aSurfaceFlags,
  5501                                   DrawTarget* aTarget)
  5503   return SurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement),
  5504                             aSurfaceFlags, aTarget);
  5507 nsLayoutUtils::SurfaceFromElementResult
  5508 nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement,
  5509                                   uint32_t aSurfaceFlags,
  5510                                   DrawTarget* aTarget)
  5512   SurfaceFromElementResult result;
  5514   bool* isPremultiplied = nullptr;
  5515   if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
  5516     isPremultiplied = &result.mIsPremultiplied;
  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();
  5531   } else if (aTarget) {
  5532     RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
  5533     if (opt) {
  5534       result.mSourceSurface = opt;
  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;
  5549 nsLayoutUtils::SurfaceFromElementResult
  5550 nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
  5551                                   uint32_t aSurfaceFlags,
  5552                                   DrawTarget* aTarget)
  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;
  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;
  5587   result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE;
  5588   result.mSize = ThebesIntSize(size);
  5589   result.mPrincipal = principal.forget();
  5590   result.mIsWriteOnly = false;
  5592   return result;
  5595 nsLayoutUtils::SurfaceFromElementResult
  5596 nsLayoutUtils::SurfaceFromElement(dom::Element* aElement,
  5597                                   uint32_t aSurfaceFlags,
  5598                                   DrawTarget* aTarget)
  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);
  5606   // Maybe it's <video>?
  5607   if (HTMLVideoElement* video =
  5608         HTMLVideoElement::FromContentOrNull(aElement)) {
  5609     return SurfaceFromElement(video, aSurfaceFlags, aTarget);
  5612   // Finally, check if it's a normal image
  5613   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
  5615   if (!imageLoader) {
  5616     return SurfaceFromElementResult();
  5619   return SurfaceFromElement(imageLoader, aSurfaceFlags, aTarget);
  5622 /* static */
  5623 nsIContent*
  5624 nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument* aDocument)
  5626   // If the document is in designMode we should return nullptr.
  5627   if (!aDocument || aDocument->HasFlag(NODE_IS_EDITABLE)) {
  5628     return nullptr;
  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;
  5640   Element* rootElement = aDocument->GetRootElement();
  5641   if (rootElement && rootElement->IsEditable()) {
  5642     return rootElement;
  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;
  5653   return nullptr;
  5656 #ifdef DEBUG
  5657 /* static */ void
  5658 nsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame* aContainer,
  5659                                               const nsFrameList& aFrameList)
  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");
  5674 // Is one of aFrame's ancestors a letter frame?
  5675 static bool
  5676 IsInLetterFrame(nsIFrame *aFrame)
  5678   for (nsIFrame *f = aFrame->GetParent(); f; f = f->GetParent()) {
  5679     if (f->GetType() == nsGkAtoms::letterFrame) {
  5680       return true;
  5683   return false;
  5686 /* static */ void
  5687 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot)
  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());
  5714 #endif
  5716 static void
  5717 GetFontFacesForFramesInner(nsIFrame* aFrame, nsFontFaceList* aFontFaceList)
  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);
  5726     return;
  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);
  5741 /* static */
  5742 nsresult
  5743 nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame,
  5744                                      nsFontFaceList* aFontFaceList)
  5746   NS_PRECONDITION(aFrame, "NULL frame pointer");
  5748   while (aFrame) {
  5749     GetFontFacesForFramesInner(aFrame, aFontFaceList);
  5750     aFrame = GetNextContinuationOrIBSplitSibling(aFrame);
  5753   return NS_OK;
  5756 /* static */
  5757 nsresult
  5758 nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame,
  5759                                    int32_t aStartOffset, int32_t aEndOffset,
  5760                                    bool aFollowContinuations,
  5761                                    nsFontFaceList* aFontFaceList)
  5763   NS_PRECONDITION(aFrame, "NULL frame pointer");
  5765   if (aFrame->GetType() != nsGkAtoms::textFrame) {
  5766     return NS_OK;
  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;
  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;
  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;
  5803 /* static */
  5804 size_t
  5805 nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame,
  5806                                        MallocSizeOf aMallocSizeOf,
  5807                                        bool clear)
  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);
  5826     return total;
  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);
  5839   return total;
  5842 /* static */
  5843 void
  5844 nsLayoutUtils::Initialize()
  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();
  5881 /* static */
  5882 void
  5883 nsLayoutUtils::Shutdown()
  5885   if (sContentMap) {
  5886     delete sContentMap;
  5887     sContentMap = nullptr;
  5890   Preferences::UnregisterCallback(GridEnabledPrefChangeCallback,
  5891                                   GRID_ENABLED_PREF_NAME);
  5892   Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback,
  5893                                   STICKY_ENABLED_PREF_NAME);
  5895   nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
  5898 /* static */
  5899 void
  5900 nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
  5901                                     imgIRequest* aRequest,
  5902                                     bool* aRequestRegistered)
  5904   if (!aPresContext) {
  5905     return;
  5908   if (aRequestRegistered && *aRequestRegistered) {
  5909     // Our request is already registered with the refresh driver, so
  5910     // no need to register it again.
  5911     return;
  5914   if (aRequest) {
  5915     if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
  5916       NS_WARNING("Unable to add image request");
  5917       return;
  5920     if (aRequestRegistered) {
  5921       *aRequestRegistered = true;
  5926 /* static */
  5927 void
  5928 nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
  5929                                               imgIRequest* aRequest,
  5930                                               bool* aRequestRegistered)
  5932   if (!aPresContext) {
  5933     return;
  5936   if (aRequestRegistered && *aRequestRegistered) {
  5937     // Our request is already registered with the refresh driver, so
  5938     // no need to register it again.
  5939     return;
  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;
  5955         if (aRequestRegistered) {
  5956           *aRequestRegistered = true;
  5963 /* static */
  5964 void
  5965 nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
  5966                                       imgIRequest* aRequest,
  5967                                       bool* aRequestRegistered)
  5969   if (!aPresContext) {
  5970     return;
  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;
  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;
  5991 /* static */
  5992 void
  5993 nsLayoutUtils::PostRestyleEvent(Element* aElement,
  5994                                 nsRestyleHint aRestyleHint,
  5995                                 nsChangeHint aMinChangeHint)
  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);
  6007 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
  6008                                      const nsAString& aValue)
  6009   : mContent(aContent),
  6010     mAttrName(aAttrName),
  6011     mValue(aValue)
  6013   NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
  6016 nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
  6017                                      int32_t aValue)
  6018   : mContent(aContent),
  6019     mAttrName(aAttrName)
  6021   NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
  6022   mValue.AppendInt(aValue);
  6025 NS_IMETHODIMP
  6026 nsSetAttrRunnable::Run()
  6028   return mContent->SetAttr(kNameSpaceID_None, mAttrName, mValue, true);
  6031 nsUnsetAttrRunnable::nsUnsetAttrRunnable(nsIContent* aContent,
  6032                                          nsIAtom* aAttrName)
  6033   : mContent(aContent),
  6034     mAttrName(aAttrName)
  6036   NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash");
  6039 NS_IMETHODIMP
  6040 nsUnsetAttrRunnable::Run()
  6042   return mContent->UnsetAttr(kNameSpaceID_None, mAttrName, true);
  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)
  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;
  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;
  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 ));
  6078   return std::max(byLine, byInch);
  6081 /* static */ float
  6082 nsLayoutUtils::FontSizeInflationInner(const nsIFrame *aFrame,
  6083                                       nscoord aMinFontSize)
  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;
  6094   if (aMinFontSize <= 0) {
  6095     // No need to scale.
  6096     return 1.0;
  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;
  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;
  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;
  6160   if (maxRatio > 1.0 && inflationRatio > maxRatio) {
  6161     return maxRatio;
  6162   } else {
  6163     return inflationRatio;
  6167 static bool
  6168 ShouldInflateFontsForContainer(const nsIFrame *aFrame)
  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);
  6186 nscoord
  6187 nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame *aFrame)
  6189   nsPresContext *presContext = aFrame->PresContext();
  6190   if (!FontSizeInflationEnabled(presContext) ||
  6191       presContext->mInflationDisabledForShrinkWrap) {
  6192     return 0;
  6195   for (const nsIFrame *f = aFrame; f; f = f->GetParent()) {
  6196     if (f->IsContainerForFontSizeInflation()) {
  6197       if (!ShouldInflateFontsForContainer(f)) {
  6198         return 0;
  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;
  6209       return MinimumFontSizeFor(aFrame->PresContext(),
  6210                                 data->EffectiveWidth());
  6214   NS_ABORT_IF_FALSE(false, "root should always be container");
  6216   return 0;
  6219 float
  6220 nsLayoutUtils::FontSizeInflationFor(const nsIFrame *aFrame)
  6222   if (aFrame->IsSVGText()) {
  6223     const nsIFrame* container = aFrame;
  6224     while (container->GetType() != nsGkAtoms::svgTextFrame) {
  6225       container = container->GetParent();
  6227     NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
  6228     return
  6229       static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor();
  6232   if (!FontSizeInflationEnabled(aFrame->PresContext())) {
  6233     return 1.0f;
  6236   return FontSizeInflationInner(aFrame, InflationMinFontSizeFor(aFrame));
  6239 /* static */ bool
  6240 nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext)
  6242   nsIPresShell* presShell = aPresContext->GetPresShell();
  6244   if (!presShell) {
  6245     return false;
  6248   return presShell->FontSizeInflationEnabled();
  6251 /* static */ nsRect
  6252 nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame, 
  6253                                         const nsSize& aFrameSize)
  6255   nsCSSShadowArray* boxShadows = aFrame->StyleBorder()->mBoxShadow;
  6256   if (!boxShadows) {
  6257     return nsRect();
  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);
  6276   return shadows;
  6279 /* static */ void
  6280 nsLayoutUtils::UpdateImageVisibilityForFrame(nsIFrame* aImageFrame)
  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;
  6294   nsIPresShell* presShell = aImageFrame->PresContext()->PresShell();
  6295   if (presShell->AssumeAllImagesVisible()) {
  6296     presShell->EnsureImageInVisibleList(content);
  6297     return;
  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;
  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();
  6319       if (transformedRect.x < scrollPort.x) {
  6320         transformedRect.x = scrollPort.x;
  6322       if (transformedRect.YMost() > scrollPort.YMost()) {
  6323         transformedRect.y -= transformedRect.YMost() - scrollPort.YMost();
  6325       if (transformedRect.y < scrollPort.y) {
  6326         transformedRect.y = scrollPort.y;
  6328       transformedRect.width = std::min(transformedRect.width, scrollPort.width);
  6329       transformedRect.height = std::min(transformedRect.height, scrollPort.height);
  6330       rect = transformedRect;
  6331       rectFrame = f;
  6333     nsIFrame* parent = f->GetParent();
  6334     if (!parent) {
  6335       parent = nsLayoutUtils::GetCrossDocParentFrame(f);
  6336       if (parent && parent->PresContext()->IsChrome()) {
  6337         break;
  6340     f = parent;
  6343   if (visible) {
  6344     presShell->EnsureImageInVisibleList(content);
  6345   } else {
  6346     presShell->RemoveImageFromVisibleList(content);
  6350 /* static */ nsSize
  6351 nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame)
  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;
  6383 #endif
  6384         } else {
  6385           size = viewSize;
  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();
  6399   return size;
  6401 /* static */ CSSSize
  6402 nsLayoutUtils::CalculateRootCompositionSize(nsIFrame* aFrame,
  6403                                             bool aIsRootContentDocRootScrollFrame,
  6404                                             const FrameMetrics& aMetrics)
  6407   if (aIsRootContentDocRootScrollFrame) {
  6408     return ViewAs<LayerPixel>(ParentLayerSize(aMetrics.mCompositionBounds.Size()),
  6409                               PixelCastJustification::ParentLayerToLayerForRootComposition)
  6410            / aMetrics.LayersPixelsPerCSSPixel();
  6412   nsPresContext* presContext = aFrame->PresContext();
  6413   LayerSize rootCompositionSize;
  6414   nsPresContext* rootPresContext =
  6415     presContext->GetToplevelContentDocumentPresContext();
  6416   if (!rootPresContext) {
  6417     rootPresContext = presContext->GetRootPresContext();
  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;
  6449 #endif
  6450         } else {
  6451           rootCompositionSize = viewSize;
  6455   } else {
  6456     nsIWidget* widget = aFrame->GetNearestWidget();
  6457     nsIntRect widgetBounds;
  6458     widget->GetBounds(widgetBounds);
  6459     rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size()));
  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();
  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();
  6475   return rootCompositionSize / aMetrics.LayersPixelsPerCSSPixel();
  6478 /* static */ nsRect
  6479 nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame)
  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;
  6496     if (aScrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
  6497       contentBounds.x = scrollPosition.x;
  6498       contentBounds.width = 0;
  6500 #endif
  6502     contentBounds.width += aScrollableFrame->GetScrollPortRect().width;
  6503     contentBounds.height += aScrollableFrame->GetScrollPortRect().height;
  6504   } else {
  6505     contentBounds = aRootFrame->GetRect();
  6507   return contentBounds;
  6510 /* static */ nsRect
  6511 nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame* aFrame)
  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));
  6526   if (scrollableRect.width < compSize.width) {
  6527     scrollableRect.x = std::max(0,
  6528                                 scrollableRect.x - (compSize.width - scrollableRect.width));
  6529     scrollableRect.width = compSize.width;
  6532   if (scrollableRect.height < compSize.height) {
  6533     scrollableRect.y = std::max(0,
  6534                                 scrollableRect.y - (compSize.height - scrollableRect.height));
  6535     scrollableRect.height = compSize.height;
  6537   return scrollableRect;
  6540 /* static */ bool
  6541 nsLayoutUtils::WantSubAPZC()
  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;
  6549 #endif
  6550    return wantSubAPZC;
  6553 nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()
  6554   // Use safe default values here
  6555   : mIsWriteOnly(true)
  6556   , mIsStillLoading(false)
  6557   , mCORSUsed(false)
  6558   , mIsPremultiplied(true)
  6562 bool
  6563 nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame)
  6565   return GetAsBlock(aFrame) && !aFrame->IsBlockWrapper();
  6568 bool
  6569 nsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext* aPresContext)
  6571   return aPresContext->IsRootPaginatedDocument() &&
  6572     (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
  6573      aPresContext->Type() == nsPresContext::eContext_PageLayout);
  6576 AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame *aFrame)
  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;
  6592 AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation()
  6594   if (mPresContext) {
  6595     mPresContext->mInflationDisabledForShrinkWrap = mOldValue;

mercurial