1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/nsLayoutUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,6597 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=78: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsLayoutUtils.h" 1.11 + 1.12 +#include "mozilla/ArrayUtils.h" 1.13 +#include "mozilla/BasicEvents.h" 1.14 +#include "mozilla/MemoryReporting.h" 1.15 +#include "nsPresContext.h" 1.16 +#include "nsIContent.h" 1.17 +#include "nsIDOMHTMLDocument.h" 1.18 +#include "nsIDOMHTMLElement.h" 1.19 +#include "nsFrameList.h" 1.20 +#include "nsGkAtoms.h" 1.21 +#include "nsIAtom.h" 1.22 +#include "nsCSSPseudoElements.h" 1.23 +#include "nsCSSAnonBoxes.h" 1.24 +#include "nsCSSColorUtils.h" 1.25 +#include "nsView.h" 1.26 +#include "nsPlaceholderFrame.h" 1.27 +#include "nsIScrollableFrame.h" 1.28 +#include "nsIDOMEvent.h" 1.29 +#include "nsDisplayList.h" 1.30 +#include "nsRegion.h" 1.31 +#include "nsFrameManager.h" 1.32 +#include "nsBlockFrame.h" 1.33 +#include "nsBidiPresUtils.h" 1.34 +#include "imgIContainer.h" 1.35 +#include "ImageOps.h" 1.36 +#include "gfxRect.h" 1.37 +#include "gfxContext.h" 1.38 +#include "nsRenderingContext.h" 1.39 +#include "nsIInterfaceRequestorUtils.h" 1.40 +#include "nsCSSRendering.h" 1.41 +#include "nsThemeConstants.h" 1.42 +#include "nsPIDOMWindow.h" 1.43 +#include "nsIDocShell.h" 1.44 +#include "nsIWidget.h" 1.45 +#include "gfxMatrix.h" 1.46 +#include "gfxPoint3D.h" 1.47 +#include "gfxPrefs.h" 1.48 +#include "gfxTypes.h" 1.49 +#include "nsTArray.h" 1.50 +#include "mozilla/dom/HTMLCanvasElement.h" 1.51 +#include "nsICanvasRenderingContextInternal.h" 1.52 +#include "gfxPlatform.h" 1.53 +#include <algorithm> 1.54 +#include "mozilla/dom/HTMLVideoElement.h" 1.55 +#include "mozilla/dom/HTMLImageElement.h" 1.56 +#include "mozilla/dom/DOMRect.h" 1.57 +#include "imgIRequest.h" 1.58 +#include "nsIImageLoadingContent.h" 1.59 +#include "nsCOMPtr.h" 1.60 +#include "nsCSSProps.h" 1.61 +#include "nsListControlFrame.h" 1.62 +#include "mozilla/dom/Element.h" 1.63 +#include "nsCanvasFrame.h" 1.64 +#include "gfxDrawable.h" 1.65 +#include "gfxUtils.h" 1.66 +#include "nsDataHashtable.h" 1.67 +#include "nsTextFrame.h" 1.68 +#include "nsFontFaceList.h" 1.69 +#include "nsFontInflationData.h" 1.70 +#include "nsSVGUtils.h" 1.71 +#include "SVGTextFrame.h" 1.72 +#include "nsStyleStructInlines.h" 1.73 +#include "nsStyleTransformMatrix.h" 1.74 +#include "nsIFrameInlines.h" 1.75 +#include "ImageContainer.h" 1.76 +#include "nsComputedDOMStyle.h" 1.77 +#include "ActiveLayerTracker.h" 1.78 +#include "mozilla/gfx/2D.h" 1.79 +#include "gfx2DGlue.h" 1.80 +#include "mozilla/LookAndFeel.h" 1.81 +#include "UnitTransforms.h" 1.82 +#include "TiledLayerBuffer.h" // For TILEDLAYERBUFFER_TILE_SIZE 1.83 + 1.84 +#include "mozilla/Preferences.h" 1.85 + 1.86 +#include "mozilla/LookAndFeel.h" 1.87 + 1.88 +#ifdef MOZ_XUL 1.89 +#include "nsXULPopupManager.h" 1.90 +#endif 1.91 + 1.92 +#include "GeckoProfiler.h" 1.93 +#include "nsAnimationManager.h" 1.94 +#include "nsTransitionManager.h" 1.95 +#include "RestyleManager.h" 1.96 + 1.97 +// Additional includes used on B2G by code in GetOrMaybeCreateDisplayPort(). 1.98 +#ifdef MOZ_WIDGET_GONK 1.99 +#include "mozilla/layers/AsyncPanZoomController.h" 1.100 +#endif 1.101 + 1.102 +using namespace mozilla; 1.103 +using namespace mozilla::css; 1.104 +using namespace mozilla::dom; 1.105 +using namespace mozilla::layers; 1.106 +using namespace mozilla::layout; 1.107 +using namespace mozilla::gfx; 1.108 + 1.109 +using mozilla::image::Angle; 1.110 +using mozilla::image::Flip; 1.111 +using mozilla::image::ImageOps; 1.112 +using mozilla::image::Orientation; 1.113 + 1.114 +#define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled" 1.115 +#define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled" 1.116 +#define TEXT_ALIGN_TRUE_ENABLED_PREF_NAME "layout.css.text-align-true-value.enabled" 1.117 + 1.118 +#ifdef DEBUG 1.119 +// TODO: remove, see bug 598468. 1.120 +bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false; 1.121 +#endif // DEBUG 1.122 + 1.123 +typedef FrameMetrics::ViewID ViewID; 1.124 + 1.125 +/* static */ uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine; 1.126 +/* static */ uint32_t nsLayoutUtils::sFontSizeInflationMinTwips; 1.127 +/* static */ uint32_t nsLayoutUtils::sFontSizeInflationLineThreshold; 1.128 +/* static */ int32_t nsLayoutUtils::sFontSizeInflationMappingIntercept; 1.129 +/* static */ uint32_t nsLayoutUtils::sFontSizeInflationMaxRatio; 1.130 +/* static */ bool nsLayoutUtils::sFontSizeInflationForceEnabled; 1.131 +/* static */ bool nsLayoutUtils::sFontSizeInflationDisabledInMasterProcess; 1.132 +/* static */ bool nsLayoutUtils::sInvalidationDebuggingIsEnabled; 1.133 +/* static */ bool nsLayoutUtils::sCSSVariablesEnabled; 1.134 +/* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled; 1.135 + 1.136 +static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID; 1.137 + 1.138 +typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap; 1.139 +static ContentMap* sContentMap = nullptr; 1.140 +static ContentMap& GetContentMap() { 1.141 + if (!sContentMap) { 1.142 + sContentMap = new ContentMap(); 1.143 + } 1.144 + return *sContentMap; 1.145 +} 1.146 + 1.147 +// When the pref "layout.css.grid.enabled" changes, this function is invoked 1.148 +// to let us update kDisplayKTable, to selectively disable or restore the 1.149 +// entries for "grid" and "inline-grid" in that table. 1.150 +static void 1.151 +GridEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) 1.152 +{ 1.153 + MOZ_ASSERT(strncmp(aPrefName, GRID_ENABLED_PREF_NAME, 1.154 + ArrayLength(GRID_ENABLED_PREF_NAME)) == 0, 1.155 + "We only registered this callback for a single pref, so it " 1.156 + "should only be called for that pref"); 1.157 + 1.158 + static int32_t sIndexOfGridInDisplayTable; 1.159 + static int32_t sIndexOfInlineGridInDisplayTable; 1.160 + static bool sAreGridKeywordIndicesInitialized; // initialized to false 1.161 + 1.162 + bool isGridEnabled = 1.163 + Preferences::GetBool(GRID_ENABLED_PREF_NAME, false); 1.164 + if (!sAreGridKeywordIndicesInitialized) { 1.165 + // First run: find the position of "grid" and "inline-grid" in 1.166 + // kDisplayKTable. 1.167 + sIndexOfGridInDisplayTable = 1.168 + nsCSSProps::FindIndexOfKeyword(eCSSKeyword_grid, 1.169 + nsCSSProps::kDisplayKTable); 1.170 + MOZ_ASSERT(sIndexOfGridInDisplayTable >= 0, 1.171 + "Couldn't find grid in kDisplayKTable"); 1.172 + sIndexOfInlineGridInDisplayTable = 1.173 + nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_grid, 1.174 + nsCSSProps::kDisplayKTable); 1.175 + MOZ_ASSERT(sIndexOfInlineGridInDisplayTable >= 0, 1.176 + "Couldn't find inline-grid in kDisplayKTable"); 1.177 + sAreGridKeywordIndicesInitialized = true; 1.178 + } 1.179 + 1.180 + // OK -- now, stomp on or restore the "grid" entries in kDisplayKTable, 1.181 + // depending on whether the grid pref is enabled vs. disabled. 1.182 + if (sIndexOfGridInDisplayTable >= 0) { 1.183 + nsCSSProps::kDisplayKTable[sIndexOfGridInDisplayTable] = 1.184 + isGridEnabled ? eCSSKeyword_grid : eCSSKeyword_UNKNOWN; 1.185 + } 1.186 + if (sIndexOfInlineGridInDisplayTable >= 0) { 1.187 + nsCSSProps::kDisplayKTable[sIndexOfInlineGridInDisplayTable] = 1.188 + isGridEnabled ? eCSSKeyword_inline_grid : eCSSKeyword_UNKNOWN; 1.189 + } 1.190 +} 1.191 + 1.192 +// When the pref "layout.css.sticky.enabled" changes, this function is invoked 1.193 +// to let us update kPositionKTable, to selectively disable or restore the 1.194 +// entry for "sticky" in that table. 1.195 +static void 1.196 +StickyEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) 1.197 +{ 1.198 + MOZ_ASSERT(strncmp(aPrefName, STICKY_ENABLED_PREF_NAME, 1.199 + ArrayLength(STICKY_ENABLED_PREF_NAME)) == 0, 1.200 + "We only registered this callback for a single pref, so it " 1.201 + "should only be called for that pref"); 1.202 + 1.203 + static int32_t sIndexOfStickyInPositionTable; 1.204 + static bool sIsStickyKeywordIndexInitialized; // initialized to false 1.205 + 1.206 + bool isStickyEnabled = 1.207 + Preferences::GetBool(STICKY_ENABLED_PREF_NAME, false); 1.208 + 1.209 + if (!sIsStickyKeywordIndexInitialized) { 1.210 + // First run: find the position of "sticky" in kPositionKTable. 1.211 + sIndexOfStickyInPositionTable = 1.212 + nsCSSProps::FindIndexOfKeyword(eCSSKeyword_sticky, 1.213 + nsCSSProps::kPositionKTable); 1.214 + MOZ_ASSERT(sIndexOfStickyInPositionTable >= 0, 1.215 + "Couldn't find sticky in kPositionKTable"); 1.216 + sIsStickyKeywordIndexInitialized = true; 1.217 + } 1.218 + 1.219 + // OK -- now, stomp on or restore the "sticky" entry in kPositionKTable, 1.220 + // depending on whether the sticky pref is enabled vs. disabled. 1.221 + nsCSSProps::kPositionKTable[sIndexOfStickyInPositionTable] = 1.222 + isStickyEnabled ? eCSSKeyword_sticky : eCSSKeyword_UNKNOWN; 1.223 +} 1.224 + 1.225 +// When the pref "layout.css.text-align-true-value.enabled" changes, this 1.226 +// function is called to let us update kTextAlignKTable & kTextAlignLastKTable, 1.227 +// to selectively disable or restore the entries for "true" in those tables. 1.228 +static void 1.229 +TextAlignTrueEnabledPrefChangeCallback(const char* aPrefName, void* aClosure) 1.230 +{ 1.231 + NS_ASSERTION(strcmp(aPrefName, TEXT_ALIGN_TRUE_ENABLED_PREF_NAME) == 0, 1.232 + "Did you misspell " TEXT_ALIGN_TRUE_ENABLED_PREF_NAME " ?"); 1.233 + 1.234 + static bool sIsInitialized; 1.235 + static int32_t sIndexOfTrueInTextAlignTable; 1.236 + static int32_t sIndexOfTrueInTextAlignLastTable; 1.237 + bool isTextAlignTrueEnabled = 1.238 + Preferences::GetBool(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, false); 1.239 + 1.240 + if (!sIsInitialized) { 1.241 + // First run: find the position of "true" in kTextAlignKTable. 1.242 + sIndexOfTrueInTextAlignTable = 1.243 + nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true, 1.244 + nsCSSProps::kTextAlignKTable); 1.245 + // First run: find the position of "true" in kTextAlignLastKTable. 1.246 + sIndexOfTrueInTextAlignLastTable = 1.247 + nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true, 1.248 + nsCSSProps::kTextAlignLastKTable); 1.249 + sIsInitialized = true; 1.250 + } 1.251 + 1.252 + // OK -- now, stomp on or restore the "true" entry in the keyword tables, 1.253 + // depending on whether the pref is enabled vs. disabled. 1.254 + MOZ_ASSERT(sIndexOfTrueInTextAlignTable >= 0); 1.255 + nsCSSProps::kTextAlignKTable[sIndexOfTrueInTextAlignTable] = 1.256 + isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN; 1.257 + MOZ_ASSERT(sIndexOfTrueInTextAlignLastTable >= 0); 1.258 + nsCSSProps::kTextAlignLastKTable[sIndexOfTrueInTextAlignLastTable] = 1.259 + isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN; 1.260 +} 1.261 + 1.262 +template <class AnimationsOrTransitions> 1.263 +static AnimationsOrTransitions* 1.264 +HasAnimationOrTransitionForCompositor(nsIContent* aContent, 1.265 + nsIAtom* aAnimationProperty, 1.266 + nsCSSProperty aProperty) 1.267 +{ 1.268 + AnimationsOrTransitions* animations = 1.269 + static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty)); 1.270 + if (animations) { 1.271 + bool propertyMatches = animations->HasAnimationOfProperty(aProperty); 1.272 + if (propertyMatches && 1.273 + animations->CanPerformOnCompositorThread( 1.274 + CommonElementAnimationData::CanAnimate_AllowPartial)) { 1.275 + return animations; 1.276 + } 1.277 + } 1.278 + 1.279 + return nullptr; 1.280 +} 1.281 + 1.282 +bool 1.283 +nsLayoutUtils::HasAnimationsForCompositor(nsIContent* aContent, 1.284 + nsCSSProperty aProperty) 1.285 +{ 1.286 + if (!aContent->MayHaveAnimations()) 1.287 + return false; 1.288 + return HasAnimationOrTransitionForCompositor<ElementAnimations> 1.289 + (aContent, nsGkAtoms::animationsProperty, aProperty) || 1.290 + HasAnimationOrTransitionForCompositor<ElementTransitions> 1.291 + (aContent, nsGkAtoms::transitionsProperty, aProperty); 1.292 +} 1.293 + 1.294 +template <class AnimationsOrTransitions> 1.295 +AnimationsOrTransitions* 1.296 +mozilla::HasAnimationOrTransition(nsIContent* aContent, 1.297 + nsIAtom* aAnimationProperty, 1.298 + nsCSSProperty aProperty) 1.299 +{ 1.300 + AnimationsOrTransitions* animations = 1.301 + static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty)); 1.302 + if (animations) { 1.303 + bool propertyMatches = animations->HasAnimationOfProperty(aProperty); 1.304 + if (propertyMatches) { 1.305 + return animations; 1.306 + } 1.307 + } 1.308 + 1.309 + return nullptr; 1.310 +} 1.311 + 1.312 +template ElementAnimations* 1.313 +mozilla::HasAnimationOrTransition<ElementAnimations>(nsIContent* aContent, 1.314 + nsIAtom* aAnimationProperty, 1.315 + nsCSSProperty aProperty); 1.316 + 1.317 +template ElementTransitions* 1.318 +mozilla::HasAnimationOrTransition<ElementTransitions>(nsIContent* aContent, 1.319 + nsIAtom* aAnimationProperty, 1.320 + nsCSSProperty aProperty); 1.321 + 1.322 + 1.323 +bool 1.324 +nsLayoutUtils::HasAnimations(nsIContent* aContent, 1.325 + nsCSSProperty aProperty) 1.326 +{ 1.327 + if (!aContent->MayHaveAnimations()) 1.328 + return false; 1.329 + return HasAnimationOrTransition<ElementAnimations> 1.330 + (aContent, nsGkAtoms::animationsProperty, aProperty) || 1.331 + HasAnimationOrTransition<ElementTransitions> 1.332 + (aContent, nsGkAtoms::transitionsProperty, aProperty); 1.333 +} 1.334 + 1.335 +static gfxSize 1.336 +GetScaleForValue(const nsStyleAnimation::Value& aValue, 1.337 + nsIFrame* aFrame) 1.338 +{ 1.339 + if (!aFrame) { 1.340 + NS_WARNING("No frame."); 1.341 + return gfxSize(); 1.342 + } 1.343 + if (aValue.GetUnit() != nsStyleAnimation::eUnit_Transform) { 1.344 + NS_WARNING("Expected a transform."); 1.345 + return gfxSize(); 1.346 + } 1.347 + 1.348 + nsCSSValueSharedList* list = aValue.GetCSSValueSharedListValue(); 1.349 + MOZ_ASSERT(list->mHead); 1.350 + 1.351 + if (list->mHead->mValue.GetUnit() == eCSSUnit_None) { 1.352 + // There is an animation, but no actual transform yet. 1.353 + return gfxSize(); 1.354 + } 1.355 + 1.356 + nsRect frameBounds = aFrame->GetRect(); 1.357 + bool dontCare; 1.358 + gfx3DMatrix transform = nsStyleTransformMatrix::ReadTransforms( 1.359 + list->mHead, 1.360 + aFrame->StyleContext(), 1.361 + aFrame->PresContext(), dontCare, frameBounds, 1.362 + aFrame->PresContext()->AppUnitsPerDevPixel()); 1.363 + 1.364 + gfxMatrix transform2d; 1.365 + bool canDraw2D = transform.CanDraw2D(&transform2d); 1.366 + if (!canDraw2D) { 1.367 + return gfxSize(); 1.368 + } 1.369 + 1.370 + return transform2d.ScaleFactors(true); 1.371 +} 1.372 + 1.373 +float 1.374 +GetSuitableScale(float aMaxScale, float aMinScale) 1.375 +{ 1.376 + // If the minimum scale >= 1.0f, use it; if the maximum <= 1.0f, use it; 1.377 + // otherwise use 1.0f. 1.378 + if (aMinScale >= 1.0f) { 1.379 + return aMinScale; 1.380 + } 1.381 + else if (aMaxScale <= 1.0f) { 1.382 + return aMaxScale; 1.383 + } 1.384 + 1.385 + return 1.0f; 1.386 +} 1.387 + 1.388 +gfxSize 1.389 +nsLayoutUtils::ComputeSuitableScaleForAnimation(nsIContent* aContent) 1.390 +{ 1.391 + gfxSize maxScale(1.0f, 1.0f); 1.392 + gfxSize minScale(1.0f, 1.0f); 1.393 + 1.394 + ElementAnimations* animations = HasAnimationOrTransitionForCompositor<ElementAnimations> 1.395 + (aContent, nsGkAtoms::animationsProperty, eCSSProperty_transform); 1.396 + if (animations) { 1.397 + for (uint32_t animIdx = animations->mAnimations.Length(); animIdx-- != 0; ) { 1.398 + mozilla::StyleAnimation& anim = animations->mAnimations[animIdx]; 1.399 + for (uint32_t propIdx = anim.mProperties.Length(); propIdx-- != 0; ) { 1.400 + AnimationProperty& prop = anim.mProperties[propIdx]; 1.401 + if (prop.mProperty == eCSSProperty_transform) { 1.402 + for (uint32_t segIdx = prop.mSegments.Length(); segIdx-- != 0; ) { 1.403 + AnimationPropertySegment& segment = prop.mSegments[segIdx]; 1.404 + gfxSize from = GetScaleForValue(segment.mFromValue, 1.405 + aContent->GetPrimaryFrame()); 1.406 + maxScale.width = std::max<float>(maxScale.width, from.width); 1.407 + maxScale.height = std::max<float>(maxScale.height, from.height); 1.408 + minScale.width = std::min<float>(minScale.width, from.width); 1.409 + minScale.height = std::min<float>(minScale.height, from.height); 1.410 + gfxSize to = GetScaleForValue(segment.mToValue, 1.411 + aContent->GetPrimaryFrame()); 1.412 + maxScale.width = std::max<float>(maxScale.width, to.width); 1.413 + maxScale.height = std::max<float>(maxScale.height, to.height); 1.414 + minScale.width = std::min<float>(minScale.width, to.width); 1.415 + minScale.height = std::min<float>(minScale.height, to.height); 1.416 + } 1.417 + } 1.418 + } 1.419 + } 1.420 + } 1.421 + 1.422 + ElementTransitions* transitions = HasAnimationOrTransitionForCompositor<ElementTransitions> 1.423 + (aContent, nsGkAtoms::transitionsProperty, eCSSProperty_transform); 1.424 + if (transitions) { 1.425 + for (uint32_t i = 0, i_end = transitions->mPropertyTransitions.Length(); 1.426 + i < i_end; ++i){ 1.427 + ElementPropertyTransition &pt = transitions->mPropertyTransitions[i]; 1.428 + if (pt.IsRemovedSentinel()) { 1.429 + continue; 1.430 + } 1.431 + MOZ_ASSERT(pt.mProperties.Length() == 1, 1.432 + "Should have one animation property for a transition"); 1.433 + MOZ_ASSERT(pt.mProperties[0].mSegments.Length() == 1, 1.434 + "Animation property should have one segment for a transition"); 1.435 + const AnimationPropertySegment& segment = pt.mProperties[0].mSegments[0]; 1.436 + 1.437 + if (pt.mProperties[0].mProperty == eCSSProperty_transform) { 1.438 + gfxSize start = GetScaleForValue(segment.mFromValue, 1.439 + aContent->GetPrimaryFrame()); 1.440 + maxScale.width = std::max<float>(maxScale.width, start.width); 1.441 + maxScale.height = std::max<float>(maxScale.height, start.height); 1.442 + minScale.width = std::min<float>(minScale.width, start.width); 1.443 + minScale.height = std::min<float>(minScale.height, start.height); 1.444 + gfxSize end = GetScaleForValue(segment.mToValue, 1.445 + aContent->GetPrimaryFrame()); 1.446 + maxScale.width = std::max<float>(maxScale.width, end.width); 1.447 + maxScale.height = std::max<float>(maxScale.height, end.height); 1.448 + minScale.width = std::min<float>(minScale.width, end.width); 1.449 + minScale.height = std::min<float>(minScale.height, end.height); 1.450 + } 1.451 + } 1.452 + } 1.453 + 1.454 + return gfxSize(GetSuitableScale(maxScale.width, minScale.width), 1.455 + GetSuitableScale(maxScale.height, minScale.height)); 1.456 +} 1.457 + 1.458 +bool 1.459 +nsLayoutUtils::AreAsyncAnimationsEnabled() 1.460 +{ 1.461 + static bool sAreAsyncAnimationsEnabled; 1.462 + static bool sAsyncPrefCached = false; 1.463 + 1.464 + if (!sAsyncPrefCached) { 1.465 + sAsyncPrefCached = true; 1.466 + Preferences::AddBoolVarCache(&sAreAsyncAnimationsEnabled, 1.467 + "layers.offmainthreadcomposition.async-animations"); 1.468 + } 1.469 + 1.470 + return sAreAsyncAnimationsEnabled && 1.471 + gfxPlatform::OffMainThreadCompositingEnabled(); 1.472 +} 1.473 + 1.474 +bool 1.475 +nsLayoutUtils::IsAnimationLoggingEnabled() 1.476 +{ 1.477 + static bool sShouldLog; 1.478 + static bool sShouldLogPrefCached; 1.479 + 1.480 + if (!sShouldLogPrefCached) { 1.481 + sShouldLogPrefCached = true; 1.482 + Preferences::AddBoolVarCache(&sShouldLog, 1.483 + "layers.offmainthreadcomposition.log-animations"); 1.484 + } 1.485 + 1.486 + return sShouldLog; 1.487 +} 1.488 + 1.489 +bool 1.490 +nsLayoutUtils::UseBackgroundNearestFiltering() 1.491 +{ 1.492 + static bool sUseBackgroundNearestFilteringEnabled; 1.493 + static bool sUseBackgroundNearestFilteringPrefInitialised = false; 1.494 + 1.495 + if (!sUseBackgroundNearestFilteringPrefInitialised) { 1.496 + sUseBackgroundNearestFilteringPrefInitialised = true; 1.497 + sUseBackgroundNearestFilteringEnabled = 1.498 + Preferences::GetBool("gfx.filter.nearest.force-enabled", false); 1.499 + } 1.500 + 1.501 + return sUseBackgroundNearestFilteringEnabled; 1.502 +} 1.503 + 1.504 +bool 1.505 +nsLayoutUtils::GPUImageScalingEnabled() 1.506 +{ 1.507 + static bool sGPUImageScalingEnabled; 1.508 + static bool sGPUImageScalingPrefInitialised = false; 1.509 + 1.510 + if (!sGPUImageScalingPrefInitialised) { 1.511 + sGPUImageScalingPrefInitialised = true; 1.512 + sGPUImageScalingEnabled = 1.513 + Preferences::GetBool("layout.gpu-image-scaling.enabled", false); 1.514 + } 1.515 + 1.516 + return sGPUImageScalingEnabled; 1.517 +} 1.518 + 1.519 +bool 1.520 +nsLayoutUtils::AnimatedImageLayersEnabled() 1.521 +{ 1.522 + static bool sAnimatedImageLayersEnabled; 1.523 + static bool sAnimatedImageLayersPrefCached = false; 1.524 + 1.525 + if (!sAnimatedImageLayersPrefCached) { 1.526 + sAnimatedImageLayersPrefCached = true; 1.527 + Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled, 1.528 + "layout.animated-image-layers.enabled", 1.529 + false); 1.530 + } 1.531 + 1.532 + return sAnimatedImageLayersEnabled; 1.533 +} 1.534 + 1.535 +bool 1.536 +nsLayoutUtils::CSSFiltersEnabled() 1.537 +{ 1.538 + static bool sCSSFiltersEnabled; 1.539 + static bool sCSSFiltersPrefCached = false; 1.540 + 1.541 + if (!sCSSFiltersPrefCached) { 1.542 + sCSSFiltersPrefCached = true; 1.543 + Preferences::AddBoolVarCache(&sCSSFiltersEnabled, 1.544 + "layout.css.filters.enabled", 1.545 + false); 1.546 + } 1.547 + 1.548 + return sCSSFiltersEnabled; 1.549 +} 1.550 + 1.551 +bool 1.552 +nsLayoutUtils::UnsetValueEnabled() 1.553 +{ 1.554 + static bool sUnsetValueEnabled; 1.555 + static bool sUnsetValuePrefCached = false; 1.556 + 1.557 + if (!sUnsetValuePrefCached) { 1.558 + sUnsetValuePrefCached = true; 1.559 + Preferences::AddBoolVarCache(&sUnsetValueEnabled, 1.560 + "layout.css.unset-value.enabled", 1.561 + false); 1.562 + } 1.563 + 1.564 + return sUnsetValueEnabled; 1.565 +} 1.566 + 1.567 +bool 1.568 +nsLayoutUtils::IsTextAlignTrueValueEnabled() 1.569 +{ 1.570 + static bool sTextAlignTrueValueEnabled; 1.571 + static bool sTextAlignTrueValueEnabledPrefCached = false; 1.572 + 1.573 + if (!sTextAlignTrueValueEnabledPrefCached) { 1.574 + sTextAlignTrueValueEnabledPrefCached = true; 1.575 + Preferences::AddBoolVarCache(&sTextAlignTrueValueEnabled, 1.576 + TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, 1.577 + false); 1.578 + } 1.579 + 1.580 + return sTextAlignTrueValueEnabled; 1.581 +} 1.582 + 1.583 +void 1.584 +nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame, 1.585 + nsOverflowAreas& aOverflowAreas, 1.586 + FrameChildListIDs aSkipChildLists) 1.587 +{ 1.588 + // Iterate over all children except pop-ups. 1.589 + FrameChildListIDs skip = aSkipChildLists | 1.590 + nsIFrame::kSelectPopupList | nsIFrame::kPopupList; 1.591 + for (nsIFrame::ChildListIterator childLists(aFrame); 1.592 + !childLists.IsDone(); childLists.Next()) { 1.593 + if (skip.Contains(childLists.CurrentID())) { 1.594 + continue; 1.595 + } 1.596 + 1.597 + nsFrameList children = childLists.CurrentList(); 1.598 + for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { 1.599 + nsIFrame* child = e.get(); 1.600 + nsOverflowAreas childOverflow = 1.601 + child->GetOverflowAreas() + child->GetPosition(); 1.602 + aOverflowAreas.UnionWith(childOverflow); 1.603 + } 1.604 + } 1.605 +} 1.606 + 1.607 +static void DestroyViewID(void* aObject, nsIAtom* aPropertyName, 1.608 + void* aPropertyValue, void* aData) 1.609 +{ 1.610 + ViewID* id = static_cast<ViewID*>(aPropertyValue); 1.611 + GetContentMap().Remove(*id); 1.612 + delete id; 1.613 +} 1.614 + 1.615 +/** 1.616 + * A namespace class for static layout utilities. 1.617 + */ 1.618 + 1.619 +bool 1.620 +nsLayoutUtils::FindIDFor(const nsIContent* aContent, ViewID* aOutViewId) 1.621 +{ 1.622 + void* scrollIdProperty = aContent->GetProperty(nsGkAtoms::RemoteId); 1.623 + if (scrollIdProperty) { 1.624 + *aOutViewId = *static_cast<ViewID*>(scrollIdProperty); 1.625 + return true; 1.626 + } 1.627 + return false; 1.628 +} 1.629 + 1.630 +ViewID 1.631 +nsLayoutUtils::FindOrCreateIDFor(nsIContent* aContent) 1.632 +{ 1.633 + ViewID scrollId; 1.634 + 1.635 + if (!FindIDFor(aContent, &scrollId)) { 1.636 + scrollId = sScrollIdCounter++; 1.637 + aContent->SetProperty(nsGkAtoms::RemoteId, new ViewID(scrollId), 1.638 + DestroyViewID); 1.639 + GetContentMap().Put(scrollId, aContent); 1.640 + } 1.641 + 1.642 + return scrollId; 1.643 +} 1.644 + 1.645 +nsIContent* 1.646 +nsLayoutUtils::FindContentFor(ViewID aId) 1.647 +{ 1.648 + NS_ABORT_IF_FALSE(aId != FrameMetrics::NULL_SCROLL_ID, 1.649 + "Cannot find a content element in map for null IDs."); 1.650 + nsIContent* content; 1.651 + bool exists = GetContentMap().Get(aId, &content); 1.652 + 1.653 + if (exists) { 1.654 + return content; 1.655 + } else { 1.656 + return nullptr; 1.657 + } 1.658 +} 1.659 + 1.660 +nsIScrollableFrame* 1.661 +nsLayoutUtils::FindScrollableFrameFor(ViewID aId) 1.662 +{ 1.663 + nsIContent* content = FindContentFor(aId); 1.664 + if (!content) { 1.665 + return nullptr; 1.666 + } 1.667 + 1.668 + nsIFrame* scrolledFrame = content->GetPrimaryFrame(); 1.669 + if (scrolledFrame && content->OwnerDoc()->GetRootElement() == content) { 1.670 + // The content is the root element of a subdocument, so return the root scrollable 1.671 + // for the subdocument. 1.672 + scrolledFrame = scrolledFrame->PresContext()->PresShell()->GetRootScrollFrame(); 1.673 + } 1.674 + return scrolledFrame ? scrolledFrame->GetScrollTargetFrame() : nullptr; 1.675 +} 1.676 + 1.677 +bool 1.678 +nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult) 1.679 +{ 1.680 + DisplayPortPropertyData* rectData = 1.681 + static_cast<DisplayPortPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPort)); 1.682 + DisplayPortMarginsPropertyData* marginsData = 1.683 + static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins)); 1.684 + if (!rectData && !marginsData) { 1.685 + return false; 1.686 + } 1.687 + 1.688 + if (aResult) { 1.689 + if (rectData && marginsData) { 1.690 + // choose margins if equal priority 1.691 + if (rectData->mPriority > marginsData->mPriority) { 1.692 + marginsData = nullptr; 1.693 + } else { 1.694 + rectData = nullptr; 1.695 + } 1.696 + } 1.697 + 1.698 + if (rectData) { 1.699 + *aResult = rectData->mRect; 1.700 + } else { 1.701 + nsRect* baseData = 1.702 + static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase)); 1.703 + nsRect base; 1.704 + if (baseData) { 1.705 + base = *baseData; 1.706 + } 1.707 + 1.708 + nsIFrame* frame = aContent->GetPrimaryFrame(); 1.709 + if (frame) { 1.710 + bool isRoot = false; 1.711 + if (aContent->OwnerDoc()->GetRootElement() == aContent) { 1.712 + // We want the scroll frame, the root scroll frame differs from all 1.713 + // others in that the primary frame is not the scroll frame. 1.714 + frame = frame->PresContext()->PresShell()->GetRootScrollFrame(); 1.715 + isRoot = true; 1.716 + } 1.717 + 1.718 + // first convert the base rect to layer pixels 1.719 + nsPresContext* presContext = frame->PresContext(); 1.720 + int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); 1.721 + gfxSize res = presContext->PresShell()->GetCumulativeResolution(); 1.722 + gfxSize parentRes = res; 1.723 + if (isRoot) { 1.724 + // the base rect for root scroll frames is specified in the parent document 1.725 + // coordinate space, so it doesn't include the local resolution. 1.726 + gfxSize localRes = presContext->PresShell()->GetResolution(); 1.727 + parentRes.width /= localRes.width; 1.728 + parentRes.height /= localRes.height; 1.729 + } 1.730 + LayerRect rect; 1.731 + rect.x = parentRes.width * NSAppUnitsToFloatPixels(base.x, auPerDevPixel); 1.732 + rect.y = parentRes.height * NSAppUnitsToFloatPixels(base.y, auPerDevPixel); 1.733 + rect.width = 1.734 + parentRes.width * NSAppUnitsToFloatPixels(base.width, auPerDevPixel); 1.735 + rect.height = 1.736 + parentRes.height * NSAppUnitsToFloatPixels(base.height, auPerDevPixel); 1.737 + 1.738 + rect.Inflate(marginsData->mMargins); 1.739 + 1.740 + nsIScrollableFrame* scrollableFrame = frame->GetScrollTargetFrame(); 1.741 + nsPoint scrollPos( 1.742 + scrollableFrame ? scrollableFrame->GetScrollPosition() : nsPoint(0,0)); 1.743 + if (marginsData->mAlignmentX > 0 || marginsData->mAlignmentY > 0) { 1.744 + // Avoid division by zero. 1.745 + if (marginsData->mAlignmentX == 0) { 1.746 + marginsData->mAlignmentX = 1; 1.747 + } 1.748 + if (marginsData->mAlignmentY == 0) { 1.749 + marginsData->mAlignmentY = 1; 1.750 + } 1.751 + 1.752 + LayerPoint scrollPosLayer( 1.753 + res.width * NSAppUnitsToFloatPixels(scrollPos.x, auPerDevPixel), 1.754 + res.height * NSAppUnitsToFloatPixels(scrollPos.y, auPerDevPixel)); 1.755 + rect += scrollPosLayer; 1.756 + 1.757 + // Inflate the rectangle by 1 so that we always push to the next tile 1.758 + // boundary. This is desirable to stop from having a rectangle with a 1.759 + // moving origin occasionally being smaller when it coincidentally lines 1.760 + // up to tile boundaries. 1.761 + rect.Inflate(1); 1.762 + 1.763 + float left = 1.764 + marginsData->mAlignmentX * floor(rect.x / marginsData->mAlignmentX); 1.765 + float top = 1.766 + marginsData->mAlignmentY * floor(rect.y / marginsData->mAlignmentY); 1.767 + float right = 1.768 + marginsData->mAlignmentX * ceil(rect.XMost() / marginsData->mAlignmentX); 1.769 + float bottom = 1.770 + marginsData->mAlignmentY * ceil(rect.YMost() / marginsData->mAlignmentY); 1.771 + rect = LayerRect(left, top, right - left, bottom - top); 1.772 + rect -= scrollPosLayer; 1.773 + } 1.774 + 1.775 + nsRect result; 1.776 + result.x = NSFloatPixelsToAppUnits(rect.x / res.width, auPerDevPixel); 1.777 + result.y = NSFloatPixelsToAppUnits(rect.y / res.height, auPerDevPixel); 1.778 + result.width = 1.779 + NSFloatPixelsToAppUnits(rect.width / res.width, auPerDevPixel); 1.780 + result.height = 1.781 + NSFloatPixelsToAppUnits(rect.height / res.height, auPerDevPixel); 1.782 + 1.783 + // Finally, clamp the display port to the expanded scrollable rect. 1.784 + nsRect expandedScrollableRect = CalculateExpandedScrollableRect(frame); 1.785 + result = expandedScrollableRect.Intersect(result + scrollPos) - scrollPos; 1.786 + 1.787 + *aResult = result; 1.788 + } 1.789 + } 1.790 + } 1.791 + 1.792 + return true; 1.793 +} 1.794 + 1.795 +void 1.796 +nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, 1.797 + nsIPresShell* aPresShell, 1.798 + const LayerMargin& aMargins, 1.799 + uint32_t aAlignmentX, 1.800 + uint32_t aAlignmentY, 1.801 + uint32_t aPriority, 1.802 + RepaintMode aRepaintMode) 1.803 +{ 1.804 + DisplayPortMarginsPropertyData* currentData = 1.805 + static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins)); 1.806 + if (currentData && currentData->mPriority > aPriority) { 1.807 + return; 1.808 + } 1.809 + 1.810 + aContent->SetProperty(nsGkAtoms::DisplayPortMargins, 1.811 + new DisplayPortMarginsPropertyData( 1.812 + aMargins, aAlignmentX, aAlignmentY, aPriority), 1.813 + nsINode::DeleteProperty<DisplayPortMarginsPropertyData>); 1.814 + 1.815 + nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame(); 1.816 + if (rootScrollFrame && aContent == rootScrollFrame->GetContent()) { 1.817 + // We are setting a root displayport for a document. 1.818 + // The pres shell needs a special flag set. 1.819 + aPresShell->SetIgnoreViewportScrolling(true); 1.820 + } 1.821 + 1.822 + if (aRepaintMode == RepaintMode::Repaint) { 1.823 + nsIFrame* rootFrame = aPresShell->FrameManager()->GetRootFrame(); 1.824 + if (rootFrame) { 1.825 + rootFrame->SchedulePaint(); 1.826 + } 1.827 + } 1.828 +} 1.829 + 1.830 +void 1.831 +nsLayoutUtils::SetDisplayPortBase(nsIContent* aContent, const nsRect& aBase) 1.832 +{ 1.833 + aContent->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aBase), 1.834 + nsINode::DeleteProperty<nsRect>); 1.835 +} 1.836 + 1.837 +void 1.838 +nsLayoutUtils::SetDisplayPortBaseIfNotSet(nsIContent* aContent, const nsRect& aBase) 1.839 +{ 1.840 + if (!aContent->GetProperty(nsGkAtoms::DisplayPortBase)) { 1.841 + SetDisplayPortBase(aContent, aBase); 1.842 + } 1.843 +} 1.844 + 1.845 +bool 1.846 +nsLayoutUtils::GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult) 1.847 +{ 1.848 + void* property = aContent->GetProperty(nsGkAtoms::CriticalDisplayPort); 1.849 + if (!property) { 1.850 + return false; 1.851 + } 1.852 + 1.853 + if (aResult) { 1.854 + *aResult = *static_cast<nsRect*>(property); 1.855 + } 1.856 + return true; 1.857 +} 1.858 + 1.859 +nsIFrame* 1.860 +nsLayoutUtils::LastContinuationWithChild(nsIFrame* aFrame) 1.861 +{ 1.862 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.863 + aFrame = aFrame->LastContinuation(); 1.864 + while (!aFrame->GetFirstPrincipalChild() && 1.865 + aFrame->GetPrevContinuation()) { 1.866 + aFrame = aFrame->GetPrevContinuation(); 1.867 + } 1.868 + return aFrame; 1.869 +} 1.870 + 1.871 +/** 1.872 + * GetFirstChildFrame returns the first "real" child frame of a 1.873 + * given frame. It will descend down into pseudo-frames (unless the 1.874 + * pseudo-frame is the :before generated frame). 1.875 + * @param aFrame the frame 1.876 + * @param aFrame the frame's content node 1.877 + */ 1.878 +static nsIFrame* 1.879 +GetFirstChildFrame(nsIFrame* aFrame, 1.880 + nsIContent* aContent) 1.881 +{ 1.882 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.883 + 1.884 + // Get the first child frame 1.885 + nsIFrame* childFrame = aFrame->GetFirstPrincipalChild(); 1.886 + 1.887 + // If the child frame is a pseudo-frame, then return its first child. 1.888 + // Note that the frame we create for the generated content is also a 1.889 + // pseudo-frame and so don't drill down in that case 1.890 + if (childFrame && 1.891 + childFrame->IsPseudoFrame(aContent) && 1.892 + !childFrame->IsGeneratedContentFrame()) { 1.893 + return GetFirstChildFrame(childFrame, aContent); 1.894 + } 1.895 + 1.896 + return childFrame; 1.897 +} 1.898 + 1.899 +/** 1.900 + * GetLastChildFrame returns the last "real" child frame of a 1.901 + * given frame. It will descend down into pseudo-frames (unless the 1.902 + * pseudo-frame is the :after generated frame). 1.903 + * @param aFrame the frame 1.904 + * @param aFrame the frame's content node 1.905 + */ 1.906 +static nsIFrame* 1.907 +GetLastChildFrame(nsIFrame* aFrame, 1.908 + nsIContent* aContent) 1.909 +{ 1.910 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.911 + 1.912 + // Get the last continuation frame that's a parent 1.913 + nsIFrame* lastParentContinuation = 1.914 + nsLayoutUtils::LastContinuationWithChild(aFrame); 1.915 + nsIFrame* lastChildFrame = 1.916 + lastParentContinuation->GetLastChild(nsIFrame::kPrincipalList); 1.917 + if (lastChildFrame) { 1.918 + // Get the frame's first continuation. This matters in case the frame has 1.919 + // been continued across multiple lines or split by BiDi resolution. 1.920 + lastChildFrame = lastChildFrame->FirstContinuation(); 1.921 + 1.922 + // If the last child frame is a pseudo-frame, then return its last child. 1.923 + // Note that the frame we create for the generated content is also a 1.924 + // pseudo-frame and so don't drill down in that case 1.925 + if (lastChildFrame && 1.926 + lastChildFrame->IsPseudoFrame(aContent) && 1.927 + !lastChildFrame->IsGeneratedContentFrame()) { 1.928 + return GetLastChildFrame(lastChildFrame, aContent); 1.929 + } 1.930 + 1.931 + return lastChildFrame; 1.932 + } 1.933 + 1.934 + return nullptr; 1.935 +} 1.936 + 1.937 +//static 1.938 +FrameChildListID 1.939 +nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) 1.940 +{ 1.941 + nsIFrame::ChildListID id = nsIFrame::kPrincipalList; 1.942 + 1.943 + if (aChildFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { 1.944 + nsIFrame* pif = aChildFrame->GetPrevInFlow(); 1.945 + if (pif->GetParent() == aChildFrame->GetParent()) { 1.946 + id = nsIFrame::kExcessOverflowContainersList; 1.947 + } 1.948 + else { 1.949 + id = nsIFrame::kOverflowContainersList; 1.950 + } 1.951 + } 1.952 + // See if the frame is moved out of the flow 1.953 + else if (aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { 1.954 + // Look at the style information to tell 1.955 + const nsStyleDisplay* disp = aChildFrame->StyleDisplay(); 1.956 + 1.957 + if (NS_STYLE_POSITION_ABSOLUTE == disp->mPosition) { 1.958 + id = nsIFrame::kAbsoluteList; 1.959 + } else if (NS_STYLE_POSITION_FIXED == disp->mPosition) { 1.960 + if (nsLayoutUtils::IsReallyFixedPos(aChildFrame)) { 1.961 + id = nsIFrame::kFixedList; 1.962 + } else { 1.963 + id = nsIFrame::kAbsoluteList; 1.964 + } 1.965 +#ifdef MOZ_XUL 1.966 + } else if (NS_STYLE_DISPLAY_POPUP == disp->mDisplay) { 1.967 + // Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set 1.968 +#ifdef DEBUG 1.969 + nsIFrame* parent = aChildFrame->GetParent(); 1.970 + NS_ASSERTION(parent && parent->GetType() == nsGkAtoms::popupSetFrame, 1.971 + "Unexpected parent"); 1.972 +#endif // DEBUG 1.973 + 1.974 + id = nsIFrame::kPopupList; 1.975 +#endif // MOZ_XUL 1.976 + } else { 1.977 + NS_ASSERTION(aChildFrame->IsFloating(), "not a floated frame"); 1.978 + id = nsIFrame::kFloatList; 1.979 + } 1.980 + 1.981 + } else { 1.982 + nsIAtom* childType = aChildFrame->GetType(); 1.983 + if (nsGkAtoms::menuPopupFrame == childType) { 1.984 + nsIFrame* parent = aChildFrame->GetParent(); 1.985 + MOZ_ASSERT(parent, "nsMenuPopupFrame can't be the root frame"); 1.986 + if (parent) { 1.987 + if (parent->GetType() == nsGkAtoms::popupSetFrame) { 1.988 + id = nsIFrame::kPopupList; 1.989 + } else { 1.990 + nsIFrame* firstPopup = parent->GetFirstChild(nsIFrame::kPopupList); 1.991 + MOZ_ASSERT(!firstPopup || !firstPopup->GetNextSibling(), 1.992 + "We assume popupList only has one child, but it has more."); 1.993 + id = firstPopup == aChildFrame 1.994 + ? nsIFrame::kPopupList 1.995 + : nsIFrame::kPrincipalList; 1.996 + } 1.997 + } else { 1.998 + id = nsIFrame::kPrincipalList; 1.999 + } 1.1000 + } else if (nsGkAtoms::tableColGroupFrame == childType) { 1.1001 + id = nsIFrame::kColGroupList; 1.1002 + } else if (nsGkAtoms::tableCaptionFrame == childType) { 1.1003 + id = nsIFrame::kCaptionList; 1.1004 + } else { 1.1005 + id = nsIFrame::kPrincipalList; 1.1006 + } 1.1007 + } 1.1008 + 1.1009 +#ifdef DEBUG 1.1010 + // Verify that the frame is actually in that child list or in the 1.1011 + // corresponding overflow list. 1.1012 + nsIFrame* parent = aChildFrame->GetParent(); 1.1013 + bool found = parent->GetChildList(id).ContainsFrame(aChildFrame); 1.1014 + if (!found) { 1.1015 + if (!(aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { 1.1016 + found = parent->GetChildList(nsIFrame::kOverflowList) 1.1017 + .ContainsFrame(aChildFrame); 1.1018 + } 1.1019 + else if (aChildFrame->IsFloating()) { 1.1020 + found = parent->GetChildList(nsIFrame::kOverflowOutOfFlowList) 1.1021 + .ContainsFrame(aChildFrame); 1.1022 + if (!found) { 1.1023 + found = parent->GetChildList(nsIFrame::kPushedFloatsList) 1.1024 + .ContainsFrame(aChildFrame); 1.1025 + } 1.1026 + } 1.1027 + // else it's positioned and should have been on the 'id' child list. 1.1028 + NS_POSTCONDITION(found, "not in child list"); 1.1029 + } 1.1030 +#endif 1.1031 + 1.1032 + return id; 1.1033 +} 1.1034 + 1.1035 +// static 1.1036 +nsIFrame* 1.1037 +nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame) 1.1038 +{ 1.1039 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.1040 + NS_ASSERTION(!aFrame->GetPrevContinuation(), 1.1041 + "aFrame must be first continuation"); 1.1042 + 1.1043 + nsIFrame* cif = aFrame->GetContentInsertionFrame(); 1.1044 + nsIFrame* firstFrame = GetFirstChildFrame(cif, aFrame->GetContent()); 1.1045 + 1.1046 + if (firstFrame && IsGeneratedContentFor(nullptr, firstFrame, 1.1047 + nsCSSPseudoElements::before)) { 1.1048 + return firstFrame; 1.1049 + } 1.1050 + 1.1051 + return nullptr; 1.1052 +} 1.1053 + 1.1054 +// static 1.1055 +nsIFrame* 1.1056 +nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame) 1.1057 +{ 1.1058 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.1059 + 1.1060 + nsIFrame* cif = aFrame->GetContentInsertionFrame(); 1.1061 + nsIFrame* lastFrame = GetLastChildFrame(cif, aFrame->GetContent()); 1.1062 + 1.1063 + if (lastFrame && IsGeneratedContentFor(nullptr, lastFrame, 1.1064 + nsCSSPseudoElements::after)) { 1.1065 + return lastFrame; 1.1066 + } 1.1067 + 1.1068 + return nullptr; 1.1069 +} 1.1070 + 1.1071 +// static 1.1072 +nsIFrame* 1.1073 +nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame, nsIAtom* aFrameType) 1.1074 +{ 1.1075 + for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) { 1.1076 + if (frame->GetType() == aFrameType) { 1.1077 + return frame; 1.1078 + } 1.1079 + } 1.1080 + return nullptr; 1.1081 +} 1.1082 + 1.1083 +// static 1.1084 +nsIFrame* 1.1085 +nsLayoutUtils::GetStyleFrame(nsIFrame* aFrame) 1.1086 +{ 1.1087 + if (aFrame->GetType() == nsGkAtoms::tableOuterFrame) { 1.1088 + nsIFrame* inner = aFrame->GetFirstPrincipalChild(); 1.1089 + NS_ASSERTION(inner, "Outer table must have an inner"); 1.1090 + return inner; 1.1091 + } 1.1092 + 1.1093 + return aFrame; 1.1094 +} 1.1095 + 1.1096 +nsIFrame* 1.1097 +nsLayoutUtils::GetStyleFrame(const nsIContent* aContent) 1.1098 +{ 1.1099 + nsIFrame *frame = aContent->GetPrimaryFrame(); 1.1100 + if (!frame) { 1.1101 + return nullptr; 1.1102 + } 1.1103 + 1.1104 + return nsLayoutUtils::GetStyleFrame(frame); 1.1105 +} 1.1106 + 1.1107 +nsIFrame* 1.1108 +nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) { 1.1109 + NS_ASSERTION(nsGkAtoms::placeholderFrame == aFrame->GetType(), 1.1110 + "Must have a placeholder here"); 1.1111 + if (aFrame->GetStateBits() & PLACEHOLDER_FOR_FLOAT) { 1.1112 + nsIFrame *outOfFlowFrame = 1.1113 + nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame); 1.1114 + NS_ASSERTION(outOfFlowFrame->IsFloating(), 1.1115 + "How did that happen?"); 1.1116 + return outOfFlowFrame; 1.1117 + } 1.1118 + 1.1119 + return nullptr; 1.1120 +} 1.1121 + 1.1122 +// static 1.1123 +bool 1.1124 +nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, 1.1125 + nsIFrame* aFrame, 1.1126 + nsIAtom* aPseudoElement) 1.1127 +{ 1.1128 + NS_PRECONDITION(aFrame, "Must have a frame"); 1.1129 + NS_PRECONDITION(aPseudoElement, "Must have a pseudo name"); 1.1130 + 1.1131 + if (!aFrame->IsGeneratedContentFrame()) { 1.1132 + return false; 1.1133 + } 1.1134 + nsIFrame* parent = aFrame->GetParent(); 1.1135 + NS_ASSERTION(parent, "Generated content can't be root frame"); 1.1136 + if (parent->IsGeneratedContentFrame()) { 1.1137 + // Not the root of the generated content 1.1138 + return false; 1.1139 + } 1.1140 + 1.1141 + if (aContent && parent->GetContent() != aContent) { 1.1142 + return false; 1.1143 + } 1.1144 + 1.1145 + return (aFrame->GetContent()->Tag() == nsGkAtoms::mozgeneratedcontentbefore) == 1.1146 + (aPseudoElement == nsCSSPseudoElements::before); 1.1147 +} 1.1148 + 1.1149 +// static 1.1150 +nsIFrame* 1.1151 +nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame, 1.1152 + nsPoint* aExtraOffset) 1.1153 +{ 1.1154 + nsIFrame* p = aFrame->GetParent(); 1.1155 + if (p) 1.1156 + return p; 1.1157 + 1.1158 + nsView* v = aFrame->GetView(); 1.1159 + if (!v) 1.1160 + return nullptr; 1.1161 + v = v->GetParent(); // anonymous inner view 1.1162 + if (!v) 1.1163 + return nullptr; 1.1164 + if (aExtraOffset) { 1.1165 + *aExtraOffset += v->GetPosition(); 1.1166 + } 1.1167 + v = v->GetParent(); // subdocumentframe's view 1.1168 + return v ? v->GetFrame() : nullptr; 1.1169 +} 1.1170 + 1.1171 +// static 1.1172 +bool 1.1173 +nsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame, 1.1174 + nsIFrame* aCommonAncestor) 1.1175 +{ 1.1176 + if (aFrame == aAncestorFrame) 1.1177 + return false; 1.1178 + return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor); 1.1179 +} 1.1180 + 1.1181 +// static 1.1182 +bool 1.1183 +nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame, const nsIFrame* aFrame, 1.1184 + const nsIFrame* aCommonAncestor) 1.1185 +{ 1.1186 + for (const nsIFrame* f = aFrame; f != aCommonAncestor; 1.1187 + f = GetCrossDocParentFrame(f)) { 1.1188 + if (f == aAncestorFrame) 1.1189 + return true; 1.1190 + } 1.1191 + return aCommonAncestor == aAncestorFrame; 1.1192 +} 1.1193 + 1.1194 +// static 1.1195 +bool 1.1196 +nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame, 1.1197 + nsIFrame* aCommonAncestor) 1.1198 +{ 1.1199 + if (aFrame == aAncestorFrame) 1.1200 + return false; 1.1201 + for (nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) { 1.1202 + if (f == aAncestorFrame) 1.1203 + return true; 1.1204 + } 1.1205 + return aCommonAncestor == aAncestorFrame; 1.1206 +} 1.1207 + 1.1208 +// static 1.1209 +int32_t 1.1210 +nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1, 1.1211 + nsIContent* aContent2, 1.1212 + int32_t aIf1Ancestor, 1.1213 + int32_t aIf2Ancestor, 1.1214 + const nsIContent* aCommonAncestor) 1.1215 +{ 1.1216 + NS_PRECONDITION(aContent1, "aContent1 must not be null"); 1.1217 + NS_PRECONDITION(aContent2, "aContent2 must not be null"); 1.1218 + 1.1219 + nsAutoTArray<nsINode*, 32> content1Ancestors; 1.1220 + nsINode* c1; 1.1221 + for (c1 = aContent1; c1 && c1 != aCommonAncestor; c1 = c1->GetParentNode()) { 1.1222 + content1Ancestors.AppendElement(c1); 1.1223 + } 1.1224 + if (!c1 && aCommonAncestor) { 1.1225 + // So, it turns out aCommonAncestor was not an ancestor of c1. Oops. 1.1226 + // Never mind. We can continue as if aCommonAncestor was null. 1.1227 + aCommonAncestor = nullptr; 1.1228 + } 1.1229 + 1.1230 + nsAutoTArray<nsINode*, 32> content2Ancestors; 1.1231 + nsINode* c2; 1.1232 + for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetParentNode()) { 1.1233 + content2Ancestors.AppendElement(c2); 1.1234 + } 1.1235 + if (!c2 && aCommonAncestor) { 1.1236 + // So, it turns out aCommonAncestor was not an ancestor of c2. 1.1237 + // We need to retry with no common ancestor hint. 1.1238 + return DoCompareTreePosition(aContent1, aContent2, 1.1239 + aIf1Ancestor, aIf2Ancestor, nullptr); 1.1240 + } 1.1241 + 1.1242 + int last1 = content1Ancestors.Length() - 1; 1.1243 + int last2 = content2Ancestors.Length() - 1; 1.1244 + nsINode* content1Ancestor = nullptr; 1.1245 + nsINode* content2Ancestor = nullptr; 1.1246 + while (last1 >= 0 && last2 >= 0 1.1247 + && ((content1Ancestor = content1Ancestors.ElementAt(last1)) == 1.1248 + (content2Ancestor = content2Ancestors.ElementAt(last2)))) { 1.1249 + last1--; 1.1250 + last2--; 1.1251 + } 1.1252 + 1.1253 + if (last1 < 0) { 1.1254 + if (last2 < 0) { 1.1255 + NS_ASSERTION(aContent1 == aContent2, "internal error?"); 1.1256 + return 0; 1.1257 + } 1.1258 + // aContent1 is an ancestor of aContent2 1.1259 + return aIf1Ancestor; 1.1260 + } 1.1261 + 1.1262 + if (last2 < 0) { 1.1263 + // aContent2 is an ancestor of aContent1 1.1264 + return aIf2Ancestor; 1.1265 + } 1.1266 + 1.1267 + // content1Ancestor != content2Ancestor, so they must be siblings with the same parent 1.1268 + nsINode* parent = content1Ancestor->GetParentNode(); 1.1269 +#ifdef DEBUG 1.1270 + // TODO: remove the uglyness, see bug 598468. 1.1271 + NS_ASSERTION(gPreventAssertInCompareTreePosition || parent, 1.1272 + "no common ancestor at all???"); 1.1273 +#endif // DEBUG 1.1274 + if (!parent) { // different documents?? 1.1275 + return 0; 1.1276 + } 1.1277 + 1.1278 + int32_t index1 = parent->IndexOf(content1Ancestor); 1.1279 + int32_t index2 = parent->IndexOf(content2Ancestor); 1.1280 + if (index1 < 0 || index2 < 0) { 1.1281 + // one of them must be anonymous; we can't determine the order 1.1282 + return 0; 1.1283 + } 1.1284 + 1.1285 + return index1 - index2; 1.1286 +} 1.1287 + 1.1288 +// static 1.1289 +nsIFrame* 1.1290 +nsLayoutUtils::FillAncestors(nsIFrame* aFrame, 1.1291 + nsIFrame* aStopAtAncestor, 1.1292 + nsTArray<nsIFrame*>* aAncestors) 1.1293 +{ 1.1294 + while (aFrame && aFrame != aStopAtAncestor) { 1.1295 + aAncestors->AppendElement(aFrame); 1.1296 + aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame); 1.1297 + } 1.1298 + return aFrame; 1.1299 +} 1.1300 + 1.1301 +// Return true if aFrame1 is after aFrame2 1.1302 +static bool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2) 1.1303 +{ 1.1304 + nsIFrame* f = aFrame2; 1.1305 + do { 1.1306 + f = f->GetNextSibling(); 1.1307 + if (f == aFrame1) 1.1308 + return true; 1.1309 + } while (f); 1.1310 + return false; 1.1311 +} 1.1312 + 1.1313 +// static 1.1314 +int32_t 1.1315 +nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1, 1.1316 + nsIFrame* aFrame2, 1.1317 + int32_t aIf1Ancestor, 1.1318 + int32_t aIf2Ancestor, 1.1319 + nsIFrame* aCommonAncestor) 1.1320 +{ 1.1321 + NS_PRECONDITION(aFrame1, "aFrame1 must not be null"); 1.1322 + NS_PRECONDITION(aFrame2, "aFrame2 must not be null"); 1.1323 + 1.1324 + nsAutoTArray<nsIFrame*,20> frame2Ancestors; 1.1325 + nsIFrame* nonCommonAncestor = 1.1326 + FillAncestors(aFrame2, aCommonAncestor, &frame2Ancestors); 1.1327 + 1.1328 + return DoCompareTreePosition(aFrame1, aFrame2, frame2Ancestors, 1.1329 + aIf1Ancestor, aIf2Ancestor, 1.1330 + nonCommonAncestor ? aCommonAncestor : nullptr); 1.1331 +} 1.1332 + 1.1333 +// static 1.1334 +int32_t 1.1335 +nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1, 1.1336 + nsIFrame* aFrame2, 1.1337 + nsTArray<nsIFrame*>& aFrame2Ancestors, 1.1338 + int32_t aIf1Ancestor, 1.1339 + int32_t aIf2Ancestor, 1.1340 + nsIFrame* aCommonAncestor) 1.1341 +{ 1.1342 + NS_PRECONDITION(aFrame1, "aFrame1 must not be null"); 1.1343 + NS_PRECONDITION(aFrame2, "aFrame2 must not be null"); 1.1344 + 1.1345 + nsPresContext* presContext = aFrame1->PresContext(); 1.1346 + if (presContext != aFrame2->PresContext()) { 1.1347 + NS_ERROR("no common ancestor at all, different documents"); 1.1348 + return 0; 1.1349 + } 1.1350 + 1.1351 + nsAutoTArray<nsIFrame*,20> frame1Ancestors; 1.1352 + if (aCommonAncestor && 1.1353 + !FillAncestors(aFrame1, aCommonAncestor, &frame1Ancestors)) { 1.1354 + // We reached the root of the frame tree ... if aCommonAncestor was set, 1.1355 + // it is wrong 1.1356 + return DoCompareTreePosition(aFrame1, aFrame2, 1.1357 + aIf1Ancestor, aIf2Ancestor, nullptr); 1.1358 + } 1.1359 + 1.1360 + int32_t last1 = int32_t(frame1Ancestors.Length()) - 1; 1.1361 + int32_t last2 = int32_t(aFrame2Ancestors.Length()) - 1; 1.1362 + while (last1 >= 0 && last2 >= 0 && 1.1363 + frame1Ancestors[last1] == aFrame2Ancestors[last2]) { 1.1364 + last1--; 1.1365 + last2--; 1.1366 + } 1.1367 + 1.1368 + if (last1 < 0) { 1.1369 + if (last2 < 0) { 1.1370 + NS_ASSERTION(aFrame1 == aFrame2, "internal error?"); 1.1371 + return 0; 1.1372 + } 1.1373 + // aFrame1 is an ancestor of aFrame2 1.1374 + return aIf1Ancestor; 1.1375 + } 1.1376 + 1.1377 + if (last2 < 0) { 1.1378 + // aFrame2 is an ancestor of aFrame1 1.1379 + return aIf2Ancestor; 1.1380 + } 1.1381 + 1.1382 + nsIFrame* ancestor1 = frame1Ancestors[last1]; 1.1383 + nsIFrame* ancestor2 = aFrame2Ancestors[last2]; 1.1384 + // Now we should be able to walk sibling chains to find which one is first 1.1385 + if (IsFrameAfter(ancestor2, ancestor1)) 1.1386 + return -1; 1.1387 + if (IsFrameAfter(ancestor1, ancestor2)) 1.1388 + return 1; 1.1389 + NS_WARNING("Frames were in different child lists???"); 1.1390 + return 0; 1.1391 +} 1.1392 + 1.1393 +// static 1.1394 +nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) { 1.1395 + if (!aFrame) { 1.1396 + return nullptr; 1.1397 + } 1.1398 + 1.1399 + nsIFrame* next; 1.1400 + while ((next = aFrame->GetNextSibling()) != nullptr) { 1.1401 + aFrame = next; 1.1402 + } 1.1403 + return aFrame; 1.1404 +} 1.1405 + 1.1406 +// static 1.1407 +nsView* 1.1408 +nsLayoutUtils::FindSiblingViewFor(nsView* aParentView, nsIFrame* aFrame) { 1.1409 + nsIFrame* parentViewFrame = aParentView->GetFrame(); 1.1410 + nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nullptr; 1.1411 + for (nsView* insertBefore = aParentView->GetFirstChild(); insertBefore; 1.1412 + insertBefore = insertBefore->GetNextSibling()) { 1.1413 + nsIFrame* f = insertBefore->GetFrame(); 1.1414 + if (!f) { 1.1415 + // this view could be some anonymous view attached to a meaningful parent 1.1416 + for (nsView* searchView = insertBefore->GetParent(); searchView; 1.1417 + searchView = searchView->GetParent()) { 1.1418 + f = searchView->GetFrame(); 1.1419 + if (f) { 1.1420 + break; 1.1421 + } 1.1422 + } 1.1423 + NS_ASSERTION(f, "Can't find a frame anywhere!"); 1.1424 + } 1.1425 + if (!f || !aFrame->GetContent() || !f->GetContent() || 1.1426 + CompareTreePosition(aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) { 1.1427 + // aFrame's content is after f's content (or we just don't know), 1.1428 + // so put our view before f's view 1.1429 + return insertBefore; 1.1430 + } 1.1431 + } 1.1432 + return nullptr; 1.1433 +} 1.1434 + 1.1435 +//static 1.1436 +nsIScrollableFrame* 1.1437 +nsLayoutUtils::GetScrollableFrameFor(const nsIFrame *aScrolledFrame) 1.1438 +{ 1.1439 + nsIFrame *frame = aScrolledFrame->GetParent(); 1.1440 + nsIScrollableFrame *sf = do_QueryFrame(frame); 1.1441 + return sf; 1.1442 +} 1.1443 + 1.1444 +/* static */ void 1.1445 +nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer, 1.1446 + const nsIFrame* aViewportFrame, 1.1447 + const nsRect& aAnchorRect, 1.1448 + const nsIFrame* aFixedPosFrame, 1.1449 + nsPresContext* aPresContext, 1.1450 + const ContainerLayerParameters& aContainerParameters) { 1.1451 + // Find out the rect of the viewport frame relative to the reference frame. 1.1452 + // This, in conjunction with the container scale, will correspond to the 1.1453 + // coordinate-space of the built layer. 1.1454 + float factor = aPresContext->AppUnitsPerDevPixel(); 1.1455 + Rect anchorRect(NSAppUnitsToFloatPixels(aAnchorRect.x, factor) * 1.1456 + aContainerParameters.mXScale, 1.1457 + NSAppUnitsToFloatPixels(aAnchorRect.y, factor) * 1.1458 + aContainerParameters.mYScale, 1.1459 + NSAppUnitsToFloatPixels(aAnchorRect.width, factor) * 1.1460 + aContainerParameters.mXScale, 1.1461 + NSAppUnitsToFloatPixels(aAnchorRect.height, factor) * 1.1462 + aContainerParameters.mYScale); 1.1463 + // Need to transform anchorRect from the container layer's coordinate system 1.1464 + // into aLayer's coordinate system. 1.1465 + Matrix transform2d; 1.1466 + if (aLayer->GetTransform().Is2D(&transform2d)) { 1.1467 + transform2d.Invert(); 1.1468 + anchorRect = transform2d.TransformBounds(anchorRect); 1.1469 + } else { 1.1470 + NS_ERROR("3D transform found between fixedpos content and its viewport (should never happen)"); 1.1471 + anchorRect = Rect(0,0,0,0); 1.1472 + } 1.1473 + 1.1474 + // Work out the anchor point for this fixed position layer. We assume that 1.1475 + // any positioning set (left/top/right/bottom) indicates that the 1.1476 + // corresponding side of its container should be the anchor point, 1.1477 + // defaulting to top-left. 1.1478 + LayerPoint anchor(anchorRect.x, anchorRect.y); 1.1479 + // Make sure the layer is aware of any fixed position margins that have 1.1480 + // been set. 1.1481 + nsMargin fixedMargins = aPresContext->PresShell()->GetContentDocumentFixedPositionMargins(); 1.1482 + LayerMargin fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins.top, factor) * 1.1483 + aContainerParameters.mYScale, 1.1484 + NSAppUnitsToFloatPixels(fixedMargins.right, factor) * 1.1485 + aContainerParameters.mXScale, 1.1486 + NSAppUnitsToFloatPixels(fixedMargins.bottom, factor) * 1.1487 + aContainerParameters.mYScale, 1.1488 + NSAppUnitsToFloatPixels(fixedMargins.left, factor) * 1.1489 + aContainerParameters.mXScale); 1.1490 + 1.1491 + if (aFixedPosFrame != aViewportFrame) { 1.1492 + const nsStylePosition* position = aFixedPosFrame->StylePosition(); 1.1493 + if (position->mOffset.GetRightUnit() != eStyleUnit_Auto) { 1.1494 + if (position->mOffset.GetLeftUnit() != eStyleUnit_Auto) { 1.1495 + anchor.x = anchorRect.x + anchorRect.width / 2.f; 1.1496 + } else { 1.1497 + anchor.x = anchorRect.XMost(); 1.1498 + } 1.1499 + } 1.1500 + if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto) { 1.1501 + if (position->mOffset.GetTopUnit() != eStyleUnit_Auto) { 1.1502 + anchor.y = anchorRect.y + anchorRect.height / 2.f; 1.1503 + } else { 1.1504 + anchor.y = anchorRect.YMost(); 1.1505 + } 1.1506 + } 1.1507 + 1.1508 + // If the frame is auto-positioned on either axis, set the top/left layer 1.1509 + // margins to -1, to indicate to the compositor that this layer is 1.1510 + // unaffected by fixed margins. 1.1511 + if (position->mOffset.GetLeftUnit() == eStyleUnit_Auto && 1.1512 + position->mOffset.GetRightUnit() == eStyleUnit_Auto) { 1.1513 + fixedLayerMargins.left = -1; 1.1514 + } 1.1515 + if (position->mOffset.GetTopUnit() == eStyleUnit_Auto && 1.1516 + position->mOffset.GetBottomUnit() == eStyleUnit_Auto) { 1.1517 + fixedLayerMargins.top = -1; 1.1518 + } 1.1519 + } 1.1520 + 1.1521 + aLayer->SetFixedPositionAnchor(anchor); 1.1522 + aLayer->SetFixedPositionMargins(fixedLayerMargins); 1.1523 +} 1.1524 + 1.1525 +bool 1.1526 +nsLayoutUtils::ViewportHasDisplayPort(nsPresContext* aPresContext, nsRect* aDisplayPort) 1.1527 +{ 1.1528 + nsIFrame* rootScrollFrame = 1.1529 + aPresContext->PresShell()->GetRootScrollFrame(); 1.1530 + return rootScrollFrame && 1.1531 + nsLayoutUtils::GetDisplayPort(rootScrollFrame->GetContent(), aDisplayPort); 1.1532 +} 1.1533 + 1.1534 +bool 1.1535 +nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame, nsRect* aDisplayPort) 1.1536 +{ 1.1537 + // Fixed-pos frames are parented by the viewport frame or the page content frame. 1.1538 + // We'll assume that printing/print preview don't have displayports for their 1.1539 + // pages! 1.1540 + nsIFrame* parent = aFrame->GetParent(); 1.1541 + if (!parent || parent->GetParent() || 1.1542 + aFrame->StyleDisplay()->mPosition != NS_STYLE_POSITION_FIXED) { 1.1543 + return false; 1.1544 + } 1.1545 + return ViewportHasDisplayPort(aFrame->PresContext(), aDisplayPort); 1.1546 +} 1.1547 + 1.1548 +static nsIFrame* 1.1549 +GetAnimatedGeometryRootForFrame(nsIFrame* aFrame, 1.1550 + const nsIFrame* aStopAtAncestor) 1.1551 +{ 1.1552 + nsIFrame* f = aFrame; 1.1553 + nsIFrame* stickyFrame = nullptr; 1.1554 + while (f != aStopAtAncestor) { 1.1555 + if (nsLayoutUtils::IsPopup(f)) 1.1556 + break; 1.1557 + if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(f)) 1.1558 + break; 1.1559 + if (!f->GetParent() && 1.1560 + nsLayoutUtils::ViewportHasDisplayPort(f->PresContext())) { 1.1561 + // Viewport frames in a display port need to be animated geometry roots 1.1562 + // for background-attachment:fixed elements. 1.1563 + break; 1.1564 + } 1.1565 + nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(f); 1.1566 + if (!parent) 1.1567 + break; 1.1568 + nsIAtom* parentType = parent->GetType(); 1.1569 +#ifdef ANDROID 1.1570 + // Treat the slider thumb as being as an active scrolled root 1.1571 + // on mobile so that it can move without repainting. 1.1572 + if (parentType == nsGkAtoms::sliderFrame) 1.1573 + break; 1.1574 +#endif 1.1575 + // Sticky frames are active if their nearest scrollable frame 1.1576 + // is also active, just keep a record of sticky frames that we 1.1577 + // encounter for now. 1.1578 + if (f->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY && 1.1579 + !stickyFrame) { 1.1580 + stickyFrame = f; 1.1581 + } 1.1582 + if (parentType == nsGkAtoms::scrollFrame) { 1.1583 + nsIScrollableFrame* sf = do_QueryFrame(parent); 1.1584 + if (sf->IsScrollingActive() && sf->GetScrolledFrame() == f) { 1.1585 + // If we found a sticky frame inside this active scroll frame, 1.1586 + // then use that. Otherwise use the scroll frame. 1.1587 + if (stickyFrame) { 1.1588 + return stickyFrame; 1.1589 + } 1.1590 + return f; 1.1591 + } else { 1.1592 + stickyFrame = nullptr; 1.1593 + } 1.1594 + } 1.1595 + // Fixed-pos frames are parented by the viewport frame, which has no parent 1.1596 + if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) { 1.1597 + return f; 1.1598 + } 1.1599 + f = parent; 1.1600 + } 1.1601 + return f; 1.1602 +} 1.1603 + 1.1604 +nsIFrame* 1.1605 +nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem, 1.1606 + nsDisplayListBuilder* aBuilder) 1.1607 +{ 1.1608 + nsIFrame* f = aItem->Frame(); 1.1609 + if (aItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER) { 1.1610 + nsDisplayScrollLayer* scrollLayerItem = 1.1611 + static_cast<nsDisplayScrollLayer*>(aItem); 1.1612 + nsIFrame* scrolledFrame = scrollLayerItem->GetScrolledFrame(); 1.1613 + return GetAnimatedGeometryRootForFrame(scrolledFrame, 1.1614 + aBuilder->FindReferenceFrameFor(scrolledFrame)); 1.1615 + } 1.1616 + if (aItem->ShouldFixToViewport(aBuilder)) { 1.1617 + // Make its active scrolled root be the active scrolled root of 1.1618 + // the enclosing viewport, since it shouldn't be scrolled by scrolled 1.1619 + // frames in its document. InvalidateFixedBackgroundFramesFromList in 1.1620 + // nsGfxScrollFrame will not repaint this item when scrolling occurs. 1.1621 + nsIFrame* viewportFrame = 1.1622 + nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame); 1.1623 + NS_ASSERTION(viewportFrame, "no viewport???"); 1.1624 + return GetAnimatedGeometryRootForFrame(viewportFrame, 1.1625 + aBuilder->FindReferenceFrameFor(viewportFrame)); 1.1626 + } 1.1627 + return GetAnimatedGeometryRootForFrame(f, aItem->ReferenceFrame()); 1.1628 +} 1.1629 + 1.1630 +// static 1.1631 +nsIScrollableFrame* 1.1632 +nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame, 1.1633 + Direction aDirection) 1.1634 +{ 1.1635 + NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame"); 1.1636 + for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { 1.1637 + nsIScrollableFrame* scrollableFrame = do_QueryFrame(f); 1.1638 + if (scrollableFrame) { 1.1639 + ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles(); 1.1640 + uint32_t directions = scrollableFrame->GetPerceivedScrollingDirections(); 1.1641 + if (aDirection == eVertical ? 1.1642 + (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN && 1.1643 + (directions & nsIScrollableFrame::VERTICAL)) : 1.1644 + (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN && 1.1645 + (directions & nsIScrollableFrame::HORIZONTAL))) 1.1646 + return scrollableFrame; 1.1647 + } 1.1648 + } 1.1649 + return nullptr; 1.1650 +} 1.1651 + 1.1652 +// static 1.1653 +nsIScrollableFrame* 1.1654 +nsLayoutUtils::GetNearestScrollableFrame(nsIFrame* aFrame, uint32_t aFlags) 1.1655 +{ 1.1656 + NS_ASSERTION(aFrame, "GetNearestScrollableFrame expects a non-null frame"); 1.1657 + for (nsIFrame* f = aFrame; f; f = (aFlags & SCROLLABLE_SAME_DOC) ? 1.1658 + f->GetParent() : nsLayoutUtils::GetCrossDocParentFrame(f)) { 1.1659 + nsIScrollableFrame* scrollableFrame = do_QueryFrame(f); 1.1660 + if (scrollableFrame) { 1.1661 + ScrollbarStyles ss = scrollableFrame->GetScrollbarStyles(); 1.1662 + if ((aFlags & SCROLLABLE_INCLUDE_HIDDEN) || 1.1663 + ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN || 1.1664 + ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) 1.1665 + return scrollableFrame; 1.1666 + } 1.1667 + } 1.1668 + return nullptr; 1.1669 +} 1.1670 + 1.1671 +// static 1.1672 +nsRect 1.1673 +nsLayoutUtils::GetScrolledRect(nsIFrame* aScrolledFrame, 1.1674 + const nsRect& aScrolledFrameOverflowArea, 1.1675 + const nsSize& aScrollPortSize, 1.1676 + uint8_t aDirection) 1.1677 +{ 1.1678 + nscoord x1 = aScrolledFrameOverflowArea.x, 1.1679 + x2 = aScrolledFrameOverflowArea.XMost(), 1.1680 + y1 = aScrolledFrameOverflowArea.y, 1.1681 + y2 = aScrolledFrameOverflowArea.YMost(); 1.1682 + if (y1 < 0) { 1.1683 + y1 = 0; 1.1684 + } 1.1685 + if (aDirection != NS_STYLE_DIRECTION_RTL) { 1.1686 + if (x1 < 0) { 1.1687 + x1 = 0; 1.1688 + } 1.1689 + } else { 1.1690 + if (x2 > aScrollPortSize.width) { 1.1691 + x2 = aScrollPortSize.width; 1.1692 + } 1.1693 + // When the scrolled frame chooses a size larger than its available width (because 1.1694 + // its padding alone is larger than the available width), we need to keep the 1.1695 + // start-edge of the scroll frame anchored to the start-edge of the scrollport. 1.1696 + // When the scrolled frame is RTL, this means moving it in our left-based 1.1697 + // coordinate system, so we need to compensate for its extra width here by 1.1698 + // effectively repositioning the frame. 1.1699 + nscoord extraWidth = std::max(0, aScrolledFrame->GetSize().width - aScrollPortSize.width); 1.1700 + x2 += extraWidth; 1.1701 + } 1.1702 + return nsRect(x1, y1, x2 - x1, y2 - y1); 1.1703 +} 1.1704 + 1.1705 +//static 1.1706 +bool 1.1707 +nsLayoutUtils::HasPseudoStyle(nsIContent* aContent, 1.1708 + nsStyleContext* aStyleContext, 1.1709 + nsCSSPseudoElements::Type aPseudoElement, 1.1710 + nsPresContext* aPresContext) 1.1711 +{ 1.1712 + NS_PRECONDITION(aPresContext, "Must have a prescontext"); 1.1713 + 1.1714 + nsRefPtr<nsStyleContext> pseudoContext; 1.1715 + if (aContent) { 1.1716 + pseudoContext = aPresContext->StyleSet()-> 1.1717 + ProbePseudoElementStyle(aContent->AsElement(), aPseudoElement, 1.1718 + aStyleContext); 1.1719 + } 1.1720 + return pseudoContext != nullptr; 1.1721 +} 1.1722 + 1.1723 +nsPoint 1.1724 +nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame) 1.1725 +{ 1.1726 + if (!aDOMEvent) 1.1727 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1728 + WidgetEvent* event = aDOMEvent->GetInternalNSEvent(); 1.1729 + if (!event) 1.1730 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1731 + return GetEventCoordinatesRelativeTo(event, aFrame); 1.1732 +} 1.1733 + 1.1734 +nsPoint 1.1735 +nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent, 1.1736 + nsIFrame* aFrame) 1.1737 +{ 1.1738 + if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT && 1.1739 + aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && 1.1740 + aEvent->eventStructType != NS_WHEEL_EVENT && 1.1741 + aEvent->eventStructType != NS_DRAG_EVENT && 1.1742 + aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT && 1.1743 + aEvent->eventStructType != NS_POINTER_EVENT && 1.1744 + aEvent->eventStructType != NS_GESTURENOTIFY_EVENT && 1.1745 + aEvent->eventStructType != NS_TOUCH_EVENT && 1.1746 + aEvent->eventStructType != NS_QUERY_CONTENT_EVENT)) 1.1747 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1748 + 1.1749 + return GetEventCoordinatesRelativeTo(aEvent, 1.1750 + LayoutDeviceIntPoint::ToUntyped(aEvent->AsGUIEvent()->refPoint), 1.1751 + aFrame); 1.1752 +} 1.1753 + 1.1754 +nsPoint 1.1755 +nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent, 1.1756 + const nsIntPoint aPoint, 1.1757 + nsIFrame* aFrame) 1.1758 +{ 1.1759 + if (!aFrame) { 1.1760 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1761 + } 1.1762 + 1.1763 + nsIWidget* widget = aEvent->AsGUIEvent()->widget; 1.1764 + if (!widget) { 1.1765 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1766 + } 1.1767 + 1.1768 + return GetEventCoordinatesRelativeTo(widget, aPoint, aFrame); 1.1769 +} 1.1770 + 1.1771 +nsPoint 1.1772 +nsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget* aWidget, 1.1773 + const nsIntPoint aPoint, 1.1774 + nsIFrame* aFrame) 1.1775 +{ 1.1776 + if (!aFrame || !aWidget) { 1.1777 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1778 + } 1.1779 + 1.1780 + nsView* view = aFrame->GetView(); 1.1781 + if (view) { 1.1782 + nsIWidget* frameWidget = view->GetWidget(); 1.1783 + if (frameWidget && frameWidget == aWidget) { 1.1784 + // Special case this cause it happens a lot. 1.1785 + // This also fixes bug 664707, events in the extra-special case of select 1.1786 + // dropdown popups that are transformed. 1.1787 + nsPresContext* presContext = aFrame->PresContext(); 1.1788 + nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x), 1.1789 + presContext->DevPixelsToAppUnits(aPoint.y)); 1.1790 + return pt - view->ViewToWidgetOffset(); 1.1791 + } 1.1792 + } 1.1793 + 1.1794 + /* If we walk up the frame tree and discover that any of the frames are 1.1795 + * transformed, we need to do extra work to convert from the global 1.1796 + * space to the local space. 1.1797 + */ 1.1798 + nsIFrame* rootFrame = aFrame; 1.1799 + bool transformFound = false; 1.1800 + for (nsIFrame* f = aFrame; f; f = GetCrossDocParentFrame(f)) { 1.1801 + if (f->IsTransformed()) { 1.1802 + transformFound = true; 1.1803 + } 1.1804 + 1.1805 + rootFrame = f; 1.1806 + } 1.1807 + 1.1808 + nsView* rootView = rootFrame->GetView(); 1.1809 + if (!rootView) { 1.1810 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1811 + } 1.1812 + 1.1813 + nsPoint widgetToView = TranslateWidgetToView(rootFrame->PresContext(), 1.1814 + aWidget, aPoint, rootView); 1.1815 + 1.1816 + if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { 1.1817 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.1818 + } 1.1819 + 1.1820 + // Convert from root document app units to app units of the document aFrame 1.1821 + // is in. 1.1822 + int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel(); 1.1823 + int32_t localAPD = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.1824 + widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD); 1.1825 + 1.1826 + /* If we encountered a transform, we can't do simple arithmetic to figure 1.1827 + * out how to convert back to aFrame's coordinates and must use the CTM. 1.1828 + */ 1.1829 + if (transformFound || aFrame->IsSVGText()) { 1.1830 + return TransformRootPointToFrame(aFrame, widgetToView); 1.1831 + } 1.1832 + 1.1833 + /* Otherwise, all coordinate systems are translations of one another, 1.1834 + * so we can just subtract out the difference. 1.1835 + */ 1.1836 + return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame); 1.1837 +} 1.1838 + 1.1839 +nsIFrame* 1.1840 +nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext, 1.1841 + const WidgetEvent* aEvent) 1.1842 +{ 1.1843 +#ifdef MOZ_XUL 1.1844 + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); 1.1845 + if (!pm) { 1.1846 + return nullptr; 1.1847 + } 1.1848 + nsTArray<nsIFrame*> popups; 1.1849 + pm->GetVisiblePopups(popups); 1.1850 + uint32_t i; 1.1851 + // Search from top to bottom 1.1852 + for (i = 0; i < popups.Length(); i++) { 1.1853 + nsIFrame* popup = popups[i]; 1.1854 + if (popup->PresContext()->GetRootPresContext() == aPresContext && 1.1855 + popup->GetScrollableOverflowRect().Contains( 1.1856 + GetEventCoordinatesRelativeTo(aEvent, popup))) { 1.1857 + return popup; 1.1858 + } 1.1859 + } 1.1860 +#endif 1.1861 + return nullptr; 1.1862 +} 1.1863 + 1.1864 +gfx3DMatrix 1.1865 +nsLayoutUtils::ChangeMatrixBasis(const gfxPoint3D &aOrigin, 1.1866 + const gfx3DMatrix &aMatrix) 1.1867 +{ 1.1868 + gfx3DMatrix result = aMatrix; 1.1869 + 1.1870 + /* Translate to the origin before aMatrix */ 1.1871 + result.Translate(-aOrigin); 1.1872 + 1.1873 + /* Translate back into position after aMatrix */ 1.1874 + result.TranslatePost(aOrigin); 1.1875 + 1.1876 + return result; 1.1877 +} 1.1878 + 1.1879 +static void ConstrainToCoordValues(float& aStart, float& aSize) 1.1880 +{ 1.1881 + MOZ_ASSERT(aSize >= 0); 1.1882 + 1.1883 + // Here we try to make sure that the resulting nsRect will continue to cover 1.1884 + // as much of the area that was covered by the original gfx Rect as possible. 1.1885 + 1.1886 + // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since 1.1887 + // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this 1.1888 + // range: 1.1889 + float end = aStart + aSize; 1.1890 + aStart = clamped(aStart, float(nscoord_MIN), float(nscoord_MAX)); 1.1891 + end = clamped(end, float(nscoord_MIN), float(nscoord_MAX)); 1.1892 + 1.1893 + aSize = end - aStart; 1.1894 + 1.1895 + // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height() 1.1896 + // can't return a value greater than nscoord_MAX. If aSize is greater than 1.1897 + // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect 1.1898 + // centered: 1.1899 + if (aSize > nscoord_MAX) { 1.1900 + float excess = aSize - nscoord_MAX; 1.1901 + excess /= 2; 1.1902 + aStart += excess; 1.1903 + aSize = nscoord_MAX; 1.1904 + } 1.1905 +} 1.1906 + 1.1907 +/** 1.1908 + * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX. 1.1909 + * 1.1910 + * @param aVal The value to constrain (in/out) 1.1911 + */ 1.1912 +static void ConstrainToCoordValues(gfxFloat& aVal) 1.1913 +{ 1.1914 + if (aVal <= nscoord_MIN) 1.1915 + aVal = nscoord_MIN; 1.1916 + else if (aVal >= nscoord_MAX) 1.1917 + aVal = nscoord_MAX; 1.1918 +} 1.1919 + 1.1920 +static void ConstrainToCoordValues(gfxFloat& aStart, gfxFloat& aSize) 1.1921 +{ 1.1922 + gfxFloat max = aStart + aSize; 1.1923 + 1.1924 + // Clamp the end points to within nscoord range 1.1925 + ConstrainToCoordValues(aStart); 1.1926 + ConstrainToCoordValues(max); 1.1927 + 1.1928 + aSize = max - aStart; 1.1929 + // If the width if still greater than the max nscoord, then bring both 1.1930 + // endpoints in by the same amount until it fits. 1.1931 + if (aSize > nscoord_MAX) { 1.1932 + gfxFloat excess = aSize - nscoord_MAX; 1.1933 + excess /= 2; 1.1934 + 1.1935 + aStart += excess; 1.1936 + aSize = nscoord_MAX; 1.1937 + } else if (aSize < nscoord_MIN) { 1.1938 + gfxFloat excess = aSize - nscoord_MIN; 1.1939 + excess /= 2; 1.1940 + 1.1941 + aStart -= excess; 1.1942 + aSize = nscoord_MIN; 1.1943 + } 1.1944 +} 1.1945 + 1.1946 +nsRect 1.1947 +nsLayoutUtils::RoundGfxRectToAppRect(const Rect &aRect, float aFactor) 1.1948 +{ 1.1949 + /* Get a new Rect whose units are app units by scaling by the specified factor. */ 1.1950 + Rect scaledRect = aRect; 1.1951 + scaledRect.ScaleRoundOut(aFactor); 1.1952 + 1.1953 + /* We now need to constrain our results to the max and min values for coords. */ 1.1954 + ConstrainToCoordValues(scaledRect.x, scaledRect.width); 1.1955 + ConstrainToCoordValues(scaledRect.y, scaledRect.height); 1.1956 + 1.1957 + /* Now typecast everything back. This is guaranteed to be safe. */ 1.1958 + return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()), 1.1959 + nscoord(scaledRect.Width()), nscoord(scaledRect.Height())); 1.1960 +} 1.1961 + 1.1962 +nsRect 1.1963 +nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor) 1.1964 +{ 1.1965 + /* Get a new gfxRect whose units are app units by scaling by the specified factor. */ 1.1966 + gfxRect scaledRect = aRect; 1.1967 + scaledRect.ScaleRoundOut(aFactor); 1.1968 + 1.1969 + /* We now need to constrain our results to the max and min values for coords. */ 1.1970 + ConstrainToCoordValues(scaledRect.x, scaledRect.width); 1.1971 + ConstrainToCoordValues(scaledRect.y, scaledRect.height); 1.1972 + 1.1973 + /* Now typecast everything back. This is guaranteed to be safe. */ 1.1974 + return nsRect(nscoord(scaledRect.X()), nscoord(scaledRect.Y()), 1.1975 + nscoord(scaledRect.Width()), nscoord(scaledRect.Height())); 1.1976 +} 1.1977 + 1.1978 + 1.1979 +nsRegion 1.1980 +nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect, 1.1981 + const nscoord aRadii[8], 1.1982 + const nsRect& aContainedRect) 1.1983 +{ 1.1984 + // rectFullHeight and rectFullWidth together will approximately contain 1.1985 + // the total area of the frame minus the rounded corners. 1.1986 + nsRect rectFullHeight = aRoundedRect; 1.1987 + nscoord xDiff = std::max(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]); 1.1988 + rectFullHeight.x += xDiff; 1.1989 + rectFullHeight.width -= std::max(aRadii[NS_CORNER_TOP_RIGHT_X], 1.1990 + aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff; 1.1991 + nsRect r1; 1.1992 + r1.IntersectRect(rectFullHeight, aContainedRect); 1.1993 + 1.1994 + nsRect rectFullWidth = aRoundedRect; 1.1995 + nscoord yDiff = std::max(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]); 1.1996 + rectFullWidth.y += yDiff; 1.1997 + rectFullWidth.height -= std::max(aRadii[NS_CORNER_BOTTOM_LEFT_Y], 1.1998 + aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff; 1.1999 + nsRect r2; 1.2000 + r2.IntersectRect(rectFullWidth, aContainedRect); 1.2001 + 1.2002 + nsRegion result; 1.2003 + result.Or(r1, r2); 1.2004 + return result; 1.2005 +} 1.2006 + 1.2007 +// Helper for RoundedRectIntersectsRect. 1.2008 +static bool 1.2009 +CheckCorner(nscoord aXOffset, nscoord aYOffset, 1.2010 + nscoord aXRadius, nscoord aYRadius) 1.2011 +{ 1.2012 + NS_ABORT_IF_FALSE(aXOffset > 0 && aYOffset > 0, 1.2013 + "must not pass nonpositives to CheckCorner"); 1.2014 + NS_ABORT_IF_FALSE(aXRadius >= 0 && aYRadius >= 0, 1.2015 + "must not pass negatives to CheckCorner"); 1.2016 + 1.2017 + // Avoid floating point math unless we're either (1) within the 1.2018 + // quarter-ellipse area at the rounded corner or (2) outside the 1.2019 + // rounding. 1.2020 + if (aXOffset >= aXRadius || aYOffset >= aYRadius) 1.2021 + return true; 1.2022 + 1.2023 + // Convert coordinates to a unit circle with (0,0) as the center of 1.2024 + // curvature, and see if we're inside the circle or outside. 1.2025 + float scaledX = float(aXRadius - aXOffset) / float(aXRadius); 1.2026 + float scaledY = float(aYRadius - aYOffset) / float(aYRadius); 1.2027 + return scaledX * scaledX + scaledY * scaledY < 1.0f; 1.2028 +} 1.2029 + 1.2030 +bool 1.2031 +nsLayoutUtils::RoundedRectIntersectsRect(const nsRect& aRoundedRect, 1.2032 + const nscoord aRadii[8], 1.2033 + const nsRect& aTestRect) 1.2034 +{ 1.2035 + if (!aTestRect.Intersects(aRoundedRect)) 1.2036 + return false; 1.2037 + 1.2038 + // distances from this edge of aRoundedRect to opposite edge of aTestRect, 1.2039 + // which we know are positive due to the Intersects check above. 1.2040 + nsMargin insets; 1.2041 + insets.top = aTestRect.YMost() - aRoundedRect.y; 1.2042 + insets.right = aRoundedRect.XMost() - aTestRect.x; 1.2043 + insets.bottom = aRoundedRect.YMost() - aTestRect.y; 1.2044 + insets.left = aTestRect.XMost() - aRoundedRect.x; 1.2045 + 1.2046 + // Check whether the bottom-right corner of aTestRect is inside the 1.2047 + // top left corner of aBounds when rounded by aRadii, etc. If any 1.2048 + // corner is not, then fail; otherwise succeed. 1.2049 + return CheckCorner(insets.left, insets.top, 1.2050 + aRadii[NS_CORNER_TOP_LEFT_X], 1.2051 + aRadii[NS_CORNER_TOP_LEFT_Y]) && 1.2052 + CheckCorner(insets.right, insets.top, 1.2053 + aRadii[NS_CORNER_TOP_RIGHT_X], 1.2054 + aRadii[NS_CORNER_TOP_RIGHT_Y]) && 1.2055 + CheckCorner(insets.right, insets.bottom, 1.2056 + aRadii[NS_CORNER_BOTTOM_RIGHT_X], 1.2057 + aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) && 1.2058 + CheckCorner(insets.left, insets.bottom, 1.2059 + aRadii[NS_CORNER_BOTTOM_LEFT_X], 1.2060 + aRadii[NS_CORNER_BOTTOM_LEFT_Y]); 1.2061 +} 1.2062 + 1.2063 +nsRect 1.2064 +nsLayoutUtils::MatrixTransformRectOut(const nsRect &aBounds, 1.2065 + const gfx3DMatrix &aMatrix, float aFactor) 1.2066 +{ 1.2067 + nsRect outside = aBounds; 1.2068 + outside.ScaleRoundOut(1/aFactor); 1.2069 + gfxRect image = aMatrix.TransformBounds(gfxRect(outside.x, 1.2070 + outside.y, 1.2071 + outside.width, 1.2072 + outside.height)); 1.2073 + return RoundGfxRectToAppRect(image, aFactor); 1.2074 +} 1.2075 + 1.2076 +nsRect 1.2077 +nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds, 1.2078 + const gfx3DMatrix &aMatrix, float aFactor) 1.2079 +{ 1.2080 + gfxRect image = aMatrix.TransformBounds(gfxRect(NSAppUnitsToDoublePixels(aBounds.x, aFactor), 1.2081 + NSAppUnitsToDoublePixels(aBounds.y, aFactor), 1.2082 + NSAppUnitsToDoublePixels(aBounds.width, aFactor), 1.2083 + NSAppUnitsToDoublePixels(aBounds.height, aFactor))); 1.2084 + 1.2085 + return RoundGfxRectToAppRect(image, aFactor); 1.2086 +} 1.2087 + 1.2088 +nsPoint 1.2089 +nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint, 1.2090 + const gfx3DMatrix &aMatrix, float aFactor) 1.2091 +{ 1.2092 + gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor), 1.2093 + NSAppUnitsToFloatPixels(aPoint.y, aFactor))); 1.2094 + return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor), 1.2095 + NSFloatPixelsToAppUnits(float(image.y), aFactor)); 1.2096 +} 1.2097 + 1.2098 +gfx3DMatrix 1.2099 +nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame, const nsIFrame *aAncestor) 1.2100 +{ 1.2101 + nsIFrame* parent; 1.2102 + gfx3DMatrix ctm; 1.2103 + if (aFrame == aAncestor) { 1.2104 + return ctm; 1.2105 + } 1.2106 + ctm = aFrame->GetTransformMatrix(aAncestor, &parent); 1.2107 + while (parent && parent != aAncestor) { 1.2108 + if (!parent->Preserves3DChildren()) { 1.2109 + ctm.ProjectTo2D(); 1.2110 + } 1.2111 + ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent); 1.2112 + } 1.2113 + return ctm; 1.2114 +} 1.2115 + 1.2116 +static nsIFrame* 1.2117 +FindNearestCommonAncestorFrame(nsIFrame* aFrame1, nsIFrame* aFrame2) 1.2118 +{ 1.2119 + nsAutoTArray<nsIFrame*,100> ancestors1; 1.2120 + nsAutoTArray<nsIFrame*,100> ancestors2; 1.2121 + nsIFrame* commonAncestor = nullptr; 1.2122 + if (aFrame1->PresContext() == aFrame2->PresContext()) { 1.2123 + commonAncestor = aFrame1->PresContext()->PresShell()->GetRootFrame(); 1.2124 + } 1.2125 + for (nsIFrame* f = aFrame1; f != commonAncestor; 1.2126 + f = nsLayoutUtils::GetCrossDocParentFrame(f)) { 1.2127 + ancestors1.AppendElement(f); 1.2128 + } 1.2129 + for (nsIFrame* f = aFrame2; f != commonAncestor; 1.2130 + f = nsLayoutUtils::GetCrossDocParentFrame(f)) { 1.2131 + ancestors2.AppendElement(f); 1.2132 + } 1.2133 + uint32_t minLengths = std::min(ancestors1.Length(), ancestors2.Length()); 1.2134 + for (uint32_t i = 1; i <= minLengths; ++i) { 1.2135 + if (ancestors1[ancestors1.Length() - i] == ancestors2[ancestors2.Length() - i]) { 1.2136 + commonAncestor = ancestors1[ancestors1.Length() - i]; 1.2137 + } else { 1.2138 + break; 1.2139 + } 1.2140 + } 1.2141 + return commonAncestor; 1.2142 +} 1.2143 + 1.2144 +nsLayoutUtils::TransformResult 1.2145 +nsLayoutUtils::TransformPoints(nsIFrame* aFromFrame, nsIFrame* aToFrame, 1.2146 + uint32_t aPointCount, CSSPoint* aPoints) 1.2147 +{ 1.2148 + nsIFrame* nearestCommonAncestor = FindNearestCommonAncestorFrame(aFromFrame, aToFrame); 1.2149 + if (!nearestCommonAncestor) { 1.2150 + return NO_COMMON_ANCESTOR; 1.2151 + } 1.2152 + gfx3DMatrix downToDest = GetTransformToAncestor(aToFrame, nearestCommonAncestor); 1.2153 + if (downToDest.IsSingular()) { 1.2154 + return NONINVERTIBLE_TRANSFORM; 1.2155 + } 1.2156 + downToDest.Invert(); 1.2157 + gfx3DMatrix upToAncestor = GetTransformToAncestor(aFromFrame, nearestCommonAncestor); 1.2158 + CSSToLayoutDeviceScale devPixelsPerCSSPixelFromFrame( 1.2159 + double(nsPresContext::AppUnitsPerCSSPixel())/ 1.2160 + aFromFrame->PresContext()->AppUnitsPerDevPixel()); 1.2161 + CSSToLayoutDeviceScale devPixelsPerCSSPixelToFrame( 1.2162 + double(nsPresContext::AppUnitsPerCSSPixel())/ 1.2163 + aToFrame->PresContext()->AppUnitsPerDevPixel()); 1.2164 + for (uint32_t i = 0; i < aPointCount; ++i) { 1.2165 + LayoutDevicePoint devPixels = aPoints[i] * devPixelsPerCSSPixelFromFrame; 1.2166 + gfxPoint toDevPixels = downToDest.ProjectPoint( 1.2167 + upToAncestor.ProjectPoint(gfxPoint(devPixels.x, devPixels.y))); 1.2168 + // Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct 1.2169 + // answer instead of some inaccuracy multiplying a number by its reciprocal. 1.2170 + aPoints[i] = LayoutDevicePoint(toDevPixels.x, toDevPixels.y) / 1.2171 + devPixelsPerCSSPixelToFrame; 1.2172 + } 1.2173 + return TRANSFORM_SUCCEEDED; 1.2174 +} 1.2175 + 1.2176 +bool 1.2177 +nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame, 1.2178 + gfx3DMatrix* aTransform) 1.2179 +{ 1.2180 + // FIXME/bug 796690: we can sometimes compute a transform in these 1.2181 + // cases, it just increases complexity considerably. Punt for now. 1.2182 + if (aFrame->Preserves3DChildren() || aFrame->HasTransformGetter()) { 1.2183 + return false; 1.2184 + } 1.2185 + 1.2186 + nsIFrame* root = nsLayoutUtils::GetDisplayRootFrame(aFrame); 1.2187 + if (root->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)) { 1.2188 + // Content may have been invalidated, so we can't reliably compute 1.2189 + // the "layer transform" in general. 1.2190 + return false; 1.2191 + } 1.2192 + // If the caller doesn't care about the value, early-return to skip 1.2193 + // overhead below. 1.2194 + if (!aTransform) { 1.2195 + return true; 1.2196 + } 1.2197 + 1.2198 + nsDisplayListBuilder builder(root, nsDisplayListBuilder::OTHER, 1.2199 + false/*don't build caret*/); 1.2200 + nsDisplayList list; 1.2201 + nsDisplayTransform* item = 1.2202 + new (&builder) nsDisplayTransform(&builder, aFrame, &list); 1.2203 + 1.2204 + *aTransform = 1.2205 + item->GetTransform(); 1.2206 + item->~nsDisplayTransform(); 1.2207 + 1.2208 + return true; 1.2209 +} 1.2210 + 1.2211 +static bool 1.2212 +TransformGfxPointFromAncestor(nsIFrame *aFrame, 1.2213 + const gfxPoint &aPoint, 1.2214 + nsIFrame *aAncestor, 1.2215 + gfxPoint* aOut) 1.2216 +{ 1.2217 + gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor); 1.2218 + 1.2219 + float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.2220 + nsRect childBounds = aFrame->GetVisualOverflowRectRelativeToSelf(); 1.2221 + gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), 1.2222 + NSAppUnitsToFloatPixels(childBounds.y, factor), 1.2223 + NSAppUnitsToFloatPixels(childBounds.width, factor), 1.2224 + NSAppUnitsToFloatPixels(childBounds.height, factor)); 1.2225 + return ctm.UntransformPoint(aPoint, childGfxBounds, aOut); 1.2226 +} 1.2227 + 1.2228 +static gfxRect 1.2229 +TransformGfxRectToAncestor(nsIFrame *aFrame, 1.2230 + const gfxRect &aRect, 1.2231 + const nsIFrame *aAncestor, 1.2232 + bool* aPreservesAxisAlignedRectangles = nullptr) 1.2233 +{ 1.2234 + gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor); 1.2235 + if (aPreservesAxisAlignedRectangles) { 1.2236 + gfxMatrix matrix2d; 1.2237 + *aPreservesAxisAlignedRectangles = 1.2238 + ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles(); 1.2239 + } 1.2240 + return ctm.TransformBounds(aRect); 1.2241 +} 1.2242 + 1.2243 +static SVGTextFrame* 1.2244 +GetContainingSVGTextFrame(nsIFrame* aFrame) 1.2245 +{ 1.2246 + if (!aFrame->IsSVGText()) { 1.2247 + return nullptr; 1.2248 + } 1.2249 + 1.2250 + return static_cast<SVGTextFrame*> 1.2251 + (nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(), 1.2252 + nsGkAtoms::svgTextFrame)); 1.2253 +} 1.2254 + 1.2255 +nsPoint 1.2256 +nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame, 1.2257 + const nsPoint& aPoint, 1.2258 + nsIFrame* aAncestor) 1.2259 +{ 1.2260 + SVGTextFrame* text = GetContainingSVGTextFrame(aFrame); 1.2261 + 1.2262 + float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.2263 + gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor), 1.2264 + NSAppUnitsToFloatPixels(aPoint.y, factor)); 1.2265 + 1.2266 + if (text) { 1.2267 + if (!TransformGfxPointFromAncestor(text, result, aAncestor, &result)) { 1.2268 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.2269 + } 1.2270 + result = text->TransformFramePointToTextChild(result, aFrame); 1.2271 + } else { 1.2272 + if (!TransformGfxPointFromAncestor(aFrame, result, nullptr, &result)) { 1.2273 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.2274 + } 1.2275 + } 1.2276 + 1.2277 + return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor), 1.2278 + NSFloatPixelsToAppUnits(float(result.y), factor)); 1.2279 +} 1.2280 + 1.2281 +nsRect 1.2282 +nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame, 1.2283 + const nsRect& aRect, 1.2284 + const nsIFrame* aAncestor, 1.2285 + bool* aPreservesAxisAlignedRectangles /* = nullptr */) 1.2286 +{ 1.2287 + SVGTextFrame* text = GetContainingSVGTextFrame(aFrame); 1.2288 + 1.2289 + float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.2290 + gfxRect result; 1.2291 + 1.2292 + if (text) { 1.2293 + result = text->TransformFrameRectFromTextChild(aRect, aFrame); 1.2294 + result = TransformGfxRectToAncestor(text, result, aAncestor); 1.2295 + // TransformFrameRectFromTextChild could involve any kind of transform, we 1.2296 + // could drill down into it to get an answer out of it but we don't yet. 1.2297 + if (aPreservesAxisAlignedRectangles) 1.2298 + *aPreservesAxisAlignedRectangles = false; 1.2299 + } else { 1.2300 + result = gfxRect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel), 1.2301 + NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel), 1.2302 + NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel), 1.2303 + NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel)); 1.2304 + result = TransformGfxRectToAncestor(aFrame, result, aAncestor, aPreservesAxisAlignedRectangles); 1.2305 + } 1.2306 + 1.2307 + float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel(); 1.2308 + return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel), 1.2309 + NSFloatPixelsToAppUnits(float(result.y), destAppUnitsPerDevPixel), 1.2310 + NSFloatPixelsToAppUnits(float(result.width), destAppUnitsPerDevPixel), 1.2311 + NSFloatPixelsToAppUnits(float(result.height), destAppUnitsPerDevPixel)); 1.2312 +} 1.2313 + 1.2314 +static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) { 1.2315 + nsIntPoint offset(0, 0); 1.2316 + nsIWidget* parent = aWidget->GetParent(); 1.2317 + while (parent) { 1.2318 + nsIntRect bounds; 1.2319 + aWidget->GetBounds(bounds); 1.2320 + offset += bounds.TopLeft(); 1.2321 + aWidget = parent; 1.2322 + parent = aWidget->GetParent(); 1.2323 + } 1.2324 + aRootWidget = aWidget; 1.2325 + return offset; 1.2326 +} 1.2327 + 1.2328 +nsPoint 1.2329 +nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext, 1.2330 + nsIWidget* aWidget, nsIntPoint aPt, 1.2331 + nsView* aView) 1.2332 +{ 1.2333 + nsPoint viewOffset; 1.2334 + nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset); 1.2335 + if (!viewWidget) { 1.2336 + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.2337 + } 1.2338 + 1.2339 + nsIWidget* fromRoot; 1.2340 + nsIntPoint fromOffset = GetWidgetOffset(aWidget, fromRoot); 1.2341 + nsIWidget* toRoot; 1.2342 + nsIntPoint toOffset = GetWidgetOffset(viewWidget, toRoot); 1.2343 + 1.2344 + nsIntPoint widgetPoint; 1.2345 + if (fromRoot == toRoot) { 1.2346 + widgetPoint = aPt + fromOffset - toOffset; 1.2347 + } else { 1.2348 + nsIntPoint screenPoint = aWidget->WidgetToScreenOffset(); 1.2349 + widgetPoint = aPt + screenPoint - viewWidget->WidgetToScreenOffset(); 1.2350 + } 1.2351 + 1.2352 + nsPoint widgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x), 1.2353 + aPresContext->DevPixelsToAppUnits(widgetPoint.y)); 1.2354 + return widgetAppUnits - viewOffset; 1.2355 +} 1.2356 + 1.2357 +// Combine aNewBreakType with aOrigBreakType, but limit the break types 1.2358 +// to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH. 1.2359 +uint8_t 1.2360 +nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType, 1.2361 + uint8_t aNewBreakType) 1.2362 +{ 1.2363 + uint8_t breakType = aOrigBreakType; 1.2364 + switch(breakType) { 1.2365 + case NS_STYLE_CLEAR_LEFT: 1.2366 + if (NS_STYLE_CLEAR_RIGHT == aNewBreakType || 1.2367 + NS_STYLE_CLEAR_BOTH == aNewBreakType) { 1.2368 + breakType = NS_STYLE_CLEAR_BOTH; 1.2369 + } 1.2370 + break; 1.2371 + case NS_STYLE_CLEAR_RIGHT: 1.2372 + if (NS_STYLE_CLEAR_LEFT == aNewBreakType || 1.2373 + NS_STYLE_CLEAR_BOTH == aNewBreakType) { 1.2374 + breakType = NS_STYLE_CLEAR_BOTH; 1.2375 + } 1.2376 + break; 1.2377 + case NS_STYLE_CLEAR_NONE: 1.2378 + if (NS_STYLE_CLEAR_LEFT == aNewBreakType || 1.2379 + NS_STYLE_CLEAR_RIGHT == aNewBreakType || 1.2380 + NS_STYLE_CLEAR_BOTH == aNewBreakType) { 1.2381 + breakType = aNewBreakType; 1.2382 + } 1.2383 + } 1.2384 + return breakType; 1.2385 +} 1.2386 + 1.2387 +#ifdef MOZ_DUMP_PAINTING 1.2388 +#include <stdio.h> 1.2389 + 1.2390 +static bool gDumpEventList = false; 1.2391 +int gPaintCount = 0; 1.2392 +#endif 1.2393 + 1.2394 +nsresult 1.2395 +nsLayoutUtils::GetRemoteContentIds(nsIFrame* aFrame, 1.2396 + const nsRect& aTarget, 1.2397 + nsTArray<ViewID> &aOutIDs, 1.2398 + bool aIgnoreRootScrollFrame) 1.2399 +{ 1.2400 + nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY, 1.2401 + false); 1.2402 + nsDisplayList list; 1.2403 + 1.2404 + if (aIgnoreRootScrollFrame) { 1.2405 + nsIFrame* rootScrollFrame = 1.2406 + aFrame->PresContext()->PresShell()->GetRootScrollFrame(); 1.2407 + if (rootScrollFrame) { 1.2408 + builder.SetIgnoreScrollFrame(rootScrollFrame); 1.2409 + } 1.2410 + } 1.2411 + 1.2412 + builder.EnterPresShell(aFrame, aTarget); 1.2413 + aFrame->BuildDisplayListForStackingContext(&builder, aTarget, &list); 1.2414 + builder.LeavePresShell(aFrame, aTarget); 1.2415 + 1.2416 + nsAutoTArray<nsIFrame*,8> outFrames; 1.2417 + nsDisplayItem::HitTestState hitTestState(&aOutIDs); 1.2418 + list.HitTest(&builder, aTarget, &hitTestState, &outFrames); 1.2419 + list.DeleteAll(); 1.2420 + 1.2421 + return NS_OK; 1.2422 +} 1.2423 + 1.2424 +nsIFrame* 1.2425 +nsLayoutUtils::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt, uint32_t aFlags) 1.2426 +{ 1.2427 + PROFILER_LABEL("nsLayoutUtils", "GetFrameForPoint"); 1.2428 + nsresult rv; 1.2429 + nsAutoTArray<nsIFrame*,8> outFrames; 1.2430 + rv = GetFramesForArea(aFrame, nsRect(aPt, nsSize(1, 1)), outFrames, aFlags); 1.2431 + NS_ENSURE_SUCCESS(rv, nullptr); 1.2432 + return outFrames.Length() ? outFrames.ElementAt(0) : nullptr; 1.2433 +} 1.2434 + 1.2435 +nsresult 1.2436 +nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect, 1.2437 + nsTArray<nsIFrame*> &aOutFrames, 1.2438 + uint32_t aFlags) 1.2439 +{ 1.2440 + PROFILER_LABEL("nsLayoutUtils","GetFramesForArea"); 1.2441 + nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY, 1.2442 + false); 1.2443 + nsDisplayList list; 1.2444 + nsRect target(aRect); 1.2445 + 1.2446 + if (aFlags & IGNORE_PAINT_SUPPRESSION) { 1.2447 + builder.IgnorePaintSuppression(); 1.2448 + } 1.2449 + 1.2450 + if (aFlags & IGNORE_ROOT_SCROLL_FRAME) { 1.2451 + nsIFrame* rootScrollFrame = 1.2452 + aFrame->PresContext()->PresShell()->GetRootScrollFrame(); 1.2453 + if (rootScrollFrame) { 1.2454 + builder.SetIgnoreScrollFrame(rootScrollFrame); 1.2455 + } 1.2456 + } 1.2457 + if (aFlags & IGNORE_CROSS_DOC) { 1.2458 + builder.SetDescendIntoSubdocuments(false); 1.2459 + } 1.2460 + 1.2461 + builder.EnterPresShell(aFrame, target); 1.2462 + aFrame->BuildDisplayListForStackingContext(&builder, target, &list); 1.2463 + builder.LeavePresShell(aFrame, target); 1.2464 + 1.2465 +#ifdef MOZ_DUMP_PAINTING 1.2466 + if (gDumpEventList) { 1.2467 + fprintf_stderr(stderr, "Event handling --- (%d,%d):\n", aRect.x, aRect.y); 1.2468 + nsFrame::PrintDisplayList(&builder, list); 1.2469 + } 1.2470 +#endif 1.2471 + 1.2472 + nsDisplayItem::HitTestState hitTestState; 1.2473 + list.HitTest(&builder, target, &hitTestState, &aOutFrames); 1.2474 + list.DeleteAll(); 1.2475 + return NS_OK; 1.2476 +} 1.2477 + 1.2478 +// This function is only used on B2G, and some compilers complain about 1.2479 +// unused static functions, so we need to #ifdef it. 1.2480 +#ifdef MOZ_WIDGET_GONK 1.2481 +// aScrollFrame and aScrollFrameAsScrollable must be non-nullptr 1.2482 +static FrameMetrics 1.2483 +CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame, 1.2484 + nsIScrollableFrame* aScrollFrameAsScrollable) { 1.2485 + // Calculate the metrics necessary for calculating the displayport. 1.2486 + // This code has a lot in common with the code in RecordFrameMetrics(); 1.2487 + // we may want to refactor this at some point. 1.2488 + FrameMetrics metrics; 1.2489 + nsPresContext* presContext = aScrollFrame->PresContext(); 1.2490 + nsIPresShell* presShell = presContext->PresShell(); 1.2491 + CSSToLayoutDeviceScale deviceScale(float(nsPresContext::AppUnitsPerCSSPixel()) 1.2492 + / presContext->AppUnitsPerDevPixel()); 1.2493 + ParentLayerToLayerScale resolution(presShell->GetResolution().width); 1.2494 + LayoutDeviceToLayerScale cumulativeResolution(presShell->GetCumulativeResolution().width); 1.2495 + 1.2496 + metrics.mDevPixelsPerCSSPixel = deviceScale; 1.2497 + metrics.mResolution = resolution; 1.2498 + metrics.mCumulativeResolution = cumulativeResolution; 1.2499 + metrics.SetZoom(deviceScale * cumulativeResolution * LayerToScreenScale(1)); 1.2500 + 1.2501 + // Only the size of the composition bounds is relevant to the 1.2502 + // displayport calculation, not its origin. 1.2503 + nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(aScrollFrame); 1.2504 + metrics.mCompositionBounds 1.2505 + = RoundedToInt(LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0, 0), compositionSize), 1.2506 + presContext->AppUnitsPerDevPixel()) 1.2507 + * (cumulativeResolution / resolution)); 1.2508 + 1.2509 + // This function is used for setting a display port for subframes, so 1.2510 + // aScrollFrame will not be the root content document's root scroll frame. 1.2511 + metrics.SetRootCompositionSize( 1.2512 + nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame, false, metrics)); 1.2513 + 1.2514 + metrics.SetScrollOffset(CSSPoint::FromAppUnits( 1.2515 + aScrollFrameAsScrollable->GetScrollPosition())); 1.2516 + 1.2517 + metrics.mScrollableRect = CSSRect::FromAppUnits( 1.2518 + nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrameAsScrollable, nullptr)); 1.2519 + 1.2520 + return metrics; 1.2521 +} 1.2522 +#endif 1.2523 + 1.2524 +bool 1.2525 +nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder, 1.2526 + nsIFrame* aScrollFrame, 1.2527 + nsRect aDisplayPortBase, 1.2528 + nsRect* aOutDisplayport) { 1.2529 + nsIContent* content = aScrollFrame->GetContent(); 1.2530 + nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame); 1.2531 + if (!content || !scrollableFrame) { 1.2532 + return false; 1.2533 + } 1.2534 + 1.2535 + // Set the base rect. Note that this will not influence 'haveDisplayPort', 1.2536 + // which is based on either the whole rect or margins being set, but it 1.2537 + // will affect what is returned in 'aOutDisplayPort' if margins are set. 1.2538 + SetDisplayPortBase(content, aDisplayPortBase); 1.2539 + 1.2540 + bool haveDisplayPort = GetDisplayPort(content, aOutDisplayport); 1.2541 + 1.2542 +#ifdef MOZ_WIDGET_GONK 1.2543 + // On B2G, we perform an optimization where we ensure that at least one 1.2544 + // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport. 1.2545 + // If that's not the case yet, and we are async-scrollable, we will get a 1.2546 + // displayport. 1.2547 + // Note: we only do this in processes where we do subframe scrolling to 1.2548 + // begin with (i.e., not in the parent process on B2G). 1.2549 + if (WantSubAPZC() && 1.2550 + !aBuilder.HaveScrollableDisplayPort() && 1.2551 + scrollableFrame->WantAsyncScroll()) { 1.2552 + 1.2553 + // If we don't already have a displayport, calculate and set one. 1.2554 + if (!haveDisplayPort) { 1.2555 + FrameMetrics metrics = CalculateFrameMetricsForDisplayPort(aScrollFrame, scrollableFrame); 1.2556 + LayerMargin displayportMargins = AsyncPanZoomController::CalculatePendingDisplayPort( 1.2557 + metrics, ScreenPoint(0.0f, 0.0f), 0.0); 1.2558 + nsIPresShell* presShell = aScrollFrame->PresContext()->GetPresShell(); 1.2559 + gfx::IntSize alignment = gfxPrefs::LayersTilesEnabled() 1.2560 + ? gfx::IntSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()) : 1.2561 + gfx::IntSize(0, 0); 1.2562 + nsLayoutUtils::SetDisplayPortMargins( 1.2563 + content, presShell, displayportMargins, alignment.width, 1.2564 + alignment.height, 0, nsLayoutUtils::RepaintMode::DoNotRepaint); 1.2565 + haveDisplayPort = GetDisplayPort(content, aOutDisplayport); 1.2566 + NS_ASSERTION(haveDisplayPort, "should have a displayport after having just set it"); 1.2567 + } 1.2568 + 1.2569 + // Record that the we now have a scrollable display port. 1.2570 + aBuilder.SetHaveScrollableDisplayPort(); 1.2571 + } 1.2572 +#endif 1.2573 + 1.2574 + return haveDisplayPort; 1.2575 +} 1.2576 + 1.2577 +nsresult 1.2578 +nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame, 1.2579 + const nsRegion& aDirtyRegion, nscolor aBackstop, 1.2580 + uint32_t aFlags) 1.2581 +{ 1.2582 + PROFILER_LABEL("nsLayoutUtils","PaintFrame"); 1.2583 + if (aFlags & PAINT_WIDGET_LAYERS) { 1.2584 + nsView* view = aFrame->GetView(); 1.2585 + if (!(view && view->GetWidget() && GetDisplayRootFrame(aFrame) == aFrame)) { 1.2586 + aFlags &= ~PAINT_WIDGET_LAYERS; 1.2587 + NS_ASSERTION(aRenderingContext, "need a rendering context"); 1.2588 + } 1.2589 + } 1.2590 + 1.2591 + nsPresContext* presContext = aFrame->PresContext(); 1.2592 + nsIPresShell* presShell = presContext->PresShell(); 1.2593 + nsRootPresContext* rootPresContext = presContext->GetRootPresContext(); 1.2594 + if (!rootPresContext) { 1.2595 + return NS_OK; 1.2596 + } 1.2597 + 1.2598 + nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::PAINTING, 1.2599 + !(aFlags & PAINT_HIDE_CARET)); 1.2600 + 1.2601 + nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); 1.2602 + bool usingDisplayPort = false; 1.2603 + nsRect displayport; 1.2604 + if (rootScrollFrame && !aFrame->GetParent()) { 1.2605 + nsRect displayportBase( 1.2606 + nsPoint(0,0), 1.2607 + nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)); 1.2608 + usingDisplayPort = nsLayoutUtils::GetOrMaybeCreateDisplayPort( 1.2609 + builder, rootScrollFrame, displayportBase, &displayport); 1.2610 + } 1.2611 + 1.2612 + nsRegion visibleRegion; 1.2613 + if (aFlags & PAINT_WIDGET_LAYERS) { 1.2614 + // This layer tree will be reused, so we'll need to calculate it 1.2615 + // for the whole "visible" area of the window 1.2616 + // 1.2617 + // |ignoreViewportScrolling| and |usingDisplayPort| are persistent 1.2618 + // document-rendering state. We rely on PresShell to flush 1.2619 + // retained layers as needed when that persistent state changes. 1.2620 + if (!usingDisplayPort) { 1.2621 + visibleRegion = aFrame->GetVisualOverflowRectRelativeToSelf(); 1.2622 + } else { 1.2623 + visibleRegion = displayport; 1.2624 + } 1.2625 + } else { 1.2626 + visibleRegion = aDirtyRegion; 1.2627 + } 1.2628 + 1.2629 + // If we're going to display something different from what we'd normally 1.2630 + // paint in a window then we will flush out any retained layer trees before 1.2631 + // *and after* we draw. 1.2632 + bool willFlushRetainedLayers = (aFlags & PAINT_HIDE_CARET) != 0; 1.2633 + 1.2634 + nsDisplayList list; 1.2635 + if (aFlags & PAINT_IN_TRANSFORM) { 1.2636 + builder.SetInTransform(true); 1.2637 + } 1.2638 + if (aFlags & PAINT_SYNC_DECODE_IMAGES) { 1.2639 + builder.SetSyncDecodeImages(true); 1.2640 + } 1.2641 + if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) { 1.2642 + builder.SetPaintingToWindow(true); 1.2643 + } 1.2644 + if (aFlags & PAINT_IGNORE_SUPPRESSION) { 1.2645 + builder.IgnorePaintSuppression(); 1.2646 + } 1.2647 + // Windowed plugins aren't allowed in popups 1.2648 + if ((aFlags & PAINT_WIDGET_LAYERS) && 1.2649 + !willFlushRetainedLayers && 1.2650 + !(aFlags & PAINT_DOCUMENT_RELATIVE) && 1.2651 + rootPresContext->NeedToComputePluginGeometryUpdates()) { 1.2652 + builder.SetWillComputePluginGeometry(true); 1.2653 + } 1.2654 + nsRect canvasArea(nsPoint(0, 0), aFrame->GetSize()); 1.2655 + 1.2656 + bool ignoreViewportScrolling = 1.2657 + aFrame->GetParent() ? false : presShell->IgnoringViewportScrolling(); 1.2658 + if (ignoreViewportScrolling && rootScrollFrame) { 1.2659 + nsIScrollableFrame* rootScrollableFrame = 1.2660 + presShell->GetRootScrollFrameAsScrollable(); 1.2661 + if (aFlags & PAINT_DOCUMENT_RELATIVE) { 1.2662 + // Make visibleRegion and aRenderingContext relative to the 1.2663 + // scrolled frame instead of the root frame. 1.2664 + nsPoint pos = rootScrollableFrame->GetScrollPosition(); 1.2665 + visibleRegion.MoveBy(-pos); 1.2666 + if (aRenderingContext) { 1.2667 + aRenderingContext->Translate(pos); 1.2668 + } 1.2669 + } 1.2670 + builder.SetIgnoreScrollFrame(rootScrollFrame); 1.2671 + 1.2672 + nsCanvasFrame* canvasFrame = 1.2673 + do_QueryFrame(rootScrollableFrame->GetScrolledFrame()); 1.2674 + if (canvasFrame) { 1.2675 + // Use UnionRect here to ensure that areas where the scrollbars 1.2676 + // were are still filled with the background color. 1.2677 + canvasArea.UnionRect(canvasArea, 1.2678 + canvasFrame->CanvasArea() + builder.ToReferenceFrame(canvasFrame)); 1.2679 + } 1.2680 + } 1.2681 + 1.2682 + nsRect dirtyRect = visibleRegion.GetBounds(); 1.2683 + builder.EnterPresShell(aFrame, dirtyRect); 1.2684 + { 1.2685 + PROFILER_LABEL("nsLayoutUtils","PaintFrame::BuildDisplayList"); 1.2686 + aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list); 1.2687 + } 1.2688 + const bool paintAllContinuations = aFlags & PAINT_ALL_CONTINUATIONS; 1.2689 + NS_ASSERTION(!paintAllContinuations || !aFrame->GetPrevContinuation(), 1.2690 + "If painting all continuations, the frame must be " 1.2691 + "first-continuation"); 1.2692 + 1.2693 + nsIAtom* frameType = aFrame->GetType(); 1.2694 + 1.2695 + if (paintAllContinuations) { 1.2696 + nsIFrame* currentFrame = aFrame; 1.2697 + while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) { 1.2698 + PROFILER_LABEL("nsLayoutUtils","PaintFrame::ContinuationsBuildDisplayList"); 1.2699 + nsRect frameDirty = dirtyRect - builder.ToReferenceFrame(currentFrame); 1.2700 + currentFrame->BuildDisplayListForStackingContext(&builder, 1.2701 + frameDirty, &list); 1.2702 + } 1.2703 + } 1.2704 + 1.2705 + // For the viewport frame in print preview/page layout we want to paint 1.2706 + // the grey background behind the page, not the canvas color. 1.2707 + if (frameType == nsGkAtoms::viewportFrame && 1.2708 + nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) { 1.2709 + nsRect bounds = nsRect(builder.ToReferenceFrame(aFrame), 1.2710 + aFrame->GetSize()); 1.2711 + presShell->AddPrintPreviewBackgroundItem(builder, list, aFrame, bounds); 1.2712 + } else if (frameType != nsGkAtoms::pageFrame) { 1.2713 + // For printing, this function is first called on an nsPageFrame, which 1.2714 + // creates a display list with a PageContent item. The PageContent item's 1.2715 + // paint function calls this function on the nsPageFrame's child which is 1.2716 + // an nsPageContentFrame. We only want to add the canvas background color 1.2717 + // item once, for the nsPageContentFrame. 1.2718 + 1.2719 + // Add the canvas background color to the bottom of the list. This 1.2720 + // happens after we've built the list so that AddCanvasBackgroundColorItem 1.2721 + // can monkey with the contents if necessary. 1.2722 + canvasArea.IntersectRect(canvasArea, visibleRegion.GetBounds()); 1.2723 + presShell->AddCanvasBackgroundColorItem( 1.2724 + builder, list, aFrame, canvasArea, aBackstop); 1.2725 + 1.2726 + // If the passed in backstop color makes us draw something different from 1.2727 + // normal, we need to flush layers. 1.2728 + if ((aFlags & PAINT_WIDGET_LAYERS) && !willFlushRetainedLayers) { 1.2729 + nsView* view = aFrame->GetView(); 1.2730 + if (view) { 1.2731 + nscolor backstop = presShell->ComputeBackstopColor(view); 1.2732 + // The PresShell's canvas background color doesn't get updated until 1.2733 + // EnterPresShell, so this check has to be done after that. 1.2734 + nscolor canvasColor = presShell->GetCanvasBackground(); 1.2735 + if (NS_ComposeColors(aBackstop, canvasColor) != 1.2736 + NS_ComposeColors(backstop, canvasColor)) { 1.2737 + willFlushRetainedLayers = true; 1.2738 + } 1.2739 + } 1.2740 + } 1.2741 + } 1.2742 + 1.2743 + builder.LeavePresShell(aFrame, dirtyRect); 1.2744 + 1.2745 + if (builder.GetHadToIgnorePaintSuppression()) { 1.2746 + willFlushRetainedLayers = true; 1.2747 + } 1.2748 + 1.2749 +#ifdef MOZ_DUMP_PAINTING 1.2750 + FILE* savedDumpFile = gfxUtils::sDumpPaintFile; 1.2751 + if (gfxUtils::sDumpPaintList || gfxUtils::sDumpPainting) { 1.2752 + if (gfxUtils::sDumpPaintingToFile) { 1.2753 + nsCString string("dump-"); 1.2754 + string.AppendInt(gPaintCount); 1.2755 + string.Append(".html"); 1.2756 + gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w"); 1.2757 + } else { 1.2758 + gfxUtils::sDumpPaintFile = stderr; 1.2759 + } 1.2760 + if (gfxUtils::sDumpPaintingToFile) { 1.2761 + fprintf_stderr(gfxUtils::sDumpPaintFile, "<html><head><script>var array = {}; function ViewImage(index) { window.location = array[index]; }</script></head><body>"); 1.2762 + } 1.2763 + fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- before optimization (dirty %d,%d,%d,%d):\n", 1.2764 + dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); 1.2765 + nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile); 1.2766 + if (gfxUtils::sDumpPaintingToFile) { 1.2767 + fprintf_stderr(gfxUtils::sDumpPaintFile, "<script>"); 1.2768 + } 1.2769 + } 1.2770 +#endif 1.2771 + 1.2772 + list.ComputeVisibilityForRoot(&builder, &visibleRegion, 1.2773 + usingDisplayPort ? rootScrollFrame : nullptr); 1.2774 + 1.2775 + uint32_t flags = nsDisplayList::PAINT_DEFAULT; 1.2776 + if (aFlags & PAINT_WIDGET_LAYERS) { 1.2777 + flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS; 1.2778 + if (willFlushRetainedLayers) { 1.2779 + // The caller wanted to paint from retained layers, but set up 1.2780 + // the paint in such a way that we can't use them. We're going 1.2781 + // to display something different from what we'd normally paint 1.2782 + // in a window, so make sure we flush out any retained layer 1.2783 + // trees before *and after* we draw. Callers should be fixed to 1.2784 + // not do this. 1.2785 + NS_WARNING("Flushing retained layers!"); 1.2786 + flags |= nsDisplayList::PAINT_FLUSH_LAYERS; 1.2787 + } else if (!(aFlags & PAINT_DOCUMENT_RELATIVE)) { 1.2788 + nsIWidget *widget = aFrame->GetNearestWidget(); 1.2789 + if (widget) { 1.2790 + builder.SetFinalTransparentRegion(visibleRegion); 1.2791 + // If we're finished building display list items for painting of the outermost 1.2792 + // pres shell, notify the widget about any toolbars we've encountered. 1.2793 + widget->UpdateThemeGeometries(builder.GetThemeGeometries()); 1.2794 + } 1.2795 + } 1.2796 + } 1.2797 + if (aFlags & PAINT_EXISTING_TRANSACTION) { 1.2798 + flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION; 1.2799 + } 1.2800 + if (aFlags & PAINT_NO_COMPOSITE) { 1.2801 + flags |= nsDisplayList::PAINT_NO_COMPOSITE; 1.2802 + } 1.2803 + if (aFlags & PAINT_COMPRESSED) { 1.2804 + flags |= nsDisplayList::PAINT_COMPRESSED; 1.2805 + } 1.2806 + 1.2807 + list.PaintRoot(&builder, aRenderingContext, flags); 1.2808 + 1.2809 +#ifdef MOZ_DUMP_PAINTING 1.2810 + if (gfxUtils::sDumpPaintList || gfxUtils::sDumpPainting) { 1.2811 + if (gfxUtils::sDumpPaintingToFile) { 1.2812 + fprintf_stderr(gfxUtils::sDumpPaintFile, "</script>"); 1.2813 + } 1.2814 + fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- after optimization:\n"); 1.2815 + nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile); 1.2816 + 1.2817 + fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- retained layer tree:\n"); 1.2818 + nsIWidget* widget = aFrame->GetNearestWidget(); 1.2819 + if (widget) { 1.2820 + nsRefPtr<LayerManager> layerManager = widget->GetLayerManager(); 1.2821 + if (layerManager) { 1.2822 + FrameLayerBuilder::DumpRetainedLayerTree(layerManager, gfxUtils::sDumpPaintFile, 1.2823 + gfxUtils::sDumpPaintingToFile); 1.2824 + } 1.2825 + } 1.2826 + if (gfxUtils::sDumpPaintingToFile) { 1.2827 + fprintf(gfxUtils::sDumpPaintFile, "</body></html>"); 1.2828 + fclose(gfxUtils::sDumpPaintFile); 1.2829 + } 1.2830 + gfxUtils::sDumpPaintFile = savedDumpFile; 1.2831 + gPaintCount++; 1.2832 + } 1.2833 +#endif 1.2834 + 1.2835 + // Update the widget's opaque region information. This sets 1.2836 + // glass boundaries on Windows. Also set up plugin clip regions and bounds. 1.2837 + if ((aFlags & PAINT_WIDGET_LAYERS) && 1.2838 + !willFlushRetainedLayers && 1.2839 + !(aFlags & PAINT_DOCUMENT_RELATIVE)) { 1.2840 + nsIWidget *widget = aFrame->GetNearestWidget(); 1.2841 + if (widget) { 1.2842 + nsRegion excludedRegion = builder.GetExcludedGlassRegion(); 1.2843 + excludedRegion.Sub(excludedRegion, visibleRegion); 1.2844 + nsIntRegion windowRegion(excludedRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel())); 1.2845 + widget->UpdateOpaqueRegion(windowRegion); 1.2846 + } 1.2847 + } 1.2848 + 1.2849 + if (builder.WillComputePluginGeometry()) { 1.2850 + nsRefPtr<LayerManager> layerManager; 1.2851 + nsIWidget* widget = aFrame->GetNearestWidget(); 1.2852 + if (widget) { 1.2853 + layerManager = widget->GetLayerManager(); 1.2854 + } 1.2855 + 1.2856 + rootPresContext->ComputePluginGeometryUpdates(aFrame, &builder, &list); 1.2857 + 1.2858 + // We're not going to get a WillPaintWindow event here if we didn't do 1.2859 + // widget invalidation, so just apply the plugin geometry update here instead. 1.2860 + // We could instead have the compositor send back an equivalent to WillPaintWindow, 1.2861 + // but it should be close enough to now not to matter. 1.2862 + if (layerManager && !layerManager->NeedsWidgetInvalidation()) { 1.2863 + rootPresContext->ApplyPluginGeometryUpdates(); 1.2864 + } 1.2865 + 1.2866 + // We told the compositor thread not to composite when it received the transaction because 1.2867 + // we wanted to update plugins first. Schedule the composite now. 1.2868 + if (layerManager) { 1.2869 + layerManager->Composite(); 1.2870 + } 1.2871 + } 1.2872 + 1.2873 + 1.2874 + // Flush the list so we don't trigger the IsEmpty-on-destruction assertion 1.2875 + list.DeleteAll(); 1.2876 + return NS_OK; 1.2877 +} 1.2878 + 1.2879 +/** 1.2880 + * Uses a binary search for find where the cursor falls in the line of text 1.2881 + * It also keeps track of the part of the string that has already been measured 1.2882 + * so it doesn't have to keep measuring the same text over and over 1.2883 + * 1.2884 + * @param "aBaseWidth" contains the width in twips of the portion 1.2885 + * of the text that has already been measured, and aBaseInx contains 1.2886 + * the index of the text that has already been measured. 1.2887 + * 1.2888 + * @param aTextWidth returns the (in twips) the length of the text that falls 1.2889 + * before the cursor aIndex contains the index of the text where the cursor falls 1.2890 + */ 1.2891 +bool 1.2892 +nsLayoutUtils::BinarySearchForPosition(nsRenderingContext* aRendContext, 1.2893 + const char16_t* aText, 1.2894 + int32_t aBaseWidth, 1.2895 + int32_t aBaseInx, 1.2896 + int32_t aStartInx, 1.2897 + int32_t aEndInx, 1.2898 + int32_t aCursorPos, 1.2899 + int32_t& aIndex, 1.2900 + int32_t& aTextWidth) 1.2901 +{ 1.2902 + int32_t range = aEndInx - aStartInx; 1.2903 + if ((range == 1) || (range == 2 && NS_IS_HIGH_SURROGATE(aText[aStartInx]))) { 1.2904 + aIndex = aStartInx + aBaseInx; 1.2905 + aTextWidth = aRendContext->GetWidth(aText, aIndex); 1.2906 + return true; 1.2907 + } 1.2908 + 1.2909 + int32_t inx = aStartInx + (range / 2); 1.2910 + 1.2911 + // Make sure we don't leave a dangling low surrogate 1.2912 + if (NS_IS_HIGH_SURROGATE(aText[inx-1])) 1.2913 + inx++; 1.2914 + 1.2915 + int32_t textWidth = aRendContext->GetWidth(aText, inx); 1.2916 + 1.2917 + int32_t fullWidth = aBaseWidth + textWidth; 1.2918 + if (fullWidth == aCursorPos) { 1.2919 + aTextWidth = textWidth; 1.2920 + aIndex = inx; 1.2921 + return true; 1.2922 + } else if (aCursorPos < fullWidth) { 1.2923 + aTextWidth = aBaseWidth; 1.2924 + if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, aStartInx, inx, aCursorPos, aIndex, aTextWidth)) { 1.2925 + return true; 1.2926 + } 1.2927 + } else { 1.2928 + aTextWidth = fullWidth; 1.2929 + if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) { 1.2930 + return true; 1.2931 + } 1.2932 + } 1.2933 + return false; 1.2934 +} 1.2935 + 1.2936 +static void 1.2937 +AddBoxesForFrame(nsIFrame* aFrame, 1.2938 + nsLayoutUtils::BoxCallback* aCallback) 1.2939 +{ 1.2940 + nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo(); 1.2941 + 1.2942 + if (pseudoType == nsCSSAnonBoxes::tableOuter) { 1.2943 + AddBoxesForFrame(aFrame->GetFirstPrincipalChild(), aCallback); 1.2944 + nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList); 1.2945 + if (kid) { 1.2946 + AddBoxesForFrame(kid, aCallback); 1.2947 + } 1.2948 + } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || 1.2949 + pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || 1.2950 + pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock || 1.2951 + pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) { 1.2952 + for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { 1.2953 + AddBoxesForFrame(kid, aCallback); 1.2954 + } 1.2955 + } else { 1.2956 + aCallback->AddBox(aFrame); 1.2957 + } 1.2958 +} 1.2959 + 1.2960 +void 1.2961 +nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback) 1.2962 +{ 1.2963 + while (aFrame) { 1.2964 + AddBoxesForFrame(aFrame, aCallback); 1.2965 + aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame); 1.2966 + } 1.2967 +} 1.2968 + 1.2969 +nsIFrame* 1.2970 +nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame) 1.2971 +{ 1.2972 + while (aFrame) { 1.2973 + nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo(); 1.2974 + 1.2975 + if (pseudoType == nsCSSAnonBoxes::tableOuter) { 1.2976 + nsIFrame* f = GetFirstNonAnonymousFrame(aFrame->GetFirstPrincipalChild()); 1.2977 + if (f) { 1.2978 + return f; 1.2979 + } 1.2980 + nsIFrame* kid = aFrame->GetFirstChild(nsIFrame::kCaptionList); 1.2981 + if (kid) { 1.2982 + f = GetFirstNonAnonymousFrame(kid); 1.2983 + if (f) { 1.2984 + return f; 1.2985 + } 1.2986 + } 1.2987 + } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || 1.2988 + pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || 1.2989 + pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock || 1.2990 + pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) { 1.2991 + for (nsIFrame* kid = aFrame->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { 1.2992 + nsIFrame* f = GetFirstNonAnonymousFrame(kid); 1.2993 + if (f) { 1.2994 + return f; 1.2995 + } 1.2996 + } 1.2997 + } else { 1.2998 + return aFrame; 1.2999 + } 1.3000 + 1.3001 + aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame); 1.3002 + } 1.3003 + return nullptr; 1.3004 +} 1.3005 + 1.3006 +struct BoxToRect : public nsLayoutUtils::BoxCallback { 1.3007 + nsIFrame* mRelativeTo; 1.3008 + nsLayoutUtils::RectCallback* mCallback; 1.3009 + uint32_t mFlags; 1.3010 + 1.3011 + BoxToRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback, 1.3012 + uint32_t aFlags) 1.3013 + : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {} 1.3014 + 1.3015 + virtual void AddBox(nsIFrame* aFrame) MOZ_OVERRIDE { 1.3016 + nsRect r; 1.3017 + nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r); 1.3018 + if (!outer) { 1.3019 + outer = aFrame; 1.3020 + switch (mFlags & nsLayoutUtils::RECTS_WHICH_BOX_MASK) { 1.3021 + case nsLayoutUtils::RECTS_USE_CONTENT_BOX: 1.3022 + r = aFrame->GetContentRectRelativeToSelf(); 1.3023 + break; 1.3024 + case nsLayoutUtils::RECTS_USE_PADDING_BOX: 1.3025 + r = aFrame->GetPaddingRectRelativeToSelf(); 1.3026 + break; 1.3027 + case nsLayoutUtils::RECTS_USE_MARGIN_BOX: 1.3028 + r = aFrame->GetMarginRectRelativeToSelf(); 1.3029 + break; 1.3030 + default: // Use the border box 1.3031 + r = aFrame->GetRectRelativeToSelf(); 1.3032 + } 1.3033 + } 1.3034 + if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) { 1.3035 + r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo); 1.3036 + } else { 1.3037 + r += outer->GetOffsetTo(mRelativeTo); 1.3038 + } 1.3039 + mCallback->AddRect(r); 1.3040 + } 1.3041 +}; 1.3042 + 1.3043 +void 1.3044 +nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, 1.3045 + RectCallback* aCallback, uint32_t aFlags) 1.3046 +{ 1.3047 + BoxToRect converter(aRelativeTo, aCallback, aFlags); 1.3048 + GetAllInFlowBoxes(aFrame, &converter); 1.3049 +} 1.3050 + 1.3051 +nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {} 1.3052 + 1.3053 +void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) { 1.3054 + mResultRect.UnionRect(mResultRect, aRect); 1.3055 + if (!mSeenFirstRect) { 1.3056 + mSeenFirstRect = true; 1.3057 + mFirstRect = aRect; 1.3058 + } 1.3059 +} 1.3060 + 1.3061 +nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList* aList) 1.3062 + : mRectList(aList) 1.3063 +{ 1.3064 +} 1.3065 + 1.3066 +void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) { 1.3067 + nsRefPtr<DOMRect> rect = new DOMRect(mRectList); 1.3068 + 1.3069 + rect->SetLayoutRect(aRect); 1.3070 + mRectList->Append(rect); 1.3071 +} 1.3072 + 1.3073 +nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame) 1.3074 +{ 1.3075 + return aFrame->PresContext()->PresShell()->GetRootFrame(); 1.3076 +} 1.3077 + 1.3078 +nsRect 1.3079 +nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo, 1.3080 + uint32_t aFlags) { 1.3081 + RectAccumulator accumulator; 1.3082 + GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags); 1.3083 + return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect 1.3084 + : accumulator.mResultRect; 1.3085 +} 1.3086 + 1.3087 +nsRect 1.3088 +nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect, 1.3089 + nsIFrame* aFrame, 1.3090 + uint32_t aFlags) 1.3091 +{ 1.3092 + const nsStyleText* textStyle = aFrame->StyleText(); 1.3093 + if (!textStyle->HasTextShadow()) 1.3094 + return aTextAndDecorationsRect; 1.3095 + 1.3096 + nsRect resultRect = aTextAndDecorationsRect; 1.3097 + int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.3098 + for (uint32_t i = 0; i < textStyle->mTextShadow->Length(); ++i) { 1.3099 + nsCSSShadowItem* shadow = textStyle->mTextShadow->ShadowAt(i); 1.3100 + nsMargin blur = nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D); 1.3101 + if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0)) 1.3102 + continue; 1.3103 + 1.3104 + nsRect tmpRect(aTextAndDecorationsRect); 1.3105 + 1.3106 + tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset)); 1.3107 + tmpRect.Inflate(blur); 1.3108 + 1.3109 + resultRect.UnionRect(resultRect, tmpRect); 1.3110 + } 1.3111 + return resultRect; 1.3112 +} 1.3113 + 1.3114 +nsresult 1.3115 +nsLayoutUtils::GetFontMetricsForFrame(const nsIFrame* aFrame, 1.3116 + nsFontMetrics** aFontMetrics, 1.3117 + float aInflation) 1.3118 +{ 1.3119 + return nsLayoutUtils::GetFontMetricsForStyleContext(aFrame->StyleContext(), 1.3120 + aFontMetrics, 1.3121 + aInflation); 1.3122 +} 1.3123 + 1.3124 +nsresult 1.3125 +nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext, 1.3126 + nsFontMetrics** aFontMetrics, 1.3127 + float aInflation) 1.3128 +{ 1.3129 + // pass the user font set object into the device context to pass along to CreateFontGroup 1.3130 + nsPresContext* pc = aStyleContext->PresContext(); 1.3131 + gfxUserFontSet* fs = pc->GetUserFontSet(); 1.3132 + gfxTextPerfMetrics* tp = pc->GetTextPerfMetrics(); 1.3133 + 1.3134 + nsFont font = aStyleContext->StyleFont()->mFont; 1.3135 + // We need to not run font.size through floats when it's large since 1.3136 + // doing so would be lossy. Fortunately, in such cases, aInflation is 1.3137 + // guaranteed to be 1.0f. 1.3138 + if (aInflation != 1.0f) { 1.3139 + font.size = NSToCoordRound(font.size * aInflation); 1.3140 + } 1.3141 + return pc->DeviceContext()->GetMetricsFor( 1.3142 + font, aStyleContext->StyleFont()->mLanguage, 1.3143 + fs, tp, *aFontMetrics); 1.3144 +} 1.3145 + 1.3146 +nsIFrame* 1.3147 +nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame) 1.3148 +{ 1.3149 + nsIFrame* result = aDescendantFrame; 1.3150 + 1.3151 + while (result) { 1.3152 + nsIFrame* parent = result->GetParent(); 1.3153 + if (parent == aParent) { 1.3154 + break; 1.3155 + } 1.3156 + 1.3157 + // The frame is not an immediate child of aParent so walk up another level 1.3158 + result = parent; 1.3159 + } 1.3160 + 1.3161 + return result; 1.3162 +} 1.3163 + 1.3164 +nsBlockFrame* 1.3165 +nsLayoutUtils::GetAsBlock(nsIFrame* aFrame) 1.3166 +{ 1.3167 + nsBlockFrame* block = do_QueryFrame(aFrame); 1.3168 + return block; 1.3169 +} 1.3170 + 1.3171 +nsBlockFrame* 1.3172 +nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame) 1.3173 +{ 1.3174 + nsIFrame* nextAncestor; 1.3175 + for (nextAncestor = aFrame->GetParent(); nextAncestor; 1.3176 + nextAncestor = nextAncestor->GetParent()) { 1.3177 + nsBlockFrame* block = GetAsBlock(nextAncestor); 1.3178 + if (block) 1.3179 + return block; 1.3180 + } 1.3181 + return nullptr; 1.3182 +} 1.3183 + 1.3184 +nsIFrame* 1.3185 +nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame* aFrame) 1.3186 +{ 1.3187 + if (!(aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT)) 1.3188 + return aFrame; 1.3189 + 1.3190 + nsIFrame* f = aFrame; 1.3191 + do { 1.3192 + f = GetParentOrPlaceholderFor(f); 1.3193 + } while (f->GetStateBits() & NS_FRAME_GENERATED_CONTENT); 1.3194 + return f; 1.3195 +} 1.3196 + 1.3197 +nsIFrame* 1.3198 +nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame* aFrame) 1.3199 +{ 1.3200 + if ((aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) 1.3201 + && !aFrame->GetPrevInFlow()) { 1.3202 + return aFrame->PresContext()->PresShell()->FrameManager()-> 1.3203 + GetPlaceholderFrameFor(aFrame); 1.3204 + } 1.3205 + return aFrame->GetParent(); 1.3206 +} 1.3207 + 1.3208 +nsIFrame* 1.3209 +nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(nsIFrame* aFrame) 1.3210 +{ 1.3211 + nsIFrame* f = GetParentOrPlaceholderFor(aFrame); 1.3212 + if (f) 1.3213 + return f; 1.3214 + return GetCrossDocParentFrame(aFrame); 1.3215 +} 1.3216 + 1.3217 +nsIFrame* 1.3218 +nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame *aFrame) 1.3219 +{ 1.3220 + nsIFrame *result = aFrame->GetNextContinuation(); 1.3221 + if (result) 1.3222 + return result; 1.3223 + 1.3224 + if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) != 0) { 1.3225 + // We only store the ib-split sibling annotation with the first 1.3226 + // frame in the continuation chain. Walk back to find that frame now. 1.3227 + aFrame = aFrame->FirstContinuation(); 1.3228 + 1.3229 + void* value = aFrame->Properties().Get(nsIFrame::IBSplitSibling()); 1.3230 + return static_cast<nsIFrame*>(value); 1.3231 + } 1.3232 + 1.3233 + return nullptr; 1.3234 +} 1.3235 + 1.3236 +nsIFrame* 1.3237 +nsLayoutUtils::FirstContinuationOrIBSplitSibling(nsIFrame *aFrame) 1.3238 +{ 1.3239 + nsIFrame *result = aFrame->FirstContinuation(); 1.3240 + if (result->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) { 1.3241 + while (true) { 1.3242 + nsIFrame *f = static_cast<nsIFrame*> 1.3243 + (result->Properties().Get(nsIFrame::IBSplitPrevSibling())); 1.3244 + if (!f) 1.3245 + break; 1.3246 + result = f; 1.3247 + } 1.3248 + } 1.3249 + 1.3250 + return result; 1.3251 +} 1.3252 + 1.3253 +bool 1.3254 +nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame *aFrame) 1.3255 +{ 1.3256 + if (aFrame->GetPrevContinuation()) { 1.3257 + return false; 1.3258 + } 1.3259 + if ((aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) && 1.3260 + aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling())) { 1.3261 + return false; 1.3262 + } 1.3263 + 1.3264 + return true; 1.3265 +} 1.3266 + 1.3267 +bool 1.3268 +nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame) 1.3269 +{ 1.3270 + if (!aFrame) 1.3271 + return false; 1.3272 + 1.3273 + nsIFrame* rootScrollFrame = 1.3274 + aFrame->PresContext()->PresShell()->GetRootScrollFrame(); 1.3275 + if (!rootScrollFrame) 1.3276 + return false; 1.3277 + 1.3278 + nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame); 1.3279 + NS_ASSERTION(rootScrollableFrame, "The root scorollable frame is null"); 1.3280 + 1.3281 + if (!IsProperAncestorFrame(rootScrollFrame, aFrame)) 1.3282 + return false; 1.3283 + 1.3284 + nsIFrame* rootScrolledFrame = rootScrollableFrame->GetScrolledFrame(); 1.3285 + return !(rootScrolledFrame == aFrame || 1.3286 + IsProperAncestorFrame(rootScrolledFrame, aFrame)); 1.3287 +} 1.3288 + 1.3289 +static nscoord AddPercents(nsLayoutUtils::IntrinsicWidthType aType, 1.3290 + nscoord aCurrent, float aPercent) 1.3291 +{ 1.3292 + nscoord result = aCurrent; 1.3293 + if (aPercent > 0.0f && aType == nsLayoutUtils::PREF_WIDTH) { 1.3294 + // XXX Should we also consider percentages for min widths, up to a 1.3295 + // limit? 1.3296 + if (aPercent >= 1.0f) 1.3297 + result = nscoord_MAX; 1.3298 + else 1.3299 + result = NSToCoordRound(float(result) / (1.0f - aPercent)); 1.3300 + } 1.3301 + return result; 1.3302 +} 1.3303 + 1.3304 +// Use only for widths/heights (or their min/max), since it clamps 1.3305 +// negative calc() results to 0. 1.3306 +static bool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult) 1.3307 +{ 1.3308 + if (aStyle.IsCalcUnit()) { 1.3309 + if (aStyle.CalcHasPercent()) { 1.3310 + return false; 1.3311 + } 1.3312 + // If it has no percents, we can pass 0 for the percentage basis. 1.3313 + aResult = nsRuleNode::ComputeComputedCalc(aStyle, 0); 1.3314 + if (aResult < 0) 1.3315 + aResult = 0; 1.3316 + return true; 1.3317 + } 1.3318 + 1.3319 + if (eStyleUnit_Coord != aStyle.GetUnit()) 1.3320 + return false; 1.3321 + 1.3322 + aResult = aStyle.GetCoordValue(); 1.3323 + NS_ASSERTION(aResult >= 0, "negative widths not allowed"); 1.3324 + return true; 1.3325 +} 1.3326 + 1.3327 +// Only call on style coords for which GetAbsoluteCoord returned false. 1.3328 +static bool 1.3329 +GetPercentHeight(const nsStyleCoord& aStyle, 1.3330 + nsIFrame* aFrame, 1.3331 + nscoord& aResult) 1.3332 +{ 1.3333 + if (eStyleUnit_Percent != aStyle.GetUnit() && 1.3334 + !aStyle.IsCalcUnit()) 1.3335 + return false; 1.3336 + 1.3337 + MOZ_ASSERT(!aStyle.IsCalcUnit() || aStyle.CalcHasPercent(), 1.3338 + "GetAbsoluteCoord should have handled this"); 1.3339 + 1.3340 + nsIFrame *f = aFrame->GetContainingBlock(); 1.3341 + if (!f) { 1.3342 + NS_NOTREACHED("top of frame tree not a containing block"); 1.3343 + return false; 1.3344 + } 1.3345 + 1.3346 + // During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses 1.3347 + // SetComputedHeight on the reflow state for its child to propagate its 1.3348 + // computed height to the scrolled content. So here we skip to the scroll 1.3349 + // frame that contains this scrolled content in order to get the same 1.3350 + // behavior as layout when computing percentage heights. 1.3351 + if (f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::scrolledContent) { 1.3352 + f = f->GetParent(); 1.3353 + } 1.3354 + 1.3355 + const nsStylePosition *pos = f->StylePosition(); 1.3356 + nscoord h; 1.3357 + if (!GetAbsoluteCoord(pos->mHeight, h) && 1.3358 + !GetPercentHeight(pos->mHeight, f, h)) { 1.3359 + NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto || 1.3360 + pos->mHeight.HasPercent(), 1.3361 + "unknown height unit"); 1.3362 + nsIAtom* fType = f->GetType(); 1.3363 + if (fType != nsGkAtoms::viewportFrame && fType != nsGkAtoms::canvasFrame && 1.3364 + fType != nsGkAtoms::pageContentFrame) { 1.3365 + // There's no basis for the percentage height, so it acts like auto. 1.3366 + // Should we consider a max-height < min-height pair a basis for 1.3367 + // percentage heights? The spec is somewhat unclear, and not doing 1.3368 + // so is simpler and avoids troubling discontinuities in behavior, 1.3369 + // so I'll choose not to. -LDB 1.3370 + return false; 1.3371 + } 1.3372 + 1.3373 + NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto, 1.3374 + "Unexpected height unit for viewport or canvas or page-content"); 1.3375 + // For the viewport, canvas, and page-content kids, the percentage 1.3376 + // basis is just the parent height. 1.3377 + h = f->GetSize().height; 1.3378 + if (h == NS_UNCONSTRAINEDSIZE) { 1.3379 + // We don't have a percentage basis after all 1.3380 + return false; 1.3381 + } 1.3382 + } 1.3383 + 1.3384 + nscoord maxh; 1.3385 + if (GetAbsoluteCoord(pos->mMaxHeight, maxh) || 1.3386 + GetPercentHeight(pos->mMaxHeight, f, maxh)) { 1.3387 + if (maxh < h) 1.3388 + h = maxh; 1.3389 + } else { 1.3390 + NS_ASSERTION(pos->mMaxHeight.GetUnit() == eStyleUnit_None || 1.3391 + pos->mMaxHeight.HasPercent(), 1.3392 + "unknown max-height unit"); 1.3393 + } 1.3394 + 1.3395 + nscoord minh; 1.3396 + if (GetAbsoluteCoord(pos->mMinHeight, minh) || 1.3397 + GetPercentHeight(pos->mMinHeight, f, minh)) { 1.3398 + if (minh > h) 1.3399 + h = minh; 1.3400 + } else { 1.3401 + NS_ASSERTION(pos->mMinHeight.HasPercent(), 1.3402 + "unknown min-height unit"); 1.3403 + } 1.3404 + 1.3405 + if (aStyle.IsCalcUnit()) { 1.3406 + aResult = std::max(nsRuleNode::ComputeComputedCalc(aStyle, h), 0); 1.3407 + return true; 1.3408 + } 1.3409 + 1.3410 + aResult = NSToCoordRound(aStyle.GetPercentValue() * h); 1.3411 + return true; 1.3412 +} 1.3413 + 1.3414 +// Handles only -moz-max-content and -moz-min-content, and 1.3415 +// -moz-fit-content for min-width and max-width, since the others 1.3416 +// (-moz-fit-content for width, and -moz-available) have no effect on 1.3417 +// intrinsic widths. 1.3418 +enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH }; 1.3419 +static bool 1.3420 +GetIntrinsicCoord(const nsStyleCoord& aStyle, 1.3421 + nsRenderingContext* aRenderingContext, 1.3422 + nsIFrame* aFrame, 1.3423 + eWidthProperty aProperty, 1.3424 + nscoord& aResult) 1.3425 +{ 1.3426 + NS_PRECONDITION(aProperty == PROP_WIDTH || aProperty == PROP_MAX_WIDTH || 1.3427 + aProperty == PROP_MIN_WIDTH, "unexpected property"); 1.3428 + if (aStyle.GetUnit() != eStyleUnit_Enumerated) 1.3429 + return false; 1.3430 + int32_t val = aStyle.GetIntValue(); 1.3431 + NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT || 1.3432 + val == NS_STYLE_WIDTH_MIN_CONTENT || 1.3433 + val == NS_STYLE_WIDTH_FIT_CONTENT || 1.3434 + val == NS_STYLE_WIDTH_AVAILABLE, 1.3435 + "unexpected enumerated value for width property"); 1.3436 + if (val == NS_STYLE_WIDTH_AVAILABLE) 1.3437 + return false; 1.3438 + if (val == NS_STYLE_WIDTH_FIT_CONTENT) { 1.3439 + if (aProperty == PROP_WIDTH) 1.3440 + return false; // handle like 'width: auto' 1.3441 + if (aProperty == PROP_MAX_WIDTH) 1.3442 + // constrain large 'width' values down to -moz-max-content 1.3443 + val = NS_STYLE_WIDTH_MAX_CONTENT; 1.3444 + else 1.3445 + // constrain small 'width' or 'max-width' values up to -moz-min-content 1.3446 + val = NS_STYLE_WIDTH_MIN_CONTENT; 1.3447 + } 1.3448 + 1.3449 + NS_ASSERTION(val == NS_STYLE_WIDTH_MAX_CONTENT || 1.3450 + val == NS_STYLE_WIDTH_MIN_CONTENT, 1.3451 + "should have reduced everything remaining to one of these"); 1.3452 + 1.3453 + // If aFrame is a container for font size inflation, then shrink 1.3454 + // wrapping inside of it should not apply font size inflation. 1.3455 + AutoMaybeDisableFontInflation an(aFrame); 1.3456 + 1.3457 + if (val == NS_STYLE_WIDTH_MAX_CONTENT) 1.3458 + aResult = aFrame->GetPrefWidth(aRenderingContext); 1.3459 + else 1.3460 + aResult = aFrame->GetMinWidth(aRenderingContext); 1.3461 + return true; 1.3462 +} 1.3463 + 1.3464 +#undef DEBUG_INTRINSIC_WIDTH 1.3465 + 1.3466 +#ifdef DEBUG_INTRINSIC_WIDTH 1.3467 +static int32_t gNoiseIndent = 0; 1.3468 +#endif 1.3469 + 1.3470 +/* static */ nscoord 1.3471 +nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext, 1.3472 + nsIFrame *aFrame, 1.3473 + IntrinsicWidthType aType, 1.3474 + uint32_t aFlags) 1.3475 +{ 1.3476 + NS_PRECONDITION(aFrame, "null frame"); 1.3477 + NS_PRECONDITION(aType == MIN_WIDTH || aType == PREF_WIDTH, "bad type"); 1.3478 + 1.3479 +#ifdef DEBUG_INTRINSIC_WIDTH 1.3480 + nsFrame::IndentBy(stderr, gNoiseIndent); 1.3481 + static_cast<nsFrame*>(aFrame)->ListTag(stderr); 1.3482 + printf_stderr(" %s intrinsic width for container:\n", 1.3483 + aType == MIN_WIDTH ? "min" : "pref"); 1.3484 +#endif 1.3485 + 1.3486 + // If aFrame is a container for font size inflation, then shrink 1.3487 + // wrapping inside of it should not apply font size inflation. 1.3488 + AutoMaybeDisableFontInflation an(aFrame); 1.3489 + 1.3490 + nsIFrame::IntrinsicWidthOffsetData offsets = 1.3491 + aFrame->IntrinsicWidthOffsets(aRenderingContext); 1.3492 + 1.3493 + const nsStylePosition *stylePos = aFrame->StylePosition(); 1.3494 + uint8_t boxSizing = stylePos->mBoxSizing; 1.3495 + const nsStyleCoord &styleWidth = stylePos->mWidth; 1.3496 + const nsStyleCoord &styleMinWidth = stylePos->mMinWidth; 1.3497 + const nsStyleCoord &styleMaxWidth = stylePos->mMaxWidth; 1.3498 + 1.3499 + // We build up two values starting with the content box, and then 1.3500 + // adding padding, border and margin. The result is normally 1.3501 + // |result|. Then, when we handle 'width', 'min-width', and 1.3502 + // 'max-width', we use the results we've been building in |min| as a 1.3503 + // minimum, overriding 'min-width'. This ensures two things: 1.3504 + // * that we don't let a value of 'box-sizing' specifying a width 1.3505 + // smaller than the padding/border inside the box-sizing box give 1.3506 + // a content width less than zero 1.3507 + // * that we prevent tables from becoming smaller than their 1.3508 + // intrinsic minimum width 1.3509 + nscoord result = 0, min = 0; 1.3510 + 1.3511 + nscoord maxw; 1.3512 + bool haveFixedMaxWidth = GetAbsoluteCoord(styleMaxWidth, maxw); 1.3513 + nscoord minw; 1.3514 + bool haveFixedMinWidth = GetAbsoluteCoord(styleMinWidth, minw); 1.3515 + 1.3516 + // If we have a specified width (or a specified 'min-width' greater 1.3517 + // than the specified 'max-width', which works out to the same thing), 1.3518 + // don't even bother getting the frame's intrinsic width, because in 1.3519 + // this case GetAbsoluteCoord(styleWidth, w) will always succeed, so 1.3520 + // we'll never need the intrinsic dimensions. 1.3521 + if (styleWidth.GetUnit() == eStyleUnit_Enumerated && 1.3522 + (styleWidth.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT || 1.3523 + styleWidth.GetIntValue() == NS_STYLE_WIDTH_MIN_CONTENT)) { 1.3524 + // -moz-fit-content and -moz-available enumerated widths compute intrinsic 1.3525 + // widths just like auto. 1.3526 + // For -moz-max-content and -moz-min-content, we handle them like 1.3527 + // specified widths, but ignore box-sizing. 1.3528 + boxSizing = NS_STYLE_BOX_SIZING_CONTENT; 1.3529 + } else if (!styleWidth.ConvertsToLength() && 1.3530 + !(haveFixedMinWidth && haveFixedMaxWidth && maxw <= minw)) { 1.3531 +#ifdef DEBUG_INTRINSIC_WIDTH 1.3532 + ++gNoiseIndent; 1.3533 +#endif 1.3534 + if (aType == MIN_WIDTH) 1.3535 + result = aFrame->GetMinWidth(aRenderingContext); 1.3536 + else 1.3537 + result = aFrame->GetPrefWidth(aRenderingContext); 1.3538 +#ifdef DEBUG_INTRINSIC_WIDTH 1.3539 + --gNoiseIndent; 1.3540 + nsFrame::IndentBy(stderr, gNoiseIndent); 1.3541 + static_cast<nsFrame*>(aFrame)->ListTag(stderr); 1.3542 + printf_stderr(" %s intrinsic width from frame is %d.\n", 1.3543 + aType == MIN_WIDTH ? "min" : "pref", result); 1.3544 +#endif 1.3545 + 1.3546 + // Handle elements with an intrinsic ratio (or size) and a specified 1.3547 + // height, min-height, or max-height. 1.3548 + const nsStyleCoord &styleHeight = stylePos->mHeight; 1.3549 + const nsStyleCoord &styleMinHeight = stylePos->mMinHeight; 1.3550 + const nsStyleCoord &styleMaxHeight = stylePos->mMaxHeight; 1.3551 + if (styleHeight.GetUnit() != eStyleUnit_Auto || 1.3552 + !(styleMinHeight.GetUnit() == eStyleUnit_Coord && 1.3553 + styleMinHeight.GetCoordValue() == 0) || 1.3554 + styleMaxHeight.GetUnit() != eStyleUnit_None) { 1.3555 + 1.3556 + nsSize ratio = aFrame->GetIntrinsicRatio(); 1.3557 + 1.3558 + if (ratio.height != 0) { 1.3559 + nscoord heightTakenByBoxSizing = 0; 1.3560 + switch (boxSizing) { 1.3561 + case NS_STYLE_BOX_SIZING_BORDER: { 1.3562 + const nsStyleBorder* styleBorder = aFrame->StyleBorder(); 1.3563 + heightTakenByBoxSizing += 1.3564 + styleBorder->GetComputedBorder().TopBottom(); 1.3565 + // fall through 1.3566 + } 1.3567 + case NS_STYLE_BOX_SIZING_PADDING: { 1.3568 + if (!(aFlags & IGNORE_PADDING)) { 1.3569 + const nsStylePadding* stylePadding = aFrame->StylePadding(); 1.3570 + nscoord pad; 1.3571 + if (GetAbsoluteCoord(stylePadding->mPadding.GetTop(), pad) || 1.3572 + GetPercentHeight(stylePadding->mPadding.GetTop(), aFrame, pad)) { 1.3573 + heightTakenByBoxSizing += pad; 1.3574 + } 1.3575 + if (GetAbsoluteCoord(stylePadding->mPadding.GetBottom(), pad) || 1.3576 + GetPercentHeight(stylePadding->mPadding.GetBottom(), aFrame, pad)) { 1.3577 + heightTakenByBoxSizing += pad; 1.3578 + } 1.3579 + } 1.3580 + // fall through 1.3581 + } 1.3582 + case NS_STYLE_BOX_SIZING_CONTENT: 1.3583 + default: 1.3584 + break; 1.3585 + } 1.3586 + 1.3587 + nscoord h; 1.3588 + if (GetAbsoluteCoord(styleHeight, h) || 1.3589 + GetPercentHeight(styleHeight, aFrame, h)) { 1.3590 + h = std::max(0, h - heightTakenByBoxSizing); 1.3591 + result = 1.3592 + NSToCoordRound(h * (float(ratio.width) / float(ratio.height))); 1.3593 + } 1.3594 + 1.3595 + if (GetAbsoluteCoord(styleMaxHeight, h) || 1.3596 + GetPercentHeight(styleMaxHeight, aFrame, h)) { 1.3597 + h = std::max(0, h - heightTakenByBoxSizing); 1.3598 + nscoord maxWidth = 1.3599 + NSToCoordRound(h * (float(ratio.width) / float(ratio.height))); 1.3600 + if (maxWidth < result) 1.3601 + result = maxWidth; 1.3602 + } 1.3603 + 1.3604 + if (GetAbsoluteCoord(styleMinHeight, h) || 1.3605 + GetPercentHeight(styleMinHeight, aFrame, h)) { 1.3606 + h = std::max(0, h - heightTakenByBoxSizing); 1.3607 + nscoord minWidth = 1.3608 + NSToCoordRound(h * (float(ratio.width) / float(ratio.height))); 1.3609 + if (minWidth > result) 1.3610 + result = minWidth; 1.3611 + } 1.3612 + } 1.3613 + } 1.3614 + } 1.3615 + 1.3616 + if (aFrame->GetType() == nsGkAtoms::tableFrame) { 1.3617 + // Tables can't shrink smaller than their intrinsic minimum width, 1.3618 + // no matter what. 1.3619 + min = aFrame->GetMinWidth(aRenderingContext); 1.3620 + } 1.3621 + 1.3622 + // We also need to track what has been added on outside of the box 1.3623 + // (controlled by 'box-sizing') where 'width', 'min-width' and 1.3624 + // 'max-width' are applied. We have to account for these properties 1.3625 + // after getting all the offsets (margin, border, padding) because 1.3626 + // percentages do not operate linearly. 1.3627 + // Doing this is ok because although percentages aren't handled 1.3628 + // linearly, they are handled monotonically. 1.3629 + nscoord coordOutsideWidth = 0; 1.3630 + float pctOutsideWidth = 0; 1.3631 + float pctTotal = 0.0f; 1.3632 + 1.3633 + if (!(aFlags & IGNORE_PADDING)) { 1.3634 + coordOutsideWidth += offsets.hPadding; 1.3635 + pctOutsideWidth += offsets.hPctPadding; 1.3636 + 1.3637 + if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) { 1.3638 + min += coordOutsideWidth; 1.3639 + result = NSCoordSaturatingAdd(result, coordOutsideWidth); 1.3640 + pctTotal += pctOutsideWidth; 1.3641 + 1.3642 + coordOutsideWidth = 0; 1.3643 + pctOutsideWidth = 0.0f; 1.3644 + } 1.3645 + } 1.3646 + 1.3647 + coordOutsideWidth += offsets.hBorder; 1.3648 + 1.3649 + if (boxSizing == NS_STYLE_BOX_SIZING_BORDER) { 1.3650 + min += coordOutsideWidth; 1.3651 + result = NSCoordSaturatingAdd(result, coordOutsideWidth); 1.3652 + pctTotal += pctOutsideWidth; 1.3653 + 1.3654 + coordOutsideWidth = 0; 1.3655 + pctOutsideWidth = 0.0f; 1.3656 + } 1.3657 + 1.3658 + coordOutsideWidth += offsets.hMargin; 1.3659 + pctOutsideWidth += offsets.hPctMargin; 1.3660 + 1.3661 + min += coordOutsideWidth; 1.3662 + result = NSCoordSaturatingAdd(result, coordOutsideWidth); 1.3663 + pctTotal += pctOutsideWidth; 1.3664 + 1.3665 + nscoord w; 1.3666 + if (GetAbsoluteCoord(styleWidth, w) || 1.3667 + GetIntrinsicCoord(styleWidth, aRenderingContext, aFrame, 1.3668 + PROP_WIDTH, w)) { 1.3669 + result = AddPercents(aType, w + coordOutsideWidth, pctOutsideWidth); 1.3670 + } 1.3671 + else if (aType == MIN_WIDTH && 1.3672 + // The only cases of coord-percent-calc() units that 1.3673 + // GetAbsoluteCoord didn't handle are percent and calc()s 1.3674 + // containing percent. 1.3675 + styleWidth.IsCoordPercentCalcUnit() && 1.3676 + aFrame->IsFrameOfType(nsIFrame::eReplaced)) { 1.3677 + // A percentage width on replaced elements means they can shrink to 0. 1.3678 + result = 0; // let |min| handle padding/border/margin 1.3679 + } 1.3680 + else { 1.3681 + // NOTE: We could really do a lot better for percents and for some 1.3682 + // cases of calc() containing percent (certainly including any where 1.3683 + // the coefficient on the percent is positive and there are no max() 1.3684 + // expressions). However, doing better for percents wouldn't be 1.3685 + // backwards compatible. 1.3686 + result = AddPercents(aType, result, pctTotal); 1.3687 + } 1.3688 + 1.3689 + if (haveFixedMaxWidth || 1.3690 + GetIntrinsicCoord(styleMaxWidth, aRenderingContext, aFrame, 1.3691 + PROP_MAX_WIDTH, maxw)) { 1.3692 + maxw = AddPercents(aType, maxw + coordOutsideWidth, pctOutsideWidth); 1.3693 + if (result > maxw) 1.3694 + result = maxw; 1.3695 + } 1.3696 + 1.3697 + if (haveFixedMinWidth || 1.3698 + GetIntrinsicCoord(styleMinWidth, aRenderingContext, aFrame, 1.3699 + PROP_MIN_WIDTH, minw)) { 1.3700 + minw = AddPercents(aType, minw + coordOutsideWidth, pctOutsideWidth); 1.3701 + if (result < minw) 1.3702 + result = minw; 1.3703 + } 1.3704 + 1.3705 + min = AddPercents(aType, min, pctTotal); 1.3706 + if (result < min) 1.3707 + result = min; 1.3708 + 1.3709 + const nsStyleDisplay *disp = aFrame->StyleDisplay(); 1.3710 + if (aFrame->IsThemed(disp)) { 1.3711 + nsIntSize size(0, 0); 1.3712 + bool canOverride = true; 1.3713 + nsPresContext *presContext = aFrame->PresContext(); 1.3714 + presContext->GetTheme()-> 1.3715 + GetMinimumWidgetSize(aRenderingContext, aFrame, disp->mAppearance, 1.3716 + &size, &canOverride); 1.3717 + 1.3718 + nscoord themeWidth = presContext->DevPixelsToAppUnits(size.width); 1.3719 + 1.3720 + // GMWS() returns a border-box width 1.3721 + themeWidth += offsets.hMargin; 1.3722 + themeWidth = AddPercents(aType, themeWidth, offsets.hPctMargin); 1.3723 + 1.3724 + if (themeWidth > result || !canOverride) 1.3725 + result = themeWidth; 1.3726 + } 1.3727 + 1.3728 +#ifdef DEBUG_INTRINSIC_WIDTH 1.3729 + nsFrame::IndentBy(stderr, gNoiseIndent); 1.3730 + static_cast<nsFrame*>(aFrame)->ListTag(stderr); 1.3731 + printf_stderr(" %s intrinsic width for container is %d twips.\n", 1.3732 + aType == MIN_WIDTH ? "min" : "pref", result); 1.3733 +#endif 1.3734 + 1.3735 + return result; 1.3736 +} 1.3737 + 1.3738 +/* static */ nscoord 1.3739 +nsLayoutUtils::ComputeCBDependentValue(nscoord aPercentBasis, 1.3740 + const nsStyleCoord& aCoord) 1.3741 +{ 1.3742 + NS_WARN_IF_FALSE(aPercentBasis != NS_UNCONSTRAINEDSIZE, 1.3743 + "have unconstrained width or height; this should only " 1.3744 + "result from very large sizes, not attempts at intrinsic " 1.3745 + "size calculation"); 1.3746 + 1.3747 + if (aCoord.IsCoordPercentCalcUnit()) { 1.3748 + return nsRuleNode::ComputeCoordPercentCalc(aCoord, aPercentBasis); 1.3749 + } 1.3750 + NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None || 1.3751 + aCoord.GetUnit() == eStyleUnit_Auto, 1.3752 + "unexpected width value"); 1.3753 + return 0; 1.3754 +} 1.3755 + 1.3756 +/* static */ nscoord 1.3757 +nsLayoutUtils::ComputeWidthValue( 1.3758 + nsRenderingContext* aRenderingContext, 1.3759 + nsIFrame* aFrame, 1.3760 + nscoord aContainingBlockWidth, 1.3761 + nscoord aContentEdgeToBoxSizing, 1.3762 + nscoord aBoxSizingToMarginEdge, 1.3763 + const nsStyleCoord& aCoord) 1.3764 +{ 1.3765 + NS_PRECONDITION(aFrame, "non-null frame expected"); 1.3766 + NS_PRECONDITION(aRenderingContext, "non-null rendering context expected"); 1.3767 + NS_WARN_IF_FALSE(aContainingBlockWidth != NS_UNCONSTRAINEDSIZE, 1.3768 + "have unconstrained width; this should only result from " 1.3769 + "very large sizes, not attempts at intrinsic width " 1.3770 + "calculation"); 1.3771 + NS_PRECONDITION(aContainingBlockWidth >= 0, 1.3772 + "width less than zero"); 1.3773 + 1.3774 + nscoord result; 1.3775 + if (aCoord.IsCoordPercentCalcUnit()) { 1.3776 + result = nsRuleNode::ComputeCoordPercentCalc(aCoord, 1.3777 + aContainingBlockWidth); 1.3778 + // The result of a calc() expression might be less than 0; we 1.3779 + // should clamp at runtime (below). (Percentages and coords that 1.3780 + // are less than 0 have already been dropped by the parser.) 1.3781 + result -= aContentEdgeToBoxSizing; 1.3782 + } else { 1.3783 + MOZ_ASSERT(eStyleUnit_Enumerated == aCoord.GetUnit()); 1.3784 + // If aFrame is a container for font size inflation, then shrink 1.3785 + // wrapping inside of it should not apply font size inflation. 1.3786 + AutoMaybeDisableFontInflation an(aFrame); 1.3787 + 1.3788 + int32_t val = aCoord.GetIntValue(); 1.3789 + switch (val) { 1.3790 + case NS_STYLE_WIDTH_MAX_CONTENT: 1.3791 + result = aFrame->GetPrefWidth(aRenderingContext); 1.3792 + NS_ASSERTION(result >= 0, "width less than zero"); 1.3793 + break; 1.3794 + case NS_STYLE_WIDTH_MIN_CONTENT: 1.3795 + result = aFrame->GetMinWidth(aRenderingContext); 1.3796 + NS_ASSERTION(result >= 0, "width less than zero"); 1.3797 + break; 1.3798 + case NS_STYLE_WIDTH_FIT_CONTENT: 1.3799 + { 1.3800 + nscoord pref = aFrame->GetPrefWidth(aRenderingContext), 1.3801 + min = aFrame->GetMinWidth(aRenderingContext), 1.3802 + fill = aContainingBlockWidth - 1.3803 + (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing); 1.3804 + result = std::max(min, std::min(pref, fill)); 1.3805 + NS_ASSERTION(result >= 0, "width less than zero"); 1.3806 + } 1.3807 + break; 1.3808 + case NS_STYLE_WIDTH_AVAILABLE: 1.3809 + result = aContainingBlockWidth - 1.3810 + (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing); 1.3811 + } 1.3812 + } 1.3813 + 1.3814 + return std::max(0, result); 1.3815 +} 1.3816 + 1.3817 +/* static */ nscoord 1.3818 +nsLayoutUtils::ComputeHeightDependentValue( 1.3819 + nscoord aContainingBlockHeight, 1.3820 + const nsStyleCoord& aCoord) 1.3821 +{ 1.3822 + // XXXldb Some callers explicitly check aContainingBlockHeight 1.3823 + // against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or 1.3824 + // calc()s containing percents before calling this function. 1.3825 + // However, it would be much more likely to catch problems without 1.3826 + // the unit conditions. 1.3827 + // XXXldb Many callers pass a non-'auto' containing block height when 1.3828 + // according to CSS2.1 they should be passing 'auto'. 1.3829 + NS_PRECONDITION(NS_AUTOHEIGHT != aContainingBlockHeight || 1.3830 + !aCoord.HasPercent(), 1.3831 + "unexpected containing block height"); 1.3832 + 1.3833 + if (aCoord.IsCoordPercentCalcUnit()) { 1.3834 + return nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockHeight); 1.3835 + } 1.3836 + 1.3837 + NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None || 1.3838 + aCoord.GetUnit() == eStyleUnit_Auto, 1.3839 + "unexpected height value"); 1.3840 + return 0; 1.3841 +} 1.3842 + 1.3843 +/* static */ void 1.3844 +nsLayoutUtils::MarkDescendantsDirty(nsIFrame *aSubtreeRoot) 1.3845 +{ 1.3846 + nsAutoTArray<nsIFrame*, 4> subtrees; 1.3847 + subtrees.AppendElement(aSubtreeRoot); 1.3848 + 1.3849 + // dirty descendants, iterating over subtrees that may include 1.3850 + // additional subtrees associated with placeholders 1.3851 + do { 1.3852 + nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1); 1.3853 + subtrees.RemoveElementAt(subtrees.Length() - 1); 1.3854 + 1.3855 + // Mark all descendants dirty (using an nsTArray stack rather than 1.3856 + // recursion). 1.3857 + // Note that nsHTMLReflowState::InitResizeFlags has some similar 1.3858 + // code; see comments there for how and why it differs. 1.3859 + nsAutoTArray<nsIFrame*, 32> stack; 1.3860 + stack.AppendElement(subtreeRoot); 1.3861 + 1.3862 + do { 1.3863 + nsIFrame *f = stack.ElementAt(stack.Length() - 1); 1.3864 + stack.RemoveElementAt(stack.Length() - 1); 1.3865 + 1.3866 + f->MarkIntrinsicWidthsDirty(); 1.3867 + 1.3868 + if (f->GetType() == nsGkAtoms::placeholderFrame) { 1.3869 + nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f); 1.3870 + if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) { 1.3871 + // We have another distinct subtree we need to mark. 1.3872 + subtrees.AppendElement(oof); 1.3873 + } 1.3874 + } 1.3875 + 1.3876 + nsIFrame::ChildListIterator lists(f); 1.3877 + for (; !lists.IsDone(); lists.Next()) { 1.3878 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.3879 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.3880 + nsIFrame* kid = childFrames.get(); 1.3881 + stack.AppendElement(kid); 1.3882 + } 1.3883 + } 1.3884 + } while (stack.Length() != 0); 1.3885 + } while (subtrees.Length() != 0); 1.3886 +} 1.3887 + 1.3888 +#define MULDIV(a,b,c) (nscoord(int64_t(a) * int64_t(b) / int64_t(c))) 1.3889 + 1.3890 +/* static */ nsSize 1.3891 +nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( 1.3892 + nsRenderingContext* aRenderingContext, nsIFrame* aFrame, 1.3893 + const IntrinsicSize& aIntrinsicSize, 1.3894 + nsSize aIntrinsicRatio, nsSize aCBSize, 1.3895 + nsSize aMargin, nsSize aBorder, nsSize aPadding) 1.3896 +{ 1.3897 + const nsStylePosition* stylePos = aFrame->StylePosition(); 1.3898 + 1.3899 + // If we're a flex item, we'll compute our size a bit differently. 1.3900 + const nsStyleCoord* widthStyleCoord = &(stylePos->mWidth); 1.3901 + const nsStyleCoord* heightStyleCoord = &(stylePos->mHeight); 1.3902 + 1.3903 + bool isFlexItem = aFrame->IsFlexItem(); 1.3904 + bool isHorizontalFlexItem = false; 1.3905 + 1.3906 + if (isFlexItem) { 1.3907 + // Flex items use their "flex-basis" property in place of their main-size 1.3908 + // property (e.g. "width") for sizing purposes, *unless* they have 1.3909 + // "flex-basis:auto", in which case they use their main-size property after 1.3910 + // all. 1.3911 + uint32_t flexDirection = 1.3912 + aFrame->GetParent()->StylePosition()->mFlexDirection; 1.3913 + isHorizontalFlexItem = 1.3914 + flexDirection == NS_STYLE_FLEX_DIRECTION_ROW || 1.3915 + flexDirection == NS_STYLE_FLEX_DIRECTION_ROW_REVERSE; 1.3916 + 1.3917 + // NOTE: The logic here should match the similar chunk for determining 1.3918 + // widthStyleCoord and heightStyleCoord in nsFrame::ComputeSize(). 1.3919 + const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis); 1.3920 + if (flexBasis->GetUnit() != eStyleUnit_Auto) { 1.3921 + if (isHorizontalFlexItem) { 1.3922 + widthStyleCoord = flexBasis; 1.3923 + } else { 1.3924 + // One caveat for vertical flex items: We don't support enumerated 1.3925 + // values (e.g. "max-content") for height properties yet. So, if our 1.3926 + // computed flex-basis is an enumerated value, we'll just behave as if 1.3927 + // it were "auto", which means "use the main-size property after all" 1.3928 + // (which is "height", in this case). 1.3929 + // NOTE: Once we support intrinsic sizing keywords for "height", 1.3930 + // we should remove this check. 1.3931 + if (flexBasis->GetUnit() != eStyleUnit_Enumerated) { 1.3932 + heightStyleCoord = flexBasis; 1.3933 + } 1.3934 + } 1.3935 + } 1.3936 + } 1.3937 + 1.3938 + // Handle intrinsic sizes and their interaction with 1.3939 + // {min-,max-,}{width,height} according to the rules in 1.3940 + // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths 1.3941 + 1.3942 + // Note: throughout the following section of the function, I avoid 1.3943 + // a * (b / c) because of its reduced accuracy relative to a * b / c 1.3944 + // or (a * b) / c (which are equivalent). 1.3945 + 1.3946 + const bool isAutoWidth = widthStyleCoord->GetUnit() == eStyleUnit_Auto; 1.3947 + const bool isAutoHeight = IsAutoHeight(*heightStyleCoord, aCBSize.height); 1.3948 + 1.3949 + nsSize boxSizingAdjust(0,0); 1.3950 + switch (stylePos->mBoxSizing) { 1.3951 + case NS_STYLE_BOX_SIZING_BORDER: 1.3952 + boxSizingAdjust += aBorder; 1.3953 + // fall through 1.3954 + case NS_STYLE_BOX_SIZING_PADDING: 1.3955 + boxSizingAdjust += aPadding; 1.3956 + } 1.3957 + nscoord boxSizingToMarginEdgeWidth = 1.3958 + aMargin.width + aBorder.width + aPadding.width - boxSizingAdjust.width; 1.3959 + 1.3960 + nscoord width, minWidth, maxWidth, height, minHeight, maxHeight; 1.3961 + 1.3962 + if (!isAutoWidth) { 1.3963 + width = nsLayoutUtils::ComputeWidthValue(aRenderingContext, 1.3964 + aFrame, aCBSize.width, boxSizingAdjust.width, 1.3965 + boxSizingToMarginEdgeWidth, *widthStyleCoord); 1.3966 + } 1.3967 + 1.3968 + if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None && 1.3969 + !(isFlexItem && isHorizontalFlexItem)) { 1.3970 + maxWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext, 1.3971 + aFrame, aCBSize.width, boxSizingAdjust.width, 1.3972 + boxSizingToMarginEdgeWidth, stylePos->mMaxWidth); 1.3973 + } else { 1.3974 + // NOTE: Flex items ignore their min & max sizing properties in their 1.3975 + // flex container's main-axis. (Those properties get applied later in 1.3976 + // the flexbox algorithm.) 1.3977 + maxWidth = nscoord_MAX; 1.3978 + } 1.3979 + 1.3980 + if (!(isFlexItem && isHorizontalFlexItem)) { 1.3981 + minWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext, 1.3982 + aFrame, aCBSize.width, boxSizingAdjust.width, 1.3983 + boxSizingToMarginEdgeWidth, stylePos->mMinWidth); 1.3984 + } else { 1.3985 + // NOTE: Flex items ignore their min & max sizing properties in their 1.3986 + // flex container's main-axis. (Those properties get applied later in 1.3987 + // the flexbox algorithm.) 1.3988 + minWidth = 0; 1.3989 + } 1.3990 + 1.3991 + if (!isAutoHeight) { 1.3992 + height = nsLayoutUtils::ComputeHeightValue(aCBSize.height, 1.3993 + boxSizingAdjust.height, 1.3994 + *heightStyleCoord); 1.3995 + } 1.3996 + 1.3997 + if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.height) && 1.3998 + !(isFlexItem && !isHorizontalFlexItem)) { 1.3999 + maxHeight = nsLayoutUtils::ComputeHeightValue(aCBSize.height, 1.4000 + boxSizingAdjust.height, 1.4001 + stylePos->mMaxHeight); 1.4002 + } else { 1.4003 + maxHeight = nscoord_MAX; 1.4004 + } 1.4005 + 1.4006 + if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.height) && 1.4007 + !(isFlexItem && !isHorizontalFlexItem)) { 1.4008 + minHeight = nsLayoutUtils::ComputeHeightValue(aCBSize.height, 1.4009 + boxSizingAdjust.height, 1.4010 + stylePos->mMinHeight); 1.4011 + } else { 1.4012 + minHeight = 0; 1.4013 + } 1.4014 + 1.4015 + // Resolve percentage intrinsic width/height as necessary: 1.4016 + 1.4017 + NS_ASSERTION(aCBSize.width != NS_UNCONSTRAINEDSIZE, 1.4018 + "Our containing block must not have unconstrained width!"); 1.4019 + 1.4020 + bool hasIntrinsicWidth, hasIntrinsicHeight; 1.4021 + nscoord intrinsicWidth, intrinsicHeight; 1.4022 + 1.4023 + if (aIntrinsicSize.width.GetUnit() == eStyleUnit_Coord) { 1.4024 + hasIntrinsicWidth = true; 1.4025 + intrinsicWidth = aIntrinsicSize.width.GetCoordValue(); 1.4026 + if (intrinsicWidth < 0) 1.4027 + intrinsicWidth = 0; 1.4028 + } else { 1.4029 + NS_ASSERTION(aIntrinsicSize.width.GetUnit() == eStyleUnit_None, 1.4030 + "unexpected unit"); 1.4031 + hasIntrinsicWidth = false; 1.4032 + intrinsicWidth = 0; 1.4033 + } 1.4034 + 1.4035 + if (aIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) { 1.4036 + hasIntrinsicHeight = true; 1.4037 + intrinsicHeight = aIntrinsicSize.height.GetCoordValue(); 1.4038 + if (intrinsicHeight < 0) 1.4039 + intrinsicHeight = 0; 1.4040 + } else { 1.4041 + NS_ASSERTION(aIntrinsicSize.height.GetUnit() == eStyleUnit_None, 1.4042 + "unexpected unit"); 1.4043 + hasIntrinsicHeight = false; 1.4044 + intrinsicHeight = 0; 1.4045 + } 1.4046 + 1.4047 + NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0, 1.4048 + "Intrinsic ratio has a negative component!"); 1.4049 + 1.4050 + // Now calculate the used values for width and height: 1.4051 + 1.4052 + if (isAutoWidth) { 1.4053 + if (isAutoHeight) { 1.4054 + 1.4055 + // 'auto' width, 'auto' height 1.4056 + 1.4057 + // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2: 1.4058 + 1.4059 + nscoord tentWidth, tentHeight; 1.4060 + 1.4061 + if (hasIntrinsicWidth) { 1.4062 + tentWidth = intrinsicWidth; 1.4063 + } else if (hasIntrinsicHeight && aIntrinsicRatio.height > 0) { 1.4064 + tentWidth = MULDIV(intrinsicHeight, aIntrinsicRatio.width, aIntrinsicRatio.height); 1.4065 + } else if (aIntrinsicRatio.width > 0) { 1.4066 + tentWidth = aCBSize.width - boxSizingToMarginEdgeWidth; // XXX scrollbar? 1.4067 + if (tentWidth < 0) tentWidth = 0; 1.4068 + } else { 1.4069 + tentWidth = nsPresContext::CSSPixelsToAppUnits(300); 1.4070 + } 1.4071 + 1.4072 + if (hasIntrinsicHeight) { 1.4073 + tentHeight = intrinsicHeight; 1.4074 + } else if (aIntrinsicRatio.width > 0) { 1.4075 + tentHeight = MULDIV(tentWidth, aIntrinsicRatio.height, aIntrinsicRatio.width); 1.4076 + } else { 1.4077 + tentHeight = nsPresContext::CSSPixelsToAppUnits(150); 1.4078 + } 1.4079 + 1.4080 + return ComputeAutoSizeWithIntrinsicDimensions(minWidth, minHeight, 1.4081 + maxWidth, maxHeight, 1.4082 + tentWidth, tentHeight); 1.4083 + } else { 1.4084 + 1.4085 + // 'auto' width, non-'auto' height 1.4086 + height = NS_CSS_MINMAX(height, minHeight, maxHeight); 1.4087 + if (aIntrinsicRatio.height > 0) { 1.4088 + width = MULDIV(height, aIntrinsicRatio.width, aIntrinsicRatio.height); 1.4089 + } else if (hasIntrinsicWidth) { 1.4090 + width = intrinsicWidth; 1.4091 + } else { 1.4092 + width = nsPresContext::CSSPixelsToAppUnits(300); 1.4093 + } 1.4094 + width = NS_CSS_MINMAX(width, minWidth, maxWidth); 1.4095 + 1.4096 + } 1.4097 + } else { 1.4098 + if (isAutoHeight) { 1.4099 + 1.4100 + // non-'auto' width, 'auto' height 1.4101 + width = NS_CSS_MINMAX(width, minWidth, maxWidth); 1.4102 + if (aIntrinsicRatio.width > 0) { 1.4103 + height = MULDIV(width, aIntrinsicRatio.height, aIntrinsicRatio.width); 1.4104 + } else if (hasIntrinsicHeight) { 1.4105 + height = intrinsicHeight; 1.4106 + } else { 1.4107 + height = nsPresContext::CSSPixelsToAppUnits(150); 1.4108 + } 1.4109 + height = NS_CSS_MINMAX(height, minHeight, maxHeight); 1.4110 + 1.4111 + } else { 1.4112 + 1.4113 + // non-'auto' width, non-'auto' height 1.4114 + width = NS_CSS_MINMAX(width, minWidth, maxWidth); 1.4115 + height = NS_CSS_MINMAX(height, minHeight, maxHeight); 1.4116 + 1.4117 + } 1.4118 + } 1.4119 + 1.4120 + return nsSize(width, height); 1.4121 +} 1.4122 + 1.4123 +nsSize 1.4124 +nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoord minWidth, nscoord minHeight, 1.4125 + nscoord maxWidth, nscoord maxHeight, 1.4126 + nscoord tentWidth, nscoord tentHeight) 1.4127 +{ 1.4128 + // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7: 1.4129 + 1.4130 + if (minWidth > maxWidth) 1.4131 + maxWidth = minWidth; 1.4132 + if (minHeight > maxHeight) 1.4133 + maxHeight = minHeight; 1.4134 + 1.4135 + nscoord heightAtMaxWidth, heightAtMinWidth, 1.4136 + widthAtMaxHeight, widthAtMinHeight; 1.4137 + 1.4138 + if (tentWidth > 0) { 1.4139 + heightAtMaxWidth = MULDIV(maxWidth, tentHeight, tentWidth); 1.4140 + if (heightAtMaxWidth < minHeight) 1.4141 + heightAtMaxWidth = minHeight; 1.4142 + heightAtMinWidth = MULDIV(minWidth, tentHeight, tentWidth); 1.4143 + if (heightAtMinWidth > maxHeight) 1.4144 + heightAtMinWidth = maxHeight; 1.4145 + } else { 1.4146 + heightAtMaxWidth = heightAtMinWidth = NS_CSS_MINMAX(tentHeight, minHeight, maxHeight); 1.4147 + } 1.4148 + 1.4149 + if (tentHeight > 0) { 1.4150 + widthAtMaxHeight = MULDIV(maxHeight, tentWidth, tentHeight); 1.4151 + if (widthAtMaxHeight < minWidth) 1.4152 + widthAtMaxHeight = minWidth; 1.4153 + widthAtMinHeight = MULDIV(minHeight, tentWidth, tentHeight); 1.4154 + if (widthAtMinHeight > maxWidth) 1.4155 + widthAtMinHeight = maxWidth; 1.4156 + } else { 1.4157 + widthAtMaxHeight = widthAtMinHeight = NS_CSS_MINMAX(tentWidth, minWidth, maxWidth); 1.4158 + } 1.4159 + 1.4160 + // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths : 1.4161 + 1.4162 + nscoord width, height; 1.4163 + 1.4164 + if (tentWidth > maxWidth) { 1.4165 + if (tentHeight > maxHeight) { 1.4166 + if (int64_t(maxWidth) * int64_t(tentHeight) <= 1.4167 + int64_t(maxHeight) * int64_t(tentWidth)) { 1.4168 + width = maxWidth; 1.4169 + height = heightAtMaxWidth; 1.4170 + } else { 1.4171 + width = widthAtMaxHeight; 1.4172 + height = maxHeight; 1.4173 + } 1.4174 + } else { 1.4175 + // This also covers "(w > max-width) and (h < min-height)" since in 1.4176 + // that case (max-width/w < 1), and with (h < min-height): 1.4177 + // max(max-width * h/w, min-height) == min-height 1.4178 + width = maxWidth; 1.4179 + height = heightAtMaxWidth; 1.4180 + } 1.4181 + } else if (tentWidth < minWidth) { 1.4182 + if (tentHeight < minHeight) { 1.4183 + if (int64_t(minWidth) * int64_t(tentHeight) <= 1.4184 + int64_t(minHeight) * int64_t(tentWidth)) { 1.4185 + width = widthAtMinHeight; 1.4186 + height = minHeight; 1.4187 + } else { 1.4188 + width = minWidth; 1.4189 + height = heightAtMinWidth; 1.4190 + } 1.4191 + } else { 1.4192 + // This also covers "(w < min-width) and (h > max-height)" since in 1.4193 + // that case (min-width/w > 1), and with (h > max-height): 1.4194 + // min(min-width * h/w, max-height) == max-height 1.4195 + width = minWidth; 1.4196 + height = heightAtMinWidth; 1.4197 + } 1.4198 + } else { 1.4199 + if (tentHeight > maxHeight) { 1.4200 + width = widthAtMaxHeight; 1.4201 + height = maxHeight; 1.4202 + } else if (tentHeight < minHeight) { 1.4203 + width = widthAtMinHeight; 1.4204 + height = minHeight; 1.4205 + } else { 1.4206 + width = tentWidth; 1.4207 + height = tentHeight; 1.4208 + } 1.4209 + } 1.4210 + 1.4211 + return nsSize(width, height); 1.4212 +} 1.4213 + 1.4214 +/* static */ nscoord 1.4215 +nsLayoutUtils::MinWidthFromInline(nsIFrame* aFrame, 1.4216 + nsRenderingContext* aRenderingContext) 1.4217 +{ 1.4218 + NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(), 1.4219 + "should not be container for font size inflation"); 1.4220 + 1.4221 + nsIFrame::InlineMinWidthData data; 1.4222 + DISPLAY_MIN_WIDTH(aFrame, data.prevLines); 1.4223 + aFrame->AddInlineMinWidth(aRenderingContext, &data); 1.4224 + data.ForceBreak(aRenderingContext); 1.4225 + return data.prevLines; 1.4226 +} 1.4227 + 1.4228 +/* static */ nscoord 1.4229 +nsLayoutUtils::PrefWidthFromInline(nsIFrame* aFrame, 1.4230 + nsRenderingContext* aRenderingContext) 1.4231 +{ 1.4232 + NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(), 1.4233 + "should not be container for font size inflation"); 1.4234 + 1.4235 + nsIFrame::InlinePrefWidthData data; 1.4236 + DISPLAY_PREF_WIDTH(aFrame, data.prevLines); 1.4237 + aFrame->AddInlinePrefWidth(aRenderingContext, &data); 1.4238 + data.ForceBreak(aRenderingContext); 1.4239 + return data.prevLines; 1.4240 +} 1.4241 + 1.4242 +static nscolor 1.4243 +DarkenColor(nscolor aColor) 1.4244 +{ 1.4245 + uint16_t hue, sat, value; 1.4246 + uint8_t alpha; 1.4247 + 1.4248 + // convert the RBG to HSV so we can get the lightness (which is the v) 1.4249 + NS_RGB2HSV(aColor, hue, sat, value, alpha); 1.4250 + 1.4251 + // The goal here is to send white to black while letting colored 1.4252 + // stuff stay colored... So we adopt the following approach. 1.4253 + // Something with sat = 0 should end up with value = 0. Something 1.4254 + // with a high sat can end up with a high value and it's ok.... At 1.4255 + // the same time, we don't want to make things lighter. Do 1.4256 + // something simple, since it seems to work. 1.4257 + if (value > sat) { 1.4258 + value = sat; 1.4259 + // convert this color back into the RGB color space. 1.4260 + NS_HSV2RGB(aColor, hue, sat, value, alpha); 1.4261 + } 1.4262 + return aColor; 1.4263 +} 1.4264 + 1.4265 +// Check whether we should darken text/decoration colors. We need to do this if 1.4266 +// background images and colors are being suppressed, because that means 1.4267 +// light text will not be visible against the (presumed light-colored) background. 1.4268 +static bool 1.4269 +ShouldDarkenColors(nsPresContext* aPresContext) 1.4270 +{ 1.4271 + return !aPresContext->GetBackgroundColorDraw() && 1.4272 + !aPresContext->GetBackgroundImageDraw(); 1.4273 +} 1.4274 + 1.4275 +nscolor 1.4276 +nsLayoutUtils::GetColor(nsIFrame* aFrame, nsCSSProperty aProperty) 1.4277 +{ 1.4278 + if (aProperty == eCSSProperty_color) 1.4279 + { 1.4280 + nscolor nativeColor = NS_RGB(0, 0, 0); 1.4281 + if (GetNativeTextColor(aFrame, nativeColor)) 1.4282 + return nativeColor; 1.4283 + } 1.4284 + 1.4285 + nscolor color = aFrame->GetVisitedDependentColor(aProperty); 1.4286 + if (ShouldDarkenColors(aFrame->PresContext())) { 1.4287 + color = DarkenColor(color); 1.4288 + } 1.4289 + 1.4290 + return color; 1.4291 +} 1.4292 + 1.4293 +bool 1.4294 +nsLayoutUtils::GetNativeTextColor(nsIFrame* aFrame, nscolor& aColor) 1.4295 +{ 1.4296 + nsPresContext *presContext = aFrame->PresContext(); 1.4297 + if (!presContext->IsChrome()) { 1.4298 + // If native appearance was used to draw the background of the containing 1.4299 + // frame, return a contrasting native foreground color instead of the 1.4300 + // color from the element's style. This avoids a problem where black 1.4301 + // text was displayed on a black background when a Windows theme such as 1.4302 + // "High Contrast Black" was used. The background is drawn inside 1.4303 + // nsNativeThemeWin::ClassicDrawWidgetBackground(). 1.4304 + // 1.4305 + // Because both the background color and this foreground color are used 1.4306 + // directly without exposing the colors via CSS computed styles, the 1.4307 + // native colors are not leaked to content. 1.4308 + nsIFrame* bgFrame = 1.4309 + nsCSSRendering::FindNonTransparentBackgroundFrame(aFrame); 1.4310 + if (bgFrame) { 1.4311 + const nsStyleDisplay* displayData = bgFrame->StyleDisplay(); 1.4312 + uint8_t widgetType = displayData->mAppearance; 1.4313 + nsITheme *theme = presContext->GetTheme(); 1.4314 + if (theme && widgetType && theme->ThemeSupportsWidget(presContext, 1.4315 + bgFrame, 1.4316 + widgetType)) { 1.4317 + bool isDisabled = false; 1.4318 + nsIContent* frameContent = bgFrame->GetContent(); 1.4319 + if (frameContent && frameContent->IsElement()) { 1.4320 + EventStates es = frameContent->AsElement()->State(); 1.4321 + isDisabled = es.HasState(NS_EVENT_STATE_DISABLED); 1.4322 + } 1.4323 + 1.4324 + if (NS_SUCCEEDED(LookAndFeel::GetColorForNativeAppearance(widgetType, 1.4325 + isDisabled, &aColor))) { 1.4326 + return true; 1.4327 + } 1.4328 + } 1.4329 + } 1.4330 + } 1.4331 + 1.4332 + return false; 1.4333 +} 1.4334 + 1.4335 +gfxFloat 1.4336 +nsLayoutUtils::GetSnappedBaselineY(nsIFrame* aFrame, gfxContext* aContext, 1.4337 + nscoord aY, nscoord aAscent) 1.4338 +{ 1.4339 + gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.4340 + gfxFloat baseline = gfxFloat(aY) + aAscent; 1.4341 + gfxRect putativeRect(0, baseline/appUnitsPerDevUnit, 1, 1); 1.4342 + if (!aContext->UserToDevicePixelSnapped(putativeRect, true)) 1.4343 + return baseline; 1.4344 + return aContext->DeviceToUser(putativeRect.TopLeft()).y * appUnitsPerDevUnit; 1.4345 +} 1.4346 + 1.4347 +void 1.4348 +nsLayoutUtils::DrawString(const nsIFrame* aFrame, 1.4349 + nsRenderingContext* aContext, 1.4350 + const char16_t* aString, 1.4351 + int32_t aLength, 1.4352 + nsPoint aPoint, 1.4353 + nsStyleContext* aStyleContext) 1.4354 +{ 1.4355 + nsresult rv = NS_ERROR_FAILURE; 1.4356 + nsPresContext* presContext = aFrame->PresContext(); 1.4357 + if (presContext->BidiEnabled()) { 1.4358 + nsBidiLevel level = 1.4359 + nsBidiPresUtils::BidiLevelFromStyle(aStyleContext ? 1.4360 + aStyleContext : aFrame->StyleContext()); 1.4361 + rv = nsBidiPresUtils::RenderText(aString, aLength, level, 1.4362 + presContext, *aContext, *aContext, 1.4363 + aPoint.x, aPoint.y); 1.4364 + } 1.4365 + if (NS_FAILED(rv)) 1.4366 + { 1.4367 + aContext->SetTextRunRTL(false); 1.4368 + aContext->DrawString(aString, aLength, aPoint.x, aPoint.y); 1.4369 + } 1.4370 +} 1.4371 + 1.4372 +nscoord 1.4373 +nsLayoutUtils::GetStringWidth(const nsIFrame* aFrame, 1.4374 + nsRenderingContext* aContext, 1.4375 + const char16_t* aString, 1.4376 + int32_t aLength) 1.4377 +{ 1.4378 + nsPresContext* presContext = aFrame->PresContext(); 1.4379 + if (presContext->BidiEnabled()) { 1.4380 + nsBidiLevel level = 1.4381 + nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext()); 1.4382 + return nsBidiPresUtils::MeasureTextWidth(aString, aLength, 1.4383 + level, presContext, *aContext); 1.4384 + } 1.4385 + aContext->SetTextRunRTL(false); 1.4386 + return aContext->GetWidth(aString, aLength); 1.4387 +} 1.4388 + 1.4389 +/* static */ void 1.4390 +nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame, 1.4391 + nsRenderingContext* aContext, 1.4392 + const nsRect& aTextRect, 1.4393 + const nsRect& aDirtyRect, 1.4394 + const nscolor& aForegroundColor, 1.4395 + TextShadowCallback aCallback, 1.4396 + void* aCallbackData) 1.4397 +{ 1.4398 + const nsStyleText* textStyle = aFrame->StyleText(); 1.4399 + if (!textStyle->HasTextShadow()) 1.4400 + return; 1.4401 + 1.4402 + // Text shadow happens with the last value being painted at the back, 1.4403 + // ie. it is painted first. 1.4404 + gfxContext* aDestCtx = aContext->ThebesContext(); 1.4405 + for (uint32_t i = textStyle->mTextShadow->Length(); i > 0; --i) { 1.4406 + nsCSSShadowItem* shadowDetails = textStyle->mTextShadow->ShadowAt(i - 1); 1.4407 + nsPoint shadowOffset(shadowDetails->mXOffset, 1.4408 + shadowDetails->mYOffset); 1.4409 + nscoord blurRadius = std::max(shadowDetails->mRadius, 0); 1.4410 + 1.4411 + nsRect shadowRect(aTextRect); 1.4412 + shadowRect.MoveBy(shadowOffset); 1.4413 + 1.4414 + nsPresContext* presCtx = aFrame->PresContext(); 1.4415 + nsContextBoxBlur contextBoxBlur; 1.4416 + gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius, 1.4417 + presCtx->AppUnitsPerDevPixel(), 1.4418 + aDestCtx, aDirtyRect, nullptr); 1.4419 + if (!shadowContext) 1.4420 + continue; 1.4421 + 1.4422 + nscolor shadowColor; 1.4423 + if (shadowDetails->mHasColor) 1.4424 + shadowColor = shadowDetails->mColor; 1.4425 + else 1.4426 + shadowColor = aForegroundColor; 1.4427 + 1.4428 + // Conjure an nsRenderingContext from a gfxContext for drawing the text 1.4429 + // to blur. 1.4430 + nsRefPtr<nsRenderingContext> renderingContext = new nsRenderingContext(); 1.4431 + renderingContext->Init(presCtx->DeviceContext(), shadowContext); 1.4432 + 1.4433 + aDestCtx->Save(); 1.4434 + aDestCtx->NewPath(); 1.4435 + aDestCtx->SetColor(gfxRGBA(shadowColor)); 1.4436 + 1.4437 + // The callback will draw whatever we want to blur as a shadow. 1.4438 + aCallback(renderingContext, shadowOffset, shadowColor, aCallbackData); 1.4439 + 1.4440 + contextBoxBlur.DoPaint(); 1.4441 + aDestCtx->Restore(); 1.4442 + } 1.4443 +} 1.4444 + 1.4445 +/* static */ nscoord 1.4446 +nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics, 1.4447 + nscoord aLineHeight) 1.4448 +{ 1.4449 + nscoord fontAscent = aFontMetrics->MaxAscent(); 1.4450 + nscoord fontHeight = aFontMetrics->MaxHeight(); 1.4451 + 1.4452 + nscoord leading = aLineHeight - fontHeight; 1.4453 + return fontAscent + leading/2; 1.4454 +} 1.4455 + 1.4456 + 1.4457 +/* static */ bool 1.4458 +nsLayoutUtils::GetFirstLineBaseline(const nsIFrame* aFrame, nscoord* aResult) 1.4459 +{ 1.4460 + LinePosition position; 1.4461 + if (!GetFirstLinePosition(aFrame, &position)) 1.4462 + return false; 1.4463 + *aResult = position.mBaseline; 1.4464 + return true; 1.4465 +} 1.4466 + 1.4467 +/* static */ bool 1.4468 +nsLayoutUtils::GetFirstLinePosition(const nsIFrame* aFrame, 1.4469 + LinePosition* aResult) 1.4470 +{ 1.4471 + const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame)); 1.4472 + if (!block) { 1.4473 + // For the first-line baseline we also have to check for a table, and if 1.4474 + // so, use the baseline of its first row. 1.4475 + nsIAtom* fType = aFrame->GetType(); 1.4476 + if (fType == nsGkAtoms::tableOuterFrame) { 1.4477 + aResult->mTop = 0; 1.4478 + aResult->mBaseline = aFrame->GetBaseline(); 1.4479 + // This is what we want for the list bullet caller; not sure if 1.4480 + // other future callers will want the same. 1.4481 + aResult->mBottom = aFrame->GetSize().height; 1.4482 + return true; 1.4483 + } 1.4484 + 1.4485 + // For first-line baselines, we have to consider scroll frames. 1.4486 + if (fType == nsGkAtoms::scrollFrame) { 1.4487 + nsIScrollableFrame *sFrame = do_QueryFrame(const_cast<nsIFrame*>(aFrame)); 1.4488 + if (!sFrame) { 1.4489 + NS_NOTREACHED("not scroll frame"); 1.4490 + } 1.4491 + LinePosition kidPosition; 1.4492 + if (GetFirstLinePosition(sFrame->GetScrolledFrame(), &kidPosition)) { 1.4493 + // Consider only the border and padding that contributes to the 1.4494 + // kid's position, not the scrolling, so we get the initial 1.4495 + // position. 1.4496 + *aResult = kidPosition + aFrame->GetUsedBorderAndPadding().top; 1.4497 + return true; 1.4498 + } 1.4499 + return false; 1.4500 + } 1.4501 + 1.4502 + if (fType == nsGkAtoms::fieldSetFrame) { 1.4503 + LinePosition kidPosition; 1.4504 + nsIFrame* kid = aFrame->GetFirstPrincipalChild(); 1.4505 + // kid might be a legend frame here, but that's ok. 1.4506 + if (GetFirstLinePosition(kid, &kidPosition)) { 1.4507 + *aResult = kidPosition + kid->GetNormalPosition().y; 1.4508 + return true; 1.4509 + } 1.4510 + return false; 1.4511 + } 1.4512 + 1.4513 + // No baseline. 1.4514 + return false; 1.4515 + } 1.4516 + 1.4517 + for (nsBlockFrame::const_line_iterator line = block->begin_lines(), 1.4518 + line_end = block->end_lines(); 1.4519 + line != line_end; ++line) { 1.4520 + if (line->IsBlock()) { 1.4521 + nsIFrame *kid = line->mFirstChild; 1.4522 + LinePosition kidPosition; 1.4523 + if (GetFirstLinePosition(kid, &kidPosition)) { 1.4524 + *aResult = kidPosition + kid->GetNormalPosition().y; 1.4525 + return true; 1.4526 + } 1.4527 + } else { 1.4528 + // XXX Is this the right test? We have some bogus empty lines 1.4529 + // floating around, but IsEmpty is perhaps too weak. 1.4530 + if (line->BSize() != 0 || !line->IsEmpty()) { 1.4531 + nscoord top = line->BStart(); 1.4532 + aResult->mTop = top; 1.4533 + aResult->mBaseline = top + line->GetAscent(); 1.4534 + aResult->mBottom = top + line->BSize(); 1.4535 + return true; 1.4536 + } 1.4537 + } 1.4538 + } 1.4539 + return false; 1.4540 +} 1.4541 + 1.4542 +/* static */ bool 1.4543 +nsLayoutUtils::GetLastLineBaseline(const nsIFrame* aFrame, nscoord* aResult) 1.4544 +{ 1.4545 + const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame)); 1.4546 + if (!block) 1.4547 + // No baseline. (We intentionally don't descend into scroll frames.) 1.4548 + return false; 1.4549 + 1.4550 + for (nsBlockFrame::const_reverse_line_iterator line = block->rbegin_lines(), 1.4551 + line_end = block->rend_lines(); 1.4552 + line != line_end; ++line) { 1.4553 + if (line->IsBlock()) { 1.4554 + nsIFrame *kid = line->mFirstChild; 1.4555 + nscoord kidBaseline; 1.4556 + if (GetLastLineBaseline(kid, &kidBaseline)) { 1.4557 + // Ignore relative positioning for baseline calculations 1.4558 + *aResult = kidBaseline + kid->GetNormalPosition().y; 1.4559 + return true; 1.4560 + } else if (kid->GetType() == nsGkAtoms::scrollFrame) { 1.4561 + // Use the bottom of the scroll frame. 1.4562 + // XXX CSS2.1 really doesn't say what to do here. 1.4563 + *aResult = kid->GetNormalPosition().y + kid->GetRect().height; 1.4564 + return true; 1.4565 + } 1.4566 + } else { 1.4567 + // XXX Is this the right test? We have some bogus empty lines 1.4568 + // floating around, but IsEmpty is perhaps too weak. 1.4569 + if (line->BSize() != 0 || !line->IsEmpty()) { 1.4570 + *aResult = line->BStart() + line->GetAscent(); 1.4571 + return true; 1.4572 + } 1.4573 + } 1.4574 + } 1.4575 + return false; 1.4576 +} 1.4577 + 1.4578 +static nscoord 1.4579 +CalculateBlockContentBottom(nsBlockFrame* aFrame) 1.4580 +{ 1.4581 + NS_PRECONDITION(aFrame, "null ptr"); 1.4582 + 1.4583 + nscoord contentBottom = 0; 1.4584 + 1.4585 + for (nsBlockFrame::line_iterator line = aFrame->begin_lines(), 1.4586 + line_end = aFrame->end_lines(); 1.4587 + line != line_end; ++line) { 1.4588 + if (line->IsBlock()) { 1.4589 + nsIFrame* child = line->mFirstChild; 1.4590 + nscoord offset = child->GetNormalPosition().y; 1.4591 + contentBottom = std::max(contentBottom, 1.4592 + nsLayoutUtils::CalculateContentBottom(child) + offset); 1.4593 + } 1.4594 + else { 1.4595 + contentBottom = std::max(contentBottom, line->BEnd()); 1.4596 + } 1.4597 + } 1.4598 + return contentBottom; 1.4599 +} 1.4600 + 1.4601 +/* static */ nscoord 1.4602 +nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame) 1.4603 +{ 1.4604 + NS_PRECONDITION(aFrame, "null ptr"); 1.4605 + 1.4606 + nscoord contentBottom = aFrame->GetRect().height; 1.4607 + 1.4608 + // We want scrollable overflow rather than visual because this 1.4609 + // calculation is intended to affect layout. 1.4610 + if (aFrame->GetScrollableOverflowRect().height > contentBottom) { 1.4611 + nsIFrame::ChildListIDs skip(nsIFrame::kOverflowList | 1.4612 + nsIFrame::kExcessOverflowContainersList | 1.4613 + nsIFrame::kOverflowOutOfFlowList); 1.4614 + nsBlockFrame* blockFrame = GetAsBlock(aFrame); 1.4615 + if (blockFrame) { 1.4616 + contentBottom = 1.4617 + std::max(contentBottom, CalculateBlockContentBottom(blockFrame)); 1.4618 + skip |= nsIFrame::kPrincipalList; 1.4619 + } 1.4620 + nsIFrame::ChildListIterator lists(aFrame); 1.4621 + for (; !lists.IsDone(); lists.Next()) { 1.4622 + if (!skip.Contains(lists.CurrentID())) { 1.4623 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.4624 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.4625 + nsIFrame* child = childFrames.get(); 1.4626 + nscoord offset = child->GetNormalPosition().y; 1.4627 + contentBottom = std::max(contentBottom, 1.4628 + CalculateContentBottom(child) + offset); 1.4629 + } 1.4630 + } 1.4631 + } 1.4632 + } 1.4633 + return contentBottom; 1.4634 +} 1.4635 + 1.4636 +/* static */ nsIFrame* 1.4637 +nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame) 1.4638 +{ 1.4639 + nsIFrame* layer; 1.4640 + for (layer = aFrame; layer; layer = layer->GetParent()) { 1.4641 + if (layer->IsPositioned() || 1.4642 + (layer->GetParent() && 1.4643 + layer->GetParent()->GetType() == nsGkAtoms::scrollFrame)) 1.4644 + break; 1.4645 + } 1.4646 + if (layer) 1.4647 + return layer; 1.4648 + return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame(); 1.4649 +} 1.4650 + 1.4651 +GraphicsFilter 1.4652 +nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame) 1.4653 +{ 1.4654 + GraphicsFilter defaultFilter = GraphicsFilter::FILTER_GOOD; 1.4655 + nsStyleContext *sc; 1.4656 + if (nsCSSRendering::IsCanvasFrame(aForFrame)) { 1.4657 + nsCSSRendering::FindBackground(aForFrame, &sc); 1.4658 + } else { 1.4659 + sc = aForFrame->StyleContext(); 1.4660 + } 1.4661 + 1.4662 + switch (sc->StyleSVG()->mImageRendering) { 1.4663 + case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED: 1.4664 + return GraphicsFilter::FILTER_FAST; 1.4665 + case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY: 1.4666 + return GraphicsFilter::FILTER_BEST; 1.4667 + case NS_STYLE_IMAGE_RENDERING_CRISPEDGES: 1.4668 + return GraphicsFilter::FILTER_NEAREST; 1.4669 + default: 1.4670 + return defaultFilter; 1.4671 + } 1.4672 +} 1.4673 + 1.4674 +/** 1.4675 + * Given an image being drawn into an appunit coordinate system, and 1.4676 + * a point in that coordinate system, map the point back into image 1.4677 + * pixel space. 1.4678 + * @param aSize the size of the image, in pixels 1.4679 + * @param aDest the rectangle that the image is being mapped into 1.4680 + * @param aPt a point in the same coordinate system as the rectangle 1.4681 + */ 1.4682 +static gfxPoint 1.4683 +MapToFloatImagePixels(const gfxSize& aSize, 1.4684 + const gfxRect& aDest, const gfxPoint& aPt) 1.4685 +{ 1.4686 + return gfxPoint(((aPt.x - aDest.X())*aSize.width)/aDest.Width(), 1.4687 + ((aPt.y - aDest.Y())*aSize.height)/aDest.Height()); 1.4688 +} 1.4689 + 1.4690 +/** 1.4691 + * Given an image being drawn into an pixel-based coordinate system, and 1.4692 + * a point in image space, map the point into the pixel-based coordinate 1.4693 + * system. 1.4694 + * @param aSize the size of the image, in pixels 1.4695 + * @param aDest the rectangle that the image is being mapped into 1.4696 + * @param aPt a point in image space 1.4697 + */ 1.4698 +static gfxPoint 1.4699 +MapToFloatUserPixels(const gfxSize& aSize, 1.4700 + const gfxRect& aDest, const gfxPoint& aPt) 1.4701 +{ 1.4702 + return gfxPoint(aPt.x*aDest.Width()/aSize.width + aDest.X(), 1.4703 + aPt.y*aDest.Height()/aSize.height + aDest.Y()); 1.4704 +} 1.4705 + 1.4706 +/* static */ gfxRect 1.4707 +nsLayoutUtils::RectToGfxRect(const nsRect& aRect, int32_t aAppUnitsPerDevPixel) 1.4708 +{ 1.4709 + return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel, 1.4710 + gfxFloat(aRect.y) / aAppUnitsPerDevPixel, 1.4711 + gfxFloat(aRect.width) / aAppUnitsPerDevPixel, 1.4712 + gfxFloat(aRect.height) / aAppUnitsPerDevPixel); 1.4713 +} 1.4714 + 1.4715 +struct SnappedImageDrawingParameters { 1.4716 + // A transform from either device space or user space (depending on mResetCTM) 1.4717 + // to image space 1.4718 + gfxMatrix mUserSpaceToImageSpace; 1.4719 + // A device-space, pixel-aligned rectangle to fill 1.4720 + gfxRect mFillRect; 1.4721 + // A pixel rectangle in tiled image space outside of which gfx should not 1.4722 + // sample (using EXTEND_PAD as necessary) 1.4723 + nsIntRect mSubimage; 1.4724 + // Whether there's anything to draw at all 1.4725 + bool mShouldDraw; 1.4726 + // true iff the CTM of the rendering context needs to be reset to the 1.4727 + // identity matrix before drawing 1.4728 + bool mResetCTM; 1.4729 + 1.4730 + SnappedImageDrawingParameters() 1.4731 + : mShouldDraw(false) 1.4732 + , mResetCTM(false) 1.4733 + {} 1.4734 + 1.4735 + SnappedImageDrawingParameters(const gfxMatrix& aUserSpaceToImageSpace, 1.4736 + const gfxRect& aFillRect, 1.4737 + const nsIntRect& aSubimage, 1.4738 + bool aResetCTM) 1.4739 + : mUserSpaceToImageSpace(aUserSpaceToImageSpace) 1.4740 + , mFillRect(aFillRect) 1.4741 + , mSubimage(aSubimage) 1.4742 + , mShouldDraw(true) 1.4743 + , mResetCTM(aResetCTM) 1.4744 + {} 1.4745 +}; 1.4746 + 1.4747 +/** 1.4748 + * Given a set of input parameters, compute certain output parameters 1.4749 + * for drawing an image with the image snapping algorithm. 1.4750 + * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering 1.4751 + * 1.4752 + * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters 1.4753 + */ 1.4754 +static SnappedImageDrawingParameters 1.4755 +ComputeSnappedImageDrawingParameters(gfxContext* aCtx, 1.4756 + int32_t aAppUnitsPerDevPixel, 1.4757 + const nsRect aDest, 1.4758 + const nsRect aFill, 1.4759 + const nsPoint aAnchor, 1.4760 + const nsRect aDirty, 1.4761 + const nsIntSize aImageSize) 1.4762 + 1.4763 +{ 1.4764 + if (aDest.IsEmpty() || aFill.IsEmpty() || !aImageSize.width || !aImageSize.height) 1.4765 + return SnappedImageDrawingParameters(); 1.4766 + 1.4767 + gfxRect devPixelDest = 1.4768 + nsLayoutUtils::RectToGfxRect(aDest, aAppUnitsPerDevPixel); 1.4769 + gfxRect devPixelFill = 1.4770 + nsLayoutUtils::RectToGfxRect(aFill, aAppUnitsPerDevPixel); 1.4771 + gfxRect devPixelDirty = 1.4772 + nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel); 1.4773 + 1.4774 + gfxMatrix currentMatrix = aCtx->CurrentMatrix(); 1.4775 + gfxRect fill = devPixelFill; 1.4776 + bool didSnap; 1.4777 + // Snap even if we have a scale in the context. But don't snap if 1.4778 + // we have something that's not translation+scale, or if the scale flips in 1.4779 + // the X or Y direction, because snapped image drawing can't handle that yet. 1.4780 + if (!currentMatrix.HasNonAxisAlignedTransform() && 1.4781 + currentMatrix.xx > 0.0 && currentMatrix.yy > 0.0 && 1.4782 + aCtx->UserToDevicePixelSnapped(fill, true)) { 1.4783 + didSnap = true; 1.4784 + if (fill.IsEmpty()) { 1.4785 + return SnappedImageDrawingParameters(); 1.4786 + } 1.4787 + } else { 1.4788 + didSnap = false; 1.4789 + fill = devPixelFill; 1.4790 + } 1.4791 + 1.4792 + gfxSize imageSize(aImageSize.width, aImageSize.height); 1.4793 + 1.4794 + // Compute the set of pixels that would be sampled by an ideal rendering 1.4795 + gfxPoint subimageTopLeft = 1.4796 + MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft()); 1.4797 + gfxPoint subimageBottomRight = 1.4798 + MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight()); 1.4799 + nsIntRect intSubimage; 1.4800 + intSubimage.MoveTo(NSToIntFloor(subimageTopLeft.x), 1.4801 + NSToIntFloor(subimageTopLeft.y)); 1.4802 + intSubimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - intSubimage.x, 1.4803 + NSToIntCeil(subimageBottomRight.y) - intSubimage.y); 1.4804 + 1.4805 + // Compute the anchor point and compute final fill rect. 1.4806 + // This code assumes that pixel-based devices have one pixel per 1.4807 + // device unit! 1.4808 + gfxPoint anchorPoint(gfxFloat(aAnchor.x)/aAppUnitsPerDevPixel, 1.4809 + gfxFloat(aAnchor.y)/aAppUnitsPerDevPixel); 1.4810 + gfxPoint imageSpaceAnchorPoint = 1.4811 + MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint); 1.4812 + 1.4813 + if (didSnap) { 1.4814 + imageSpaceAnchorPoint.Round(); 1.4815 + anchorPoint = imageSpaceAnchorPoint; 1.4816 + anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint); 1.4817 + anchorPoint = currentMatrix.Transform(anchorPoint); 1.4818 + anchorPoint.Round(); 1.4819 + 1.4820 + // This form of Transform is safe to call since non-axis-aligned 1.4821 + // transforms wouldn't be snapped. 1.4822 + devPixelDirty = currentMatrix.Transform(devPixelDirty); 1.4823 + } 1.4824 + 1.4825 + gfxFloat scaleX = imageSize.width*aAppUnitsPerDevPixel/aDest.width; 1.4826 + gfxFloat scaleY = imageSize.height*aAppUnitsPerDevPixel/aDest.height; 1.4827 + if (didSnap) { 1.4828 + // We'll reset aCTX to the identity matrix before drawing, so we need to 1.4829 + // adjust our scales to match. 1.4830 + scaleX /= currentMatrix.xx; 1.4831 + scaleY /= currentMatrix.yy; 1.4832 + } 1.4833 + gfxFloat translateX = imageSpaceAnchorPoint.x - anchorPoint.x*scaleX; 1.4834 + gfxFloat translateY = imageSpaceAnchorPoint.y - anchorPoint.y*scaleY; 1.4835 + gfxMatrix transform(scaleX, 0, 0, scaleY, translateX, translateY); 1.4836 + 1.4837 + gfxRect finalFillRect = fill; 1.4838 + // If the user-space-to-image-space transform is not a straight 1.4839 + // translation by integers, then filtering will occur, and 1.4840 + // restricting the fill rect to the dirty rect would change the values 1.4841 + // computed for edge pixels, which we can't allow. 1.4842 + // Also, if didSnap is false then rounding out 'devPixelDirty' might not 1.4843 + // produce pixel-aligned coordinates, which would also break the values 1.4844 + // computed for edge pixels. 1.4845 + if (didSnap && !transform.HasNonIntegerTranslation()) { 1.4846 + devPixelDirty.RoundOut(); 1.4847 + finalFillRect = fill.Intersect(devPixelDirty); 1.4848 + } 1.4849 + if (finalFillRect.IsEmpty()) 1.4850 + return SnappedImageDrawingParameters(); 1.4851 + 1.4852 + return SnappedImageDrawingParameters(transform, finalFillRect, intSubimage, 1.4853 + didSnap); 1.4854 +} 1.4855 + 1.4856 + 1.4857 +static nsresult 1.4858 +DrawImageInternal(nsRenderingContext* aRenderingContext, 1.4859 + imgIContainer* aImage, 1.4860 + GraphicsFilter aGraphicsFilter, 1.4861 + const nsRect& aDest, 1.4862 + const nsRect& aFill, 1.4863 + const nsPoint& aAnchor, 1.4864 + const nsRect& aDirty, 1.4865 + const nsIntSize& aImageSize, 1.4866 + const SVGImageContext* aSVGContext, 1.4867 + uint32_t aImageFlags) 1.4868 +{ 1.4869 + if (aDest.Contains(aFill)) { 1.4870 + aImageFlags |= imgIContainer::FLAG_CLAMP; 1.4871 + } 1.4872 + int32_t appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel(); 1.4873 + gfxContext* ctx = aRenderingContext->ThebesContext(); 1.4874 + 1.4875 + SnappedImageDrawingParameters drawingParams = 1.4876 + ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill, 1.4877 + aAnchor, aDirty, aImageSize); 1.4878 + 1.4879 + if (!drawingParams.mShouldDraw) 1.4880 + return NS_OK; 1.4881 + 1.4882 + gfxContextMatrixAutoSaveRestore saveMatrix(ctx); 1.4883 + if (drawingParams.mResetCTM) { 1.4884 + ctx->IdentityMatrix(); 1.4885 + } 1.4886 + 1.4887 + aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace, 1.4888 + drawingParams.mFillRect, drawingParams.mSubimage, aImageSize, 1.4889 + aSVGContext, imgIContainer::FRAME_CURRENT, aImageFlags); 1.4890 + return NS_OK; 1.4891 +} 1.4892 + 1.4893 +/* static */ void 1.4894 +nsLayoutUtils::DrawPixelSnapped(nsRenderingContext* aRenderingContext, 1.4895 + gfxDrawable* aDrawable, 1.4896 + GraphicsFilter aFilter, 1.4897 + const nsRect& aDest, 1.4898 + const nsRect& aFill, 1.4899 + const nsPoint& aAnchor, 1.4900 + const nsRect& aDirty) 1.4901 +{ 1.4902 + int32_t appUnitsPerDevPixel = aRenderingContext->AppUnitsPerDevPixel(); 1.4903 + gfxContext* ctx = aRenderingContext->ThebesContext(); 1.4904 + gfxIntSize drawableSize = aDrawable->Size(); 1.4905 + nsIntSize imageSize(drawableSize.width, drawableSize.height); 1.4906 + 1.4907 + SnappedImageDrawingParameters drawingParams = 1.4908 + ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill, 1.4909 + aAnchor, aDirty, imageSize); 1.4910 + 1.4911 + if (!drawingParams.mShouldDraw) 1.4912 + return; 1.4913 + 1.4914 + gfxContextMatrixAutoSaveRestore saveMatrix(ctx); 1.4915 + if (drawingParams.mResetCTM) { 1.4916 + ctx->IdentityMatrix(); 1.4917 + } 1.4918 + 1.4919 + gfxRect sourceRect = 1.4920 + drawingParams.mUserSpaceToImageSpace.Transform(drawingParams.mFillRect); 1.4921 + gfxRect imageRect(0, 0, imageSize.width, imageSize.height); 1.4922 + gfxRect subimage(drawingParams.mSubimage.x, drawingParams.mSubimage.y, 1.4923 + drawingParams.mSubimage.width, drawingParams.mSubimage.height); 1.4924 + 1.4925 + NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(), 1.4926 + "We must be allowed to sample *some* source pixels!"); 1.4927 + 1.4928 + gfxUtils::DrawPixelSnapped(ctx, aDrawable, 1.4929 + drawingParams.mUserSpaceToImageSpace, subimage, 1.4930 + sourceRect, imageRect, drawingParams.mFillRect, 1.4931 + gfxImageFormat::ARGB32, aFilter); 1.4932 +} 1.4933 + 1.4934 +/* static */ nsresult 1.4935 +nsLayoutUtils::DrawSingleUnscaledImage(nsRenderingContext* aRenderingContext, 1.4936 + imgIContainer* aImage, 1.4937 + GraphicsFilter aGraphicsFilter, 1.4938 + const nsPoint& aDest, 1.4939 + const nsRect* aDirty, 1.4940 + uint32_t aImageFlags, 1.4941 + const nsRect* aSourceArea) 1.4942 +{ 1.4943 + nsIntSize imageSize; 1.4944 + aImage->GetWidth(&imageSize.width); 1.4945 + aImage->GetHeight(&imageSize.height); 1.4946 + NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE); 1.4947 + 1.4948 + nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel(); 1.4949 + nsSize size(imageSize.width*appUnitsPerCSSPixel, 1.4950 + imageSize.height*appUnitsPerCSSPixel); 1.4951 + 1.4952 + nsRect source; 1.4953 + if (aSourceArea) { 1.4954 + source = *aSourceArea; 1.4955 + } else { 1.4956 + source.SizeTo(size); 1.4957 + } 1.4958 + 1.4959 + nsRect dest(aDest - source.TopLeft(), size); 1.4960 + nsRect fill(aDest, source.Size()); 1.4961 + // Ensure that only a single image tile is drawn. If aSourceArea extends 1.4962 + // outside the image bounds, we want to honor the aSourceArea-to-aDest 1.4963 + // translation but we don't want to actually tile the image. 1.4964 + fill.IntersectRect(fill, dest); 1.4965 + return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, 1.4966 + dest, fill, aDest, aDirty ? *aDirty : dest, 1.4967 + imageSize, nullptr, aImageFlags); 1.4968 +} 1.4969 + 1.4970 +/* static */ nsresult 1.4971 +nsLayoutUtils::DrawSingleImage(nsRenderingContext* aRenderingContext, 1.4972 + imgIContainer* aImage, 1.4973 + GraphicsFilter aGraphicsFilter, 1.4974 + const nsRect& aDest, 1.4975 + const nsRect& aDirty, 1.4976 + const SVGImageContext* aSVGContext, 1.4977 + uint32_t aImageFlags, 1.4978 + const nsRect* aSourceArea) 1.4979 +{ 1.4980 + nsIntSize imageSize; 1.4981 + if (aImage->GetType() == imgIContainer::TYPE_VECTOR) { 1.4982 + // We choose a size for vector images that emulates a raster image which 1.4983 + // is perfectly sized for the destination rect: each pixel in the image 1.4984 + // maps exactly to a single pixel on-screen. 1.4985 + nscoord appUnitsPerDevPx = aRenderingContext->AppUnitsPerDevPixel(); 1.4986 + imageSize.width = NSAppUnitsToIntPixels(aDest.width, appUnitsPerDevPx); 1.4987 + imageSize.height = NSAppUnitsToIntPixels(aDest.height, appUnitsPerDevPx); 1.4988 + } else { 1.4989 + // Raster images have an intrinsic size, so we just use that. 1.4990 + aImage->GetWidth(&imageSize.width); 1.4991 + aImage->GetHeight(&imageSize.height); 1.4992 + } 1.4993 + NS_ENSURE_TRUE(imageSize.width > 0 && imageSize.height > 0, NS_ERROR_FAILURE); 1.4994 + 1.4995 + nsRect source; 1.4996 + if (aSourceArea) { 1.4997 + source = *aSourceArea; 1.4998 + } else { 1.4999 + nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel(); 1.5000 + source.SizeTo(imageSize.width*appUnitsPerCSSPixel, 1.5001 + imageSize.height*appUnitsPerCSSPixel); 1.5002 + } 1.5003 + 1.5004 + nsRect dest = nsLayoutUtils::GetWholeImageDestination(imageSize, source, 1.5005 + aDest); 1.5006 + // Ensure that only a single image tile is drawn. If aSourceArea extends 1.5007 + // outside the image bounds, we want to honor the aSourceArea-to-aDest 1.5008 + // transform but we don't want to actually tile the image. 1.5009 + nsRect fill; 1.5010 + fill.IntersectRect(aDest, dest); 1.5011 + return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, dest, fill, 1.5012 + fill.TopLeft(), aDirty, imageSize, aSVGContext, aImageFlags); 1.5013 +} 1.5014 + 1.5015 +/* static */ void 1.5016 +nsLayoutUtils::ComputeSizeForDrawing(imgIContainer *aImage, 1.5017 + nsIntSize& aImageSize, /*outparam*/ 1.5018 + nsSize& aIntrinsicRatio, /*outparam*/ 1.5019 + bool& aGotWidth, /*outparam*/ 1.5020 + bool& aGotHeight /*outparam*/) 1.5021 +{ 1.5022 + aGotWidth = NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width)); 1.5023 + aGotHeight = NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height)); 1.5024 + bool gotRatio = NS_SUCCEEDED(aImage->GetIntrinsicRatio(&aIntrinsicRatio)); 1.5025 + 1.5026 + if (!(aGotWidth && aGotHeight) && !gotRatio) { 1.5027 + // We hit an error (say, because the image failed to load or couldn't be 1.5028 + // decoded) and should return zero size. 1.5029 + aGotWidth = aGotHeight = true; 1.5030 + aImageSize = nsIntSize(0, 0); 1.5031 + aIntrinsicRatio = nsSize(0, 0); 1.5032 + } 1.5033 +} 1.5034 + 1.5035 + 1.5036 +/* static */ nsresult 1.5037 +nsLayoutUtils::DrawBackgroundImage(nsRenderingContext* aRenderingContext, 1.5038 + imgIContainer* aImage, 1.5039 + const nsIntSize& aImageSize, 1.5040 + GraphicsFilter aGraphicsFilter, 1.5041 + const nsRect& aDest, 1.5042 + const nsRect& aFill, 1.5043 + const nsPoint& aAnchor, 1.5044 + const nsRect& aDirty, 1.5045 + uint32_t aImageFlags) 1.5046 +{ 1.5047 + PROFILER_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage"); 1.5048 + 1.5049 + if (UseBackgroundNearestFiltering()) { 1.5050 + aGraphicsFilter = GraphicsFilter::FILTER_NEAREST; 1.5051 + } 1.5052 + 1.5053 + return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, 1.5054 + aDest, aFill, aAnchor, aDirty, 1.5055 + aImageSize, nullptr, aImageFlags); 1.5056 +} 1.5057 + 1.5058 +/* static */ nsresult 1.5059 +nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext, 1.5060 + imgIContainer* aImage, 1.5061 + GraphicsFilter aGraphicsFilter, 1.5062 + const nsRect& aDest, 1.5063 + const nsRect& aFill, 1.5064 + const nsPoint& aAnchor, 1.5065 + const nsRect& aDirty, 1.5066 + uint32_t aImageFlags) 1.5067 +{ 1.5068 + nsIntSize imageSize; 1.5069 + nsSize imageRatio; 1.5070 + bool gotHeight, gotWidth; 1.5071 + ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight); 1.5072 + 1.5073 + // XXX Dimensionless images shouldn't fall back to filled-area size -- the 1.5074 + // caller should provide the image size, a la DrawBackgroundImage. 1.5075 + if (gotWidth != gotHeight) { 1.5076 + if (!gotWidth) { 1.5077 + if (imageRatio.height != 0) { 1.5078 + imageSize.width = 1.5079 + NSCoordSaturatingNonnegativeMultiply(imageSize.height, 1.5080 + float(imageRatio.width) / 1.5081 + float(imageRatio.height)); 1.5082 + gotWidth = true; 1.5083 + } 1.5084 + } else { 1.5085 + if (imageRatio.width != 0) { 1.5086 + imageSize.height = 1.5087 + NSCoordSaturatingNonnegativeMultiply(imageSize.width, 1.5088 + float(imageRatio.height) / 1.5089 + float(imageRatio.width)); 1.5090 + gotHeight = true; 1.5091 + } 1.5092 + } 1.5093 + } 1.5094 + 1.5095 + if (!gotWidth) { 1.5096 + imageSize.width = nsPresContext::AppUnitsToIntCSSPixels(aFill.width); 1.5097 + } 1.5098 + if (!gotHeight) { 1.5099 + imageSize.height = nsPresContext::AppUnitsToIntCSSPixels(aFill.height); 1.5100 + } 1.5101 + 1.5102 + return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter, 1.5103 + aDest, aFill, aAnchor, aDirty, 1.5104 + imageSize, nullptr, aImageFlags); 1.5105 +} 1.5106 + 1.5107 +/* static */ nsRect 1.5108 +nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize, 1.5109 + const nsRect& aImageSourceArea, 1.5110 + const nsRect& aDestArea) 1.5111 +{ 1.5112 + double scaleX = double(aDestArea.width)/aImageSourceArea.width; 1.5113 + double scaleY = double(aDestArea.height)/aImageSourceArea.height; 1.5114 + nscoord destOffsetX = NSToCoordRound(aImageSourceArea.x*scaleX); 1.5115 + nscoord destOffsetY = NSToCoordRound(aImageSourceArea.y*scaleY); 1.5116 + nscoord appUnitsPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel(); 1.5117 + nscoord wholeSizeX = NSToCoordRound(aWholeImageSize.width*appUnitsPerCSSPixel*scaleX); 1.5118 + nscoord wholeSizeY = NSToCoordRound(aWholeImageSize.height*appUnitsPerCSSPixel*scaleY); 1.5119 + return nsRect(aDestArea.TopLeft() - nsPoint(destOffsetX, destOffsetY), 1.5120 + nsSize(wholeSizeX, wholeSizeY)); 1.5121 +} 1.5122 + 1.5123 +/* static */ already_AddRefed<imgIContainer> 1.5124 +nsLayoutUtils::OrientImage(imgIContainer* aContainer, 1.5125 + const nsStyleImageOrientation& aOrientation) 1.5126 +{ 1.5127 + MOZ_ASSERT(aContainer, "Should have an image container"); 1.5128 + nsCOMPtr<imgIContainer> img(aContainer); 1.5129 + 1.5130 + if (aOrientation.IsFromImage()) { 1.5131 + img = ImageOps::Orient(img, img->GetOrientation()); 1.5132 + } else if (!aOrientation.IsDefault()) { 1.5133 + Angle angle = aOrientation.Angle(); 1.5134 + Flip flip = aOrientation.IsFlipped() ? Flip::Horizontal 1.5135 + : Flip::Unflipped; 1.5136 + img = ImageOps::Orient(img, Orientation(angle, flip)); 1.5137 + } 1.5138 + 1.5139 + return img.forget(); 1.5140 +} 1.5141 + 1.5142 +static bool NonZeroStyleCoord(const nsStyleCoord& aCoord) 1.5143 +{ 1.5144 + if (aCoord.IsCoordPercentCalcUnit()) { 1.5145 + // Since negative results are clamped to 0, check > 0. 1.5146 + return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 || 1.5147 + nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0; 1.5148 + } 1.5149 + 1.5150 + return true; 1.5151 +} 1.5152 + 1.5153 +/* static */ bool 1.5154 +nsLayoutUtils::HasNonZeroCorner(const nsStyleCorners& aCorners) 1.5155 +{ 1.5156 + NS_FOR_CSS_HALF_CORNERS(corner) { 1.5157 + if (NonZeroStyleCoord(aCorners.Get(corner))) 1.5158 + return true; 1.5159 + } 1.5160 + return false; 1.5161 +} 1.5162 + 1.5163 +// aCorner is a "full corner" value, i.e. NS_CORNER_TOP_LEFT etc 1.5164 +static bool IsCornerAdjacentToSide(uint8_t aCorner, css::Side aSide) 1.5165 +{ 1.5166 + PR_STATIC_ASSERT((int)NS_SIDE_TOP == NS_CORNER_TOP_LEFT); 1.5167 + PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == NS_CORNER_TOP_RIGHT); 1.5168 + PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == NS_CORNER_BOTTOM_RIGHT); 1.5169 + PR_STATIC_ASSERT((int)NS_SIDE_LEFT == NS_CORNER_BOTTOM_LEFT); 1.5170 + PR_STATIC_ASSERT((int)NS_SIDE_TOP == ((NS_CORNER_TOP_RIGHT - 1)&3)); 1.5171 + PR_STATIC_ASSERT((int)NS_SIDE_RIGHT == ((NS_CORNER_BOTTOM_RIGHT - 1)&3)); 1.5172 + PR_STATIC_ASSERT((int)NS_SIDE_BOTTOM == ((NS_CORNER_BOTTOM_LEFT - 1)&3)); 1.5173 + PR_STATIC_ASSERT((int)NS_SIDE_LEFT == ((NS_CORNER_TOP_LEFT - 1)&3)); 1.5174 + 1.5175 + return aSide == aCorner || aSide == ((aCorner - 1)&3); 1.5176 +} 1.5177 + 1.5178 +/* static */ bool 1.5179 +nsLayoutUtils::HasNonZeroCornerOnSide(const nsStyleCorners& aCorners, 1.5180 + css::Side aSide) 1.5181 +{ 1.5182 + PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_X/2 == NS_CORNER_TOP_LEFT); 1.5183 + PR_STATIC_ASSERT(NS_CORNER_TOP_LEFT_Y/2 == NS_CORNER_TOP_LEFT); 1.5184 + PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_X/2 == NS_CORNER_TOP_RIGHT); 1.5185 + PR_STATIC_ASSERT(NS_CORNER_TOP_RIGHT_Y/2 == NS_CORNER_TOP_RIGHT); 1.5186 + PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_X/2 == NS_CORNER_BOTTOM_RIGHT); 1.5187 + PR_STATIC_ASSERT(NS_CORNER_BOTTOM_RIGHT_Y/2 == NS_CORNER_BOTTOM_RIGHT); 1.5188 + PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_X/2 == NS_CORNER_BOTTOM_LEFT); 1.5189 + PR_STATIC_ASSERT(NS_CORNER_BOTTOM_LEFT_Y/2 == NS_CORNER_BOTTOM_LEFT); 1.5190 + 1.5191 + NS_FOR_CSS_HALF_CORNERS(corner) { 1.5192 + // corner is a "half corner" value, so dividing by two gives us a 1.5193 + // "full corner" value. 1.5194 + if (NonZeroStyleCoord(aCorners.Get(corner)) && 1.5195 + IsCornerAdjacentToSide(corner/2, aSide)) 1.5196 + return true; 1.5197 + } 1.5198 + return false; 1.5199 +} 1.5200 + 1.5201 +/* static */ nsTransparencyMode 1.5202 +nsLayoutUtils::GetFrameTransparency(nsIFrame* aBackgroundFrame, 1.5203 + nsIFrame* aCSSRootFrame) { 1.5204 + if (aCSSRootFrame->StyleDisplay()->mOpacity < 1.0f) 1.5205 + return eTransparencyTransparent; 1.5206 + 1.5207 + if (HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius)) 1.5208 + return eTransparencyTransparent; 1.5209 + 1.5210 + if (aCSSRootFrame->StyleDisplay()->mAppearance == NS_THEME_WIN_GLASS) 1.5211 + return eTransparencyGlass; 1.5212 + 1.5213 + if (aCSSRootFrame->StyleDisplay()->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS) 1.5214 + return eTransparencyBorderlessGlass; 1.5215 + 1.5216 + nsITheme::Transparency transparency; 1.5217 + if (aCSSRootFrame->IsThemed(&transparency)) 1.5218 + return transparency == nsITheme::eTransparent 1.5219 + ? eTransparencyTransparent 1.5220 + : eTransparencyOpaque; 1.5221 + 1.5222 + // We need an uninitialized window to be treated as opaque because 1.5223 + // doing otherwise breaks window display effects on some platforms, 1.5224 + // specifically Vista. (bug 450322) 1.5225 + if (aBackgroundFrame->GetType() == nsGkAtoms::viewportFrame && 1.5226 + !aBackgroundFrame->GetFirstPrincipalChild()) { 1.5227 + return eTransparencyOpaque; 1.5228 + } 1.5229 + 1.5230 + nsStyleContext* bgSC; 1.5231 + if (!nsCSSRendering::FindBackground(aBackgroundFrame, &bgSC)) { 1.5232 + return eTransparencyTransparent; 1.5233 + } 1.5234 + const nsStyleBackground* bg = bgSC->StyleBackground(); 1.5235 + if (NS_GET_A(bg->mBackgroundColor) < 255 || 1.5236 + // bottom layer's clip is used for the color 1.5237 + bg->BottomLayer().mClip != NS_STYLE_BG_CLIP_BORDER) 1.5238 + return eTransparencyTransparent; 1.5239 + return eTransparencyOpaque; 1.5240 +} 1.5241 + 1.5242 +static bool IsPopupFrame(nsIFrame* aFrame) 1.5243 +{ 1.5244 + // aFrame is a popup it's the list control frame dropdown for a combobox. 1.5245 + nsIAtom* frameType = aFrame->GetType(); 1.5246 + if (frameType == nsGkAtoms::listControlFrame) { 1.5247 + nsListControlFrame* lcf = static_cast<nsListControlFrame*>(aFrame); 1.5248 + return lcf->IsInDropDownMode(); 1.5249 + } 1.5250 + 1.5251 + // ... or if it's a XUL menupopup frame. 1.5252 + return frameType == nsGkAtoms::menuPopupFrame; 1.5253 +} 1.5254 + 1.5255 +/* static */ bool 1.5256 +nsLayoutUtils::IsPopup(nsIFrame* aFrame) 1.5257 +{ 1.5258 + // Optimization: the frame can't possibly be a popup if it has no view. 1.5259 + if (!aFrame->HasView()) { 1.5260 + NS_ASSERTION(!IsPopupFrame(aFrame), "popup frame must have a view"); 1.5261 + return false; 1.5262 + } 1.5263 + return IsPopupFrame(aFrame); 1.5264 +} 1.5265 + 1.5266 +/* static */ nsIFrame* 1.5267 +nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame) 1.5268 +{ 1.5269 + // We could use GetRootPresContext() here if the 1.5270 + // NS_FRAME_IN_POPUP frame bit is set. 1.5271 + nsIFrame* f = aFrame; 1.5272 + for (;;) { 1.5273 + if (!f->HasAnyStateBits(NS_FRAME_IN_POPUP)) { 1.5274 + f = f->PresContext()->FrameManager()->GetRootFrame(); 1.5275 + } else if (IsPopup(f)) { 1.5276 + return f; 1.5277 + } 1.5278 + nsIFrame* parent = GetCrossDocParentFrame(f); 1.5279 + if (!parent) 1.5280 + return f; 1.5281 + f = parent; 1.5282 + } 1.5283 +} 1.5284 + 1.5285 +/* static */ nsIFrame* 1.5286 +nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame) 1.5287 +{ 1.5288 + nsIFrame *f = aFrame; 1.5289 + for (;;) { 1.5290 + if (f->IsTransformed() || IsPopup(f)) { 1.5291 + return f; 1.5292 + } 1.5293 + nsIFrame* parent = GetCrossDocParentFrame(f); 1.5294 + if (!parent) { 1.5295 + return f; 1.5296 + } 1.5297 + f = parent; 1.5298 + } 1.5299 +} 1.5300 + 1.5301 +/* static */ nsIFrame* 1.5302 +nsLayoutUtils::GetTransformRootFrame(nsIFrame* aFrame) 1.5303 +{ 1.5304 + nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); 1.5305 + while (parent && parent->Preserves3DChildren()) { 1.5306 + parent = nsLayoutUtils::GetCrossDocParentFrame(parent); 1.5307 + } 1.5308 + return parent; 1.5309 +} 1.5310 + 1.5311 +/* static */ uint32_t 1.5312 +nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext, 1.5313 + const nsStyleFont* aStyleFont, 1.5314 + const nsStyleText* aStyleText, 1.5315 + nscoord aLetterSpacing) 1.5316 +{ 1.5317 + uint32_t result = 0; 1.5318 + if (aLetterSpacing != 0) { 1.5319 + result |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES; 1.5320 + } 1.5321 + if (aStyleText->mControlCharacterVisibility == NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN) { 1.5322 + result |= gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS; 1.5323 + } 1.5324 + switch (aStyleContext->StyleSVG()->mTextRendering) { 1.5325 + case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED: 1.5326 + result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED; 1.5327 + break; 1.5328 + case NS_STYLE_TEXT_RENDERING_AUTO: 1.5329 + if (aStyleFont->mFont.size < 1.5330 + aStyleContext->PresContext()->GetAutoQualityMinFontSize()) { 1.5331 + result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED; 1.5332 + } 1.5333 + break; 1.5334 + default: 1.5335 + break; 1.5336 + } 1.5337 + return result; 1.5338 +} 1.5339 + 1.5340 +/* static */ void 1.5341 +nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2, 1.5342 + nsRect* aHStrip, nsRect* aVStrip) { 1.5343 + NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(), 1.5344 + "expected rects at the same position"); 1.5345 + nsRect unionRect(aR1.x, aR1.y, std::max(aR1.width, aR2.width), 1.5346 + std::max(aR1.height, aR2.height)); 1.5347 + nscoord VStripStart = std::min(aR1.width, aR2.width); 1.5348 + nscoord HStripStart = std::min(aR1.height, aR2.height); 1.5349 + *aVStrip = unionRect; 1.5350 + aVStrip->x += VStripStart; 1.5351 + aVStrip->width -= VStripStart; 1.5352 + *aHStrip = unionRect; 1.5353 + aHStrip->y += HStripStart; 1.5354 + aHStrip->height -= HStripStart; 1.5355 +} 1.5356 + 1.5357 +nsDeviceContext* 1.5358 +nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindow* aWindow) 1.5359 +{ 1.5360 + if (!aWindow) { 1.5361 + return nullptr; 1.5362 + } 1.5363 + 1.5364 + nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell(); 1.5365 + while (docShell) { 1.5366 + // Now make sure our size is up to date. That will mean that the device 1.5367 + // context does the right thing on multi-monitor systems when we return it to 1.5368 + // the caller. It will also make sure that our prescontext has been created, 1.5369 + // if we're supposed to have one. 1.5370 + nsCOMPtr<nsPIDOMWindow> win = do_GetInterface(docShell); 1.5371 + if (!win) { 1.5372 + // No reason to go on 1.5373 + return nullptr; 1.5374 + } 1.5375 + 1.5376 + win->EnsureSizeUpToDate(); 1.5377 + 1.5378 + nsRefPtr<nsPresContext> presContext; 1.5379 + docShell->GetPresContext(getter_AddRefs(presContext)); 1.5380 + if (presContext) { 1.5381 + nsDeviceContext* context = presContext->DeviceContext(); 1.5382 + if (context) { 1.5383 + return context; 1.5384 + } 1.5385 + } 1.5386 + 1.5387 + nsCOMPtr<nsIDocShellTreeItem> parentItem; 1.5388 + docShell->GetParent(getter_AddRefs(parentItem)); 1.5389 + docShell = do_QueryInterface(parentItem); 1.5390 + } 1.5391 + 1.5392 + return nullptr; 1.5393 +} 1.5394 + 1.5395 +/* static */ bool 1.5396 +nsLayoutUtils::IsReallyFixedPos(nsIFrame* aFrame) 1.5397 +{ 1.5398 + NS_PRECONDITION(aFrame->GetParent(), 1.5399 + "IsReallyFixedPos called on frame not in tree"); 1.5400 + NS_PRECONDITION(aFrame->StyleDisplay()->mPosition == 1.5401 + NS_STYLE_POSITION_FIXED, 1.5402 + "IsReallyFixedPos called on non-'position:fixed' frame"); 1.5403 + 1.5404 + nsIAtom *parentType = aFrame->GetParent()->GetType(); 1.5405 + return parentType == nsGkAtoms::viewportFrame || 1.5406 + parentType == nsGkAtoms::pageContentFrame; 1.5407 +} 1.5408 + 1.5409 +nsLayoutUtils::SurfaceFromElementResult 1.5410 +nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement, 1.5411 + uint32_t aSurfaceFlags, 1.5412 + DrawTarget* aTarget) 1.5413 +{ 1.5414 + SurfaceFromElementResult result; 1.5415 + nsresult rv; 1.5416 + 1.5417 + nsCOMPtr<imgIRequest> imgRequest; 1.5418 + rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.5419 + getter_AddRefs(imgRequest)); 1.5420 + if (NS_FAILED(rv) || !imgRequest) 1.5421 + return result; 1.5422 + 1.5423 + uint32_t status; 1.5424 + imgRequest->GetImageStatus(&status); 1.5425 + if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) { 1.5426 + // Spec says to use GetComplete, but that only works on 1.5427 + // nsIDOMHTMLImageElement, and we support all sorts of other stuff 1.5428 + // here. Do this for now pending spec clarification. 1.5429 + result.mIsStillLoading = (status & imgIRequest::STATUS_ERROR) == 0; 1.5430 + return result; 1.5431 + } 1.5432 + 1.5433 + nsCOMPtr<nsIPrincipal> principal; 1.5434 + rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal)); 1.5435 + if (NS_FAILED(rv)) 1.5436 + return result; 1.5437 + 1.5438 + nsCOMPtr<imgIContainer> imgContainer; 1.5439 + rv = imgRequest->GetImage(getter_AddRefs(imgContainer)); 1.5440 + if (NS_FAILED(rv)) 1.5441 + return result; 1.5442 + 1.5443 + uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS; 1.5444 + 1.5445 + uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME) 1.5446 + ? (uint32_t) imgIContainer::FRAME_FIRST 1.5447 + : (uint32_t) imgIContainer::FRAME_CURRENT; 1.5448 + uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE; 1.5449 + if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION) 1.5450 + frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION; 1.5451 + if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { 1.5452 + frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; 1.5453 + result.mIsPremultiplied = false; 1.5454 + } 1.5455 + 1.5456 + int32_t imgWidth, imgHeight; 1.5457 + rv = imgContainer->GetWidth(&imgWidth); 1.5458 + nsresult rv2 = imgContainer->GetHeight(&imgHeight); 1.5459 + if (NS_FAILED(rv) || NS_FAILED(rv2)) 1.5460 + return result; 1.5461 + 1.5462 + if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) { 1.5463 + if (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) { 1.5464 + frameFlags |= imgIContainer::FLAG_WANT_DATA_SURFACE; 1.5465 + } 1.5466 + result.mSourceSurface = imgContainer->GetFrame(whichFrame, frameFlags); 1.5467 + if (!result.mSourceSurface) { 1.5468 + return result; 1.5469 + } 1.5470 + // The surface we return is likely to be cached. We don't want to have to 1.5471 + // convert to a surface that's compatible with aTarget each time it's used 1.5472 + // (that would result in terrible performance), so we convert once here 1.5473 + // upfront if aTarget is specified. 1.5474 + if (aTarget) { 1.5475 + RefPtr<SourceSurface> optSurface = 1.5476 + aTarget->OptimizeSourceSurface(result.mSourceSurface); 1.5477 + if (optSurface) { 1.5478 + result.mSourceSurface = optSurface; 1.5479 + } 1.5480 + } 1.5481 + } else { 1.5482 + result.mDrawInfo.mImgContainer = imgContainer; 1.5483 + result.mDrawInfo.mWhichFrame = whichFrame; 1.5484 + result.mDrawInfo.mDrawingFlags = frameFlags; 1.5485 + } 1.5486 + 1.5487 + int32_t corsmode; 1.5488 + if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) { 1.5489 + result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE); 1.5490 + } 1.5491 + 1.5492 + result.mSize = gfxIntSize(imgWidth, imgHeight); 1.5493 + result.mPrincipal = principal.forget(); 1.5494 + // no images, including SVG images, can load content from another domain. 1.5495 + result.mIsWriteOnly = false; 1.5496 + result.mImageRequest = imgRequest.forget(); 1.5497 + 1.5498 + return result; 1.5499 +} 1.5500 + 1.5501 +nsLayoutUtils::SurfaceFromElementResult 1.5502 +nsLayoutUtils::SurfaceFromElement(HTMLImageElement *aElement, 1.5503 + uint32_t aSurfaceFlags, 1.5504 + DrawTarget* aTarget) 1.5505 +{ 1.5506 + return SurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement), 1.5507 + aSurfaceFlags, aTarget); 1.5508 +} 1.5509 + 1.5510 +nsLayoutUtils::SurfaceFromElementResult 1.5511 +nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement, 1.5512 + uint32_t aSurfaceFlags, 1.5513 + DrawTarget* aTarget) 1.5514 +{ 1.5515 + SurfaceFromElementResult result; 1.5516 + 1.5517 + bool* isPremultiplied = nullptr; 1.5518 + if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { 1.5519 + isPremultiplied = &result.mIsPremultiplied; 1.5520 + } 1.5521 + 1.5522 + gfxIntSize size = aElement->GetSize(); 1.5523 + 1.5524 + result.mSourceSurface = aElement->GetSurfaceSnapshot(isPremultiplied); 1.5525 + if (!result.mSourceSurface) { 1.5526 + // 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 1.5527 + // draw nothing, so return an empty surface. 1.5528 + DrawTarget *ref = aTarget ? aTarget : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); 1.5529 + RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height), 1.5530 + SurfaceFormat::B8G8R8A8); 1.5531 + if (dt) { 1.5532 + result.mSourceSurface = dt->Snapshot(); 1.5533 + } 1.5534 + } else if (aTarget) { 1.5535 + RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); 1.5536 + if (opt) { 1.5537 + result.mSourceSurface = opt; 1.5538 + } 1.5539 + } 1.5540 + 1.5541 + // Ensure that any future changes to the canvas trigger proper invalidation, 1.5542 + // in case this is being used by -moz-element() 1.5543 + aElement->MarkContextClean(); 1.5544 + 1.5545 + result.mSize = size; 1.5546 + result.mPrincipal = aElement->NodePrincipal(); 1.5547 + result.mIsWriteOnly = aElement->IsWriteOnly(); 1.5548 + 1.5549 + return result; 1.5550 +} 1.5551 + 1.5552 +nsLayoutUtils::SurfaceFromElementResult 1.5553 +nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement, 1.5554 + uint32_t aSurfaceFlags, 1.5555 + DrawTarget* aTarget) 1.5556 +{ 1.5557 + SurfaceFromElementResult result; 1.5558 + 1.5559 + NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!"); 1.5560 + 1.5561 + uint16_t readyState; 1.5562 + if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) && 1.5563 + (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING || 1.5564 + readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) { 1.5565 + result.mIsStillLoading = true; 1.5566 + return result; 1.5567 + } 1.5568 + 1.5569 + // If it doesn't have a principal, just bail 1.5570 + nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentPrincipal(); 1.5571 + if (!principal) 1.5572 + return result; 1.5573 + 1.5574 + ImageContainer *container = aElement->GetImageContainer(); 1.5575 + if (!container) 1.5576 + return result; 1.5577 + 1.5578 + mozilla::gfx::IntSize size; 1.5579 + result.mSourceSurface = container->GetCurrentAsSourceSurface(&size); 1.5580 + if (!result.mSourceSurface) 1.5581 + return result; 1.5582 + 1.5583 + if (aTarget) { 1.5584 + RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); 1.5585 + if (opt) { 1.5586 + result.mSourceSurface = opt; 1.5587 + } 1.5588 + } 1.5589 + 1.5590 + result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE; 1.5591 + result.mSize = ThebesIntSize(size); 1.5592 + result.mPrincipal = principal.forget(); 1.5593 + result.mIsWriteOnly = false; 1.5594 + 1.5595 + return result; 1.5596 +} 1.5597 + 1.5598 +nsLayoutUtils::SurfaceFromElementResult 1.5599 +nsLayoutUtils::SurfaceFromElement(dom::Element* aElement, 1.5600 + uint32_t aSurfaceFlags, 1.5601 + DrawTarget* aTarget) 1.5602 +{ 1.5603 + // If it's a <canvas>, we may be able to just grab its internal surface 1.5604 + if (HTMLCanvasElement* canvas = 1.5605 + HTMLCanvasElement::FromContentOrNull(aElement)) { 1.5606 + return SurfaceFromElement(canvas, aSurfaceFlags, aTarget); 1.5607 + } 1.5608 + 1.5609 + // Maybe it's <video>? 1.5610 + if (HTMLVideoElement* video = 1.5611 + HTMLVideoElement::FromContentOrNull(aElement)) { 1.5612 + return SurfaceFromElement(video, aSurfaceFlags, aTarget); 1.5613 + } 1.5614 + 1.5615 + // Finally, check if it's a normal image 1.5616 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement); 1.5617 + 1.5618 + if (!imageLoader) { 1.5619 + return SurfaceFromElementResult(); 1.5620 + } 1.5621 + 1.5622 + return SurfaceFromElement(imageLoader, aSurfaceFlags, aTarget); 1.5623 +} 1.5624 + 1.5625 +/* static */ 1.5626 +nsIContent* 1.5627 +nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument* aDocument) 1.5628 +{ 1.5629 + // If the document is in designMode we should return nullptr. 1.5630 + if (!aDocument || aDocument->HasFlag(NODE_IS_EDITABLE)) { 1.5631 + return nullptr; 1.5632 + } 1.5633 + 1.5634 + // contenteditable only works with HTML document. 1.5635 + // Note: Use nsIDOMHTMLDocument rather than nsIHTMLDocument for getting the 1.5636 + // body node because nsIDOMHTMLDocument::GetBody() does something 1.5637 + // additional work for some cases and nsEditor uses them. 1.5638 + nsCOMPtr<nsIDOMHTMLDocument> domHTMLDoc = do_QueryInterface(aDocument); 1.5639 + if (!domHTMLDoc) { 1.5640 + return nullptr; 1.5641 + } 1.5642 + 1.5643 + Element* rootElement = aDocument->GetRootElement(); 1.5644 + if (rootElement && rootElement->IsEditable()) { 1.5645 + return rootElement; 1.5646 + } 1.5647 + 1.5648 + // If there are no editable root element, check its <body> element. 1.5649 + // Note that the body element could be <frameset> element. 1.5650 + nsCOMPtr<nsIDOMHTMLElement> body; 1.5651 + nsresult rv = domHTMLDoc->GetBody(getter_AddRefs(body)); 1.5652 + nsCOMPtr<nsIContent> content = do_QueryInterface(body); 1.5653 + if (NS_SUCCEEDED(rv) && content && content->IsEditable()) { 1.5654 + return content; 1.5655 + } 1.5656 + return nullptr; 1.5657 +} 1.5658 + 1.5659 +#ifdef DEBUG 1.5660 +/* static */ void 1.5661 +nsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame* aContainer, 1.5662 + const nsFrameList& aFrameList) 1.5663 +{ 1.5664 + for (nsIFrame* f = aFrameList.FirstChild(); f ; f = f->GetNextSibling()) { 1.5665 + // Check only later continuations of f; we deal with checking the 1.5666 + // earlier continuations when we hit those earlier continuations in 1.5667 + // the frame list. 1.5668 + for (nsIFrame *c = f; (c = c->GetNextInFlow());) { 1.5669 + NS_ASSERTION(c->GetParent() != aContainer || 1.5670 + !aFrameList.ContainsFrame(c), 1.5671 + "Two continuations of the same frame in the same " 1.5672 + "frame list"); 1.5673 + } 1.5674 + } 1.5675 +} 1.5676 + 1.5677 +// Is one of aFrame's ancestors a letter frame? 1.5678 +static bool 1.5679 +IsInLetterFrame(nsIFrame *aFrame) 1.5680 +{ 1.5681 + for (nsIFrame *f = aFrame->GetParent(); f; f = f->GetParent()) { 1.5682 + if (f->GetType() == nsGkAtoms::letterFrame) { 1.5683 + return true; 1.5684 + } 1.5685 + } 1.5686 + return false; 1.5687 +} 1.5688 + 1.5689 +/* static */ void 1.5690 +nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame *aSubtreeRoot) 1.5691 +{ 1.5692 + NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(), 1.5693 + "frame tree not empty, but caller reported complete status"); 1.5694 + 1.5695 + // Also assert that text frames map no text. 1.5696 + int32_t start, end; 1.5697 + nsresult rv = aSubtreeRoot->GetOffsets(start, end); 1.5698 + NS_ASSERTION(NS_SUCCEEDED(rv), "GetOffsets failed"); 1.5699 + // In some cases involving :first-letter, we'll partially unlink a 1.5700 + // continuation in the middle of a continuation chain from its 1.5701 + // previous and next continuations before destroying it, presumably so 1.5702 + // that we don't also destroy the later continuations. Once we've 1.5703 + // done this, GetOffsets returns incorrect values. 1.5704 + // For examples, see list of tests in 1.5705 + // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29 1.5706 + NS_ASSERTION(start == end || IsInLetterFrame(aSubtreeRoot), 1.5707 + "frame tree not empty, but caller reported complete status"); 1.5708 + 1.5709 + nsIFrame::ChildListIterator lists(aSubtreeRoot); 1.5710 + for (; !lists.IsDone(); lists.Next()) { 1.5711 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.5712 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.5713 + nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames.get()); 1.5714 + } 1.5715 + } 1.5716 +} 1.5717 +#endif 1.5718 + 1.5719 +static void 1.5720 +GetFontFacesForFramesInner(nsIFrame* aFrame, nsFontFaceList* aFontFaceList) 1.5721 +{ 1.5722 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.5723 + 1.5724 + if (aFrame->GetType() == nsGkAtoms::textFrame) { 1.5725 + if (!aFrame->GetPrevContinuation()) { 1.5726 + nsLayoutUtils::GetFontFacesForText(aFrame, 0, INT32_MAX, true, 1.5727 + aFontFaceList); 1.5728 + } 1.5729 + return; 1.5730 + } 1.5731 + 1.5732 + nsIFrame::ChildListID childLists[] = { nsIFrame::kPrincipalList, 1.5733 + nsIFrame::kPopupList }; 1.5734 + for (size_t i = 0; i < ArrayLength(childLists); ++i) { 1.5735 + nsFrameList children(aFrame->GetChildList(childLists[i])); 1.5736 + for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { 1.5737 + nsIFrame* child = e.get(); 1.5738 + child = nsPlaceholderFrame::GetRealFrameFor(child); 1.5739 + GetFontFacesForFramesInner(child, aFontFaceList); 1.5740 + } 1.5741 + } 1.5742 +} 1.5743 + 1.5744 +/* static */ 1.5745 +nsresult 1.5746 +nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame, 1.5747 + nsFontFaceList* aFontFaceList) 1.5748 +{ 1.5749 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.5750 + 1.5751 + while (aFrame) { 1.5752 + GetFontFacesForFramesInner(aFrame, aFontFaceList); 1.5753 + aFrame = GetNextContinuationOrIBSplitSibling(aFrame); 1.5754 + } 1.5755 + 1.5756 + return NS_OK; 1.5757 +} 1.5758 + 1.5759 +/* static */ 1.5760 +nsresult 1.5761 +nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame, 1.5762 + int32_t aStartOffset, int32_t aEndOffset, 1.5763 + bool aFollowContinuations, 1.5764 + nsFontFaceList* aFontFaceList) 1.5765 +{ 1.5766 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.5767 + 1.5768 + if (aFrame->GetType() != nsGkAtoms::textFrame) { 1.5769 + return NS_OK; 1.5770 + } 1.5771 + 1.5772 + nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame); 1.5773 + do { 1.5774 + int32_t fstart = std::max(curr->GetContentOffset(), aStartOffset); 1.5775 + int32_t fend = std::min(curr->GetContentEnd(), aEndOffset); 1.5776 + if (fstart >= fend) { 1.5777 + curr = static_cast<nsTextFrame*>(curr->GetNextContinuation()); 1.5778 + continue; 1.5779 + } 1.5780 + 1.5781 + // curr is overlapping with the offset we want 1.5782 + gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated); 1.5783 + gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated); 1.5784 + NS_ENSURE_TRUE(textRun, NS_ERROR_OUT_OF_MEMORY); 1.5785 + 1.5786 + // include continuations in the range that share the same textrun 1.5787 + nsTextFrame* next = nullptr; 1.5788 + if (aFollowContinuations && fend < aEndOffset) { 1.5789 + next = static_cast<nsTextFrame*>(curr->GetNextContinuation()); 1.5790 + while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) { 1.5791 + fend = std::min(next->GetContentEnd(), aEndOffset); 1.5792 + next = fend < aEndOffset ? 1.5793 + static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr; 1.5794 + } 1.5795 + } 1.5796 + 1.5797 + uint32_t skipStart = iter.ConvertOriginalToSkipped(fstart); 1.5798 + uint32_t skipEnd = iter.ConvertOriginalToSkipped(fend); 1.5799 + aFontFaceList->AddFontsFromTextRun(textRun, skipStart, skipEnd - skipStart); 1.5800 + curr = next; 1.5801 + } while (aFollowContinuations && curr); 1.5802 + 1.5803 + return NS_OK; 1.5804 +} 1.5805 + 1.5806 +/* static */ 1.5807 +size_t 1.5808 +nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame, 1.5809 + MallocSizeOf aMallocSizeOf, 1.5810 + bool clear) 1.5811 +{ 1.5812 + NS_PRECONDITION(aFrame, "NULL frame pointer"); 1.5813 + 1.5814 + size_t total = 0; 1.5815 + 1.5816 + if (aFrame->GetType() == nsGkAtoms::textFrame) { 1.5817 + nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame); 1.5818 + for (uint32_t i = 0; i < 2; ++i) { 1.5819 + gfxTextRun *run = textFrame->GetTextRun( 1.5820 + (i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated); 1.5821 + if (run) { 1.5822 + if (clear) { 1.5823 + run->ResetSizeOfAccountingFlags(); 1.5824 + } else { 1.5825 + total += run->MaybeSizeOfIncludingThis(aMallocSizeOf); 1.5826 + } 1.5827 + } 1.5828 + } 1.5829 + return total; 1.5830 + } 1.5831 + 1.5832 + nsAutoTArray<nsIFrame::ChildList,4> childListArray; 1.5833 + aFrame->GetChildLists(&childListArray); 1.5834 + 1.5835 + for (nsIFrame::ChildListArrayIterator childLists(childListArray); 1.5836 + !childLists.IsDone(); childLists.Next()) { 1.5837 + for (nsFrameList::Enumerator e(childLists.CurrentList()); 1.5838 + !e.AtEnd(); e.Next()) { 1.5839 + total += SizeOfTextRunsForFrames(e.get(), aMallocSizeOf, clear); 1.5840 + } 1.5841 + } 1.5842 + return total; 1.5843 +} 1.5844 + 1.5845 +/* static */ 1.5846 +void 1.5847 +nsLayoutUtils::Initialize() 1.5848 +{ 1.5849 + Preferences::AddUintVarCache(&sFontSizeInflationMaxRatio, 1.5850 + "font.size.inflation.maxRatio"); 1.5851 + Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine, 1.5852 + "font.size.inflation.emPerLine"); 1.5853 + Preferences::AddUintVarCache(&sFontSizeInflationMinTwips, 1.5854 + "font.size.inflation.minTwips"); 1.5855 + Preferences::AddUintVarCache(&sFontSizeInflationLineThreshold, 1.5856 + "font.size.inflation.lineThreshold"); 1.5857 + Preferences::AddIntVarCache(&sFontSizeInflationMappingIntercept, 1.5858 + "font.size.inflation.mappingIntercept"); 1.5859 + Preferences::AddBoolVarCache(&sFontSizeInflationForceEnabled, 1.5860 + "font.size.inflation.forceEnabled"); 1.5861 + Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess, 1.5862 + "font.size.inflation.disabledInMasterProcess"); 1.5863 + Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled, 1.5864 + "nglayout.debug.invalidation"); 1.5865 + Preferences::AddBoolVarCache(&sCSSVariablesEnabled, 1.5866 + "layout.css.variables.enabled"); 1.5867 + Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled, 1.5868 + "layout.interruptible-reflow.enabled"); 1.5869 + 1.5870 + Preferences::RegisterCallback(GridEnabledPrefChangeCallback, 1.5871 + GRID_ENABLED_PREF_NAME); 1.5872 + GridEnabledPrefChangeCallback(GRID_ENABLED_PREF_NAME, nullptr); 1.5873 + Preferences::RegisterCallback(StickyEnabledPrefChangeCallback, 1.5874 + STICKY_ENABLED_PREF_NAME); 1.5875 + StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME, nullptr); 1.5876 + Preferences::RegisterCallback(TextAlignTrueEnabledPrefChangeCallback, 1.5877 + TEXT_ALIGN_TRUE_ENABLED_PREF_NAME); 1.5878 + TextAlignTrueEnabledPrefChangeCallback(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, 1.5879 + nullptr); 1.5880 + 1.5881 + nsComputedDOMStyle::RegisterPrefChangeCallbacks(); 1.5882 +} 1.5883 + 1.5884 +/* static */ 1.5885 +void 1.5886 +nsLayoutUtils::Shutdown() 1.5887 +{ 1.5888 + if (sContentMap) { 1.5889 + delete sContentMap; 1.5890 + sContentMap = nullptr; 1.5891 + } 1.5892 + 1.5893 + Preferences::UnregisterCallback(GridEnabledPrefChangeCallback, 1.5894 + GRID_ENABLED_PREF_NAME); 1.5895 + Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback, 1.5896 + STICKY_ENABLED_PREF_NAME); 1.5897 + 1.5898 + nsComputedDOMStyle::UnregisterPrefChangeCallbacks(); 1.5899 +} 1.5900 + 1.5901 +/* static */ 1.5902 +void 1.5903 +nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext, 1.5904 + imgIRequest* aRequest, 1.5905 + bool* aRequestRegistered) 1.5906 +{ 1.5907 + if (!aPresContext) { 1.5908 + return; 1.5909 + } 1.5910 + 1.5911 + if (aRequestRegistered && *aRequestRegistered) { 1.5912 + // Our request is already registered with the refresh driver, so 1.5913 + // no need to register it again. 1.5914 + return; 1.5915 + } 1.5916 + 1.5917 + if (aRequest) { 1.5918 + if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) { 1.5919 + NS_WARNING("Unable to add image request"); 1.5920 + return; 1.5921 + } 1.5922 + 1.5923 + if (aRequestRegistered) { 1.5924 + *aRequestRegistered = true; 1.5925 + } 1.5926 + } 1.5927 +} 1.5928 + 1.5929 +/* static */ 1.5930 +void 1.5931 +nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext, 1.5932 + imgIRequest* aRequest, 1.5933 + bool* aRequestRegistered) 1.5934 +{ 1.5935 + if (!aPresContext) { 1.5936 + return; 1.5937 + } 1.5938 + 1.5939 + if (aRequestRegistered && *aRequestRegistered) { 1.5940 + // Our request is already registered with the refresh driver, so 1.5941 + // no need to register it again. 1.5942 + return; 1.5943 + } 1.5944 + 1.5945 + if (aRequest) { 1.5946 + nsCOMPtr<imgIContainer> image; 1.5947 + if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) { 1.5948 + // Check to verify that the image is animated. If so, then add it to the 1.5949 + // list of images tracked by the refresh driver. 1.5950 + bool isAnimated = false; 1.5951 + nsresult rv = image->GetAnimated(&isAnimated); 1.5952 + if (NS_SUCCEEDED(rv) && isAnimated) { 1.5953 + if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) { 1.5954 + NS_WARNING("Unable to add image request"); 1.5955 + return; 1.5956 + } 1.5957 + 1.5958 + if (aRequestRegistered) { 1.5959 + *aRequestRegistered = true; 1.5960 + } 1.5961 + } 1.5962 + } 1.5963 + } 1.5964 +} 1.5965 + 1.5966 +/* static */ 1.5967 +void 1.5968 +nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext, 1.5969 + imgIRequest* aRequest, 1.5970 + bool* aRequestRegistered) 1.5971 +{ 1.5972 + if (!aPresContext) { 1.5973 + return; 1.5974 + } 1.5975 + 1.5976 + // Deregister our imgIRequest with the refresh driver to 1.5977 + // complete tear-down, but only if it has been registered 1.5978 + if (aRequestRegistered && !*aRequestRegistered) { 1.5979 + return; 1.5980 + } 1.5981 + 1.5982 + if (aRequest) { 1.5983 + nsCOMPtr<imgIContainer> image; 1.5984 + if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) { 1.5985 + aPresContext->RefreshDriver()->RemoveImageRequest(aRequest); 1.5986 + 1.5987 + if (aRequestRegistered) { 1.5988 + *aRequestRegistered = false; 1.5989 + } 1.5990 + } 1.5991 + } 1.5992 +} 1.5993 + 1.5994 +/* static */ 1.5995 +void 1.5996 +nsLayoutUtils::PostRestyleEvent(Element* aElement, 1.5997 + nsRestyleHint aRestyleHint, 1.5998 + nsChangeHint aMinChangeHint) 1.5999 +{ 1.6000 + nsIDocument* doc = aElement->GetCurrentDoc(); 1.6001 + if (doc) { 1.6002 + nsCOMPtr<nsIPresShell> presShell = doc->GetShell(); 1.6003 + if (presShell) { 1.6004 + presShell->GetPresContext()->RestyleManager()->PostRestyleEvent( 1.6005 + aElement, aRestyleHint, aMinChangeHint); 1.6006 + } 1.6007 + } 1.6008 +} 1.6009 + 1.6010 +nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName, 1.6011 + const nsAString& aValue) 1.6012 + : mContent(aContent), 1.6013 + mAttrName(aAttrName), 1.6014 + mValue(aValue) 1.6015 +{ 1.6016 + NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash"); 1.6017 +} 1.6018 + 1.6019 +nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName, 1.6020 + int32_t aValue) 1.6021 + : mContent(aContent), 1.6022 + mAttrName(aAttrName) 1.6023 +{ 1.6024 + NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash"); 1.6025 + mValue.AppendInt(aValue); 1.6026 +} 1.6027 + 1.6028 +NS_IMETHODIMP 1.6029 +nsSetAttrRunnable::Run() 1.6030 +{ 1.6031 + return mContent->SetAttr(kNameSpaceID_None, mAttrName, mValue, true); 1.6032 +} 1.6033 + 1.6034 +nsUnsetAttrRunnable::nsUnsetAttrRunnable(nsIContent* aContent, 1.6035 + nsIAtom* aAttrName) 1.6036 + : mContent(aContent), 1.6037 + mAttrName(aAttrName) 1.6038 +{ 1.6039 + NS_ASSERTION(aContent && aAttrName, "Missing stuff, prepare to crash"); 1.6040 +} 1.6041 + 1.6042 +NS_IMETHODIMP 1.6043 +nsUnsetAttrRunnable::Run() 1.6044 +{ 1.6045 + return mContent->UnsetAttr(kNameSpaceID_None, mAttrName, true); 1.6046 +} 1.6047 + 1.6048 +/** 1.6049 + * Compute the minimum font size inside of a container with the given 1.6050 + * width, such that **when the user zooms the container to fill the full 1.6051 + * width of the device**, the fonts satisfy our minima. 1.6052 + */ 1.6053 +static nscoord 1.6054 +MinimumFontSizeFor(nsPresContext* aPresContext, nscoord aContainerWidth) 1.6055 +{ 1.6056 + nsIPresShell* presShell = aPresContext->PresShell(); 1.6057 + 1.6058 + uint32_t emPerLine = presShell->FontSizeInflationEmPerLine(); 1.6059 + uint32_t minTwips = presShell->FontSizeInflationMinTwips(); 1.6060 + if (emPerLine == 0 && minTwips == 0) { 1.6061 + return 0; 1.6062 + } 1.6063 + 1.6064 + // Clamp the container width to the device dimensions 1.6065 + nscoord iFrameWidth = aPresContext->GetVisibleArea().width; 1.6066 + nscoord effectiveContainerWidth = std::min(iFrameWidth, aContainerWidth); 1.6067 + 1.6068 + nscoord byLine = 0, byInch = 0; 1.6069 + if (emPerLine != 0) { 1.6070 + byLine = effectiveContainerWidth / emPerLine; 1.6071 + } 1.6072 + if (minTwips != 0) { 1.6073 + // REVIEW: Is this giving us app units and sizes *not* counting 1.6074 + // viewport scaling? 1.6075 + float deviceWidthInches = 1.6076 + aPresContext->ScreenWidthInchesForFontInflation(); 1.6077 + byInch = NSToCoordRound(effectiveContainerWidth / 1.6078 + (deviceWidthInches * 1440 / 1.6079 + minTwips )); 1.6080 + } 1.6081 + return std::max(byLine, byInch); 1.6082 +} 1.6083 + 1.6084 +/* static */ float 1.6085 +nsLayoutUtils::FontSizeInflationInner(const nsIFrame *aFrame, 1.6086 + nscoord aMinFontSize) 1.6087 +{ 1.6088 + // Note that line heights should be inflated by the same ratio as the 1.6089 + // font size of the same text; thus we operate only on the font size 1.6090 + // even when we're scaling a line height. 1.6091 + nscoord styleFontSize = aFrame->StyleFont()->mFont.size; 1.6092 + if (styleFontSize <= 0) { 1.6093 + // Never scale zero font size. 1.6094 + return 1.0; 1.6095 + } 1.6096 + 1.6097 + if (aMinFontSize <= 0) { 1.6098 + // No need to scale. 1.6099 + return 1.0; 1.6100 + } 1.6101 + 1.6102 + // If between this current frame and its font inflation container there is a 1.6103 + // non-inline element with fixed width or height, then we should not inflate 1.6104 + // fonts for this frame. 1.6105 + for (const nsIFrame* f = aFrame; 1.6106 + f && !f->IsContainerForFontSizeInflation(); 1.6107 + f = f->GetParent()) { 1.6108 + nsIContent* content = f->GetContent(); 1.6109 + nsIAtom* fType = f->GetType(); 1.6110 + // Also, if there is more than one frame corresponding to a single 1.6111 + // content node, we want the outermost one. 1.6112 + if (!(f->GetParent() && f->GetParent()->GetContent() == content) && 1.6113 + // ignore width/height on inlines since they don't apply 1.6114 + fType != nsGkAtoms::inlineFrame && 1.6115 + // ignore width on radios and checkboxes since we enlarge them and 1.6116 + // they have width/height in ua.css 1.6117 + fType != nsGkAtoms::formControlFrame) { 1.6118 + nsStyleCoord stylePosWidth = f->StylePosition()->mWidth; 1.6119 + nsStyleCoord stylePosHeight = f->StylePosition()->mHeight; 1.6120 + if (stylePosWidth.GetUnit() != eStyleUnit_Auto || 1.6121 + stylePosHeight.GetUnit() != eStyleUnit_Auto) { 1.6122 + 1.6123 + return 1.0; 1.6124 + } 1.6125 + } 1.6126 + } 1.6127 + 1.6128 + int32_t interceptParam = nsLayoutUtils::FontSizeInflationMappingIntercept(); 1.6129 + float maxRatio = (float)nsLayoutUtils::FontSizeInflationMaxRatio() / 100.0f; 1.6130 + 1.6131 + float ratio = float(styleFontSize) / float(aMinFontSize); 1.6132 + float inflationRatio; 1.6133 + 1.6134 + // Given a minimum inflated font size m, a specified font size s, we want to 1.6135 + // find the inflated font size i and then return the ratio of i to s (i/s). 1.6136 + if (interceptParam >= 0) { 1.6137 + // Since the mapping intercept parameter P is greater than zero, we use it 1.6138 + // to determine the point where our mapping function intersects the i=s 1.6139 + // line. This means that we have an equation of the form: 1.6140 + // 1.6141 + // i = m + s·(P/2)/(1 + P/2), if s <= (1 + P/2)·m 1.6142 + // i = s, if s >= (1 + P/2)·m 1.6143 + 1.6144 + float intercept = 1 + float(interceptParam)/2.0f; 1.6145 + if (ratio >= intercept) { 1.6146 + // If we're already at 1+P/2 or more times the minimum, don't scale. 1.6147 + return 1.0; 1.6148 + } 1.6149 + 1.6150 + // The point (intercept, intercept) is where the part of the i vs. s graph 1.6151 + // that's not slope 1 meets the i=s line. (This part of the 1.6152 + // graph is a line from (0, m), to that point). We calculate the 1.6153 + // intersection point to be ((1+P/2)m, (1+P/2)m), where P is the 1.6154 + // intercept parameter above. We then need to return i/s. 1.6155 + inflationRatio = (1.0f + (ratio * (intercept - 1) / intercept)) / ratio; 1.6156 + } else { 1.6157 + // This is the case where P is negative. We essentially want to implement 1.6158 + // the case for P=infinity here, so we make i = s + m, which means that 1.6159 + // i/s = s/s + m/s = 1 + 1/ratio 1.6160 + inflationRatio = 1 + 1.0f / ratio; 1.6161 + } 1.6162 + 1.6163 + if (maxRatio > 1.0 && inflationRatio > maxRatio) { 1.6164 + return maxRatio; 1.6165 + } else { 1.6166 + return inflationRatio; 1.6167 + } 1.6168 +} 1.6169 + 1.6170 +static bool 1.6171 +ShouldInflateFontsForContainer(const nsIFrame *aFrame) 1.6172 +{ 1.6173 + // We only want to inflate fonts for text that is in a place 1.6174 + // with room to expand. The question is what the best heuristic for 1.6175 + // that is... 1.6176 + // For now, we're going to use NS_FRAME_IN_CONSTRAINED_HEIGHT, which 1.6177 + // indicates whether the frame is inside something with a constrained 1.6178 + // height (propagating down the tree), but the propagation stops when 1.6179 + // we hit overflow-y: scroll or auto. 1.6180 + const nsStyleText* styleText = aFrame->StyleText(); 1.6181 + 1.6182 + return styleText->mTextSizeAdjust != NS_STYLE_TEXT_SIZE_ADJUST_NONE && 1.6183 + !(aFrame->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) && 1.6184 + // We also want to disable font inflation for containers that have 1.6185 + // preformatted text. 1.6186 + styleText->WhiteSpaceCanWrap(aFrame); 1.6187 +} 1.6188 + 1.6189 +nscoord 1.6190 +nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame *aFrame) 1.6191 +{ 1.6192 + nsPresContext *presContext = aFrame->PresContext(); 1.6193 + if (!FontSizeInflationEnabled(presContext) || 1.6194 + presContext->mInflationDisabledForShrinkWrap) { 1.6195 + return 0; 1.6196 + } 1.6197 + 1.6198 + for (const nsIFrame *f = aFrame; f; f = f->GetParent()) { 1.6199 + if (f->IsContainerForFontSizeInflation()) { 1.6200 + if (!ShouldInflateFontsForContainer(f)) { 1.6201 + return 0; 1.6202 + } 1.6203 + 1.6204 + nsFontInflationData *data = 1.6205 + nsFontInflationData::FindFontInflationDataFor(aFrame); 1.6206 + // FIXME: The need to null-check here is sort of a bug, and might 1.6207 + // lead to incorrect results. 1.6208 + if (!data || !data->InflationEnabled()) { 1.6209 + return 0; 1.6210 + } 1.6211 + 1.6212 + return MinimumFontSizeFor(aFrame->PresContext(), 1.6213 + data->EffectiveWidth()); 1.6214 + } 1.6215 + } 1.6216 + 1.6217 + NS_ABORT_IF_FALSE(false, "root should always be container"); 1.6218 + 1.6219 + return 0; 1.6220 +} 1.6221 + 1.6222 +float 1.6223 +nsLayoutUtils::FontSizeInflationFor(const nsIFrame *aFrame) 1.6224 +{ 1.6225 + if (aFrame->IsSVGText()) { 1.6226 + const nsIFrame* container = aFrame; 1.6227 + while (container->GetType() != nsGkAtoms::svgTextFrame) { 1.6228 + container = container->GetParent(); 1.6229 + } 1.6230 + NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame"); 1.6231 + return 1.6232 + static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor(); 1.6233 + } 1.6234 + 1.6235 + if (!FontSizeInflationEnabled(aFrame->PresContext())) { 1.6236 + return 1.0f; 1.6237 + } 1.6238 + 1.6239 + return FontSizeInflationInner(aFrame, InflationMinFontSizeFor(aFrame)); 1.6240 +} 1.6241 + 1.6242 +/* static */ bool 1.6243 +nsLayoutUtils::FontSizeInflationEnabled(nsPresContext *aPresContext) 1.6244 +{ 1.6245 + nsIPresShell* presShell = aPresContext->GetPresShell(); 1.6246 + 1.6247 + if (!presShell) { 1.6248 + return false; 1.6249 + } 1.6250 + 1.6251 + return presShell->FontSizeInflationEnabled(); 1.6252 +} 1.6253 + 1.6254 +/* static */ nsRect 1.6255 +nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame, 1.6256 + const nsSize& aFrameSize) 1.6257 +{ 1.6258 + nsCSSShadowArray* boxShadows = aFrame->StyleBorder()->mBoxShadow; 1.6259 + if (!boxShadows) { 1.6260 + return nsRect(); 1.6261 + } 1.6262 + 1.6263 + nsRect shadows; 1.6264 + int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.6265 + for (uint32_t i = 0; i < boxShadows->Length(); ++i) { 1.6266 + nsRect tmpRect(nsPoint(0, 0), aFrameSize); 1.6267 + nsCSSShadowItem* shadow = boxShadows->ShadowAt(i); 1.6268 + 1.6269 + // inset shadows are never painted outside the frame 1.6270 + if (shadow->mInset) 1.6271 + continue; 1.6272 + 1.6273 + tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset)); 1.6274 + tmpRect.Inflate(shadow->mSpread); 1.6275 + tmpRect.Inflate( 1.6276 + nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius, A2D)); 1.6277 + shadows.UnionRect(shadows, tmpRect); 1.6278 + } 1.6279 + return shadows; 1.6280 +} 1.6281 + 1.6282 +/* static */ void 1.6283 +nsLayoutUtils::UpdateImageVisibilityForFrame(nsIFrame* aImageFrame) 1.6284 +{ 1.6285 +#ifdef DEBUG 1.6286 + nsIAtom* type = aImageFrame->GetType(); 1.6287 + MOZ_ASSERT(type == nsGkAtoms::imageFrame || 1.6288 + type == nsGkAtoms::imageControlFrame || 1.6289 + type == nsGkAtoms::svgImageFrame, "wrong type of frame"); 1.6290 +#endif 1.6291 + 1.6292 + nsCOMPtr<nsIImageLoadingContent> content = do_QueryInterface(aImageFrame->GetContent()); 1.6293 + if (!content) { 1.6294 + return; 1.6295 + } 1.6296 + 1.6297 + nsIPresShell* presShell = aImageFrame->PresContext()->PresShell(); 1.6298 + if (presShell->AssumeAllImagesVisible()) { 1.6299 + presShell->EnsureImageInVisibleList(content); 1.6300 + return; 1.6301 + } 1.6302 + 1.6303 + bool visible = true; 1.6304 + nsIFrame* f = aImageFrame->GetParent(); 1.6305 + nsRect rect = aImageFrame->GetContentRectRelativeToSelf(); 1.6306 + nsIFrame* rectFrame = aImageFrame; 1.6307 + while (f) { 1.6308 + nsIScrollableFrame* sf = do_QueryFrame(f); 1.6309 + if (sf) { 1.6310 + nsRect transformedRect = 1.6311 + nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f); 1.6312 + if (!sf->IsRectNearlyVisible(transformedRect)) { 1.6313 + visible = false; 1.6314 + break; 1.6315 + } 1.6316 + // Move transformedRect to be contained in the scrollport as best we can 1.6317 + // (it might not fit) to pretend that it was scrolled into view. 1.6318 + nsRect scrollPort = sf->GetScrollPortRect(); 1.6319 + if (transformedRect.XMost() > scrollPort.XMost()) { 1.6320 + transformedRect.x -= transformedRect.XMost() - scrollPort.XMost(); 1.6321 + } 1.6322 + if (transformedRect.x < scrollPort.x) { 1.6323 + transformedRect.x = scrollPort.x; 1.6324 + } 1.6325 + if (transformedRect.YMost() > scrollPort.YMost()) { 1.6326 + transformedRect.y -= transformedRect.YMost() - scrollPort.YMost(); 1.6327 + } 1.6328 + if (transformedRect.y < scrollPort.y) { 1.6329 + transformedRect.y = scrollPort.y; 1.6330 + } 1.6331 + transformedRect.width = std::min(transformedRect.width, scrollPort.width); 1.6332 + transformedRect.height = std::min(transformedRect.height, scrollPort.height); 1.6333 + rect = transformedRect; 1.6334 + rectFrame = f; 1.6335 + } 1.6336 + nsIFrame* parent = f->GetParent(); 1.6337 + if (!parent) { 1.6338 + parent = nsLayoutUtils::GetCrossDocParentFrame(f); 1.6339 + if (parent && parent->PresContext()->IsChrome()) { 1.6340 + break; 1.6341 + } 1.6342 + } 1.6343 + f = parent; 1.6344 + } 1.6345 + 1.6346 + if (visible) { 1.6347 + presShell->EnsureImageInVisibleList(content); 1.6348 + } else { 1.6349 + presShell->RemoveImageFromVisibleList(content); 1.6350 + } 1.6351 +} 1.6352 + 1.6353 +/* static */ nsSize 1.6354 +nsLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame* aFrame) 1.6355 +{ 1.6356 + nsSize size(aFrame->GetSize()); 1.6357 + 1.6358 + nsPresContext* presContext = aFrame->PresContext(); 1.6359 + nsIPresShell* presShell = presContext->PresShell(); 1.6360 + 1.6361 + // See the comments in the code that calculates the root 1.6362 + // composition bounds in RecordFrameMetrics. 1.6363 + // TODO: Reuse that code here. 1.6364 + bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument() 1.6365 + && aFrame == presShell->GetRootScrollFrame(); 1.6366 + if (isRootContentDocRootScrollFrame) { 1.6367 + if (nsIFrame* rootFrame = presShell->GetRootFrame()) { 1.6368 + if (nsView* view = rootFrame->GetView()) { 1.6369 + nsSize viewSize = view->GetBounds().Size(); 1.6370 + nsIWidget* widget = 1.6371 +#ifdef MOZ_WIDGET_ANDROID 1.6372 + rootFrame->GetNearestWidget(); 1.6373 +#else 1.6374 + view->GetWidget(); 1.6375 +#endif 1.6376 + if (widget) { 1.6377 + nsIntRect widgetBounds; 1.6378 + widget->GetBounds(widgetBounds); 1.6379 + int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); 1.6380 + size = nsSize(widgetBounds.width * auPerDevPixel, 1.6381 + widgetBounds.height * auPerDevPixel); 1.6382 +#ifdef MOZ_WIDGET_ANDROID 1.6383 + if (viewSize.height < size.height) { 1.6384 + size.height = viewSize.height; 1.6385 + } 1.6386 +#endif 1.6387 + } else { 1.6388 + size = viewSize; 1.6389 + } 1.6390 + } 1.6391 + } 1.6392 + } 1.6393 + 1.6394 + // Adjust composition bounds for the size of scroll bars. 1.6395 + nsIScrollableFrame* scrollableFrame = aFrame->GetScrollTargetFrame(); 1.6396 + if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { 1.6397 + nsMargin margins = scrollableFrame->GetActualScrollbarSizes(); 1.6398 + size.width -= margins.LeftRight(); 1.6399 + size.height -= margins.TopBottom(); 1.6400 + } 1.6401 + 1.6402 + return size; 1.6403 +} 1.6404 +/* static */ CSSSize 1.6405 +nsLayoutUtils::CalculateRootCompositionSize(nsIFrame* aFrame, 1.6406 + bool aIsRootContentDocRootScrollFrame, 1.6407 + const FrameMetrics& aMetrics) 1.6408 +{ 1.6409 + 1.6410 + if (aIsRootContentDocRootScrollFrame) { 1.6411 + return ViewAs<LayerPixel>(ParentLayerSize(aMetrics.mCompositionBounds.Size()), 1.6412 + PixelCastJustification::ParentLayerToLayerForRootComposition) 1.6413 + / aMetrics.LayersPixelsPerCSSPixel(); 1.6414 + } 1.6415 + nsPresContext* presContext = aFrame->PresContext(); 1.6416 + LayerSize rootCompositionSize; 1.6417 + nsPresContext* rootPresContext = 1.6418 + presContext->GetToplevelContentDocumentPresContext(); 1.6419 + if (!rootPresContext) { 1.6420 + rootPresContext = presContext->GetRootPresContext(); 1.6421 + } 1.6422 + nsIPresShell* rootPresShell = nullptr; 1.6423 + if (rootPresContext) { 1.6424 + // See the comments in the code that calculates the root 1.6425 + // composition bounds in RecordFrameMetrics. 1.6426 + // TODO: Reuse that code here. 1.6427 + nsIPresShell* rootPresShell = rootPresContext->PresShell(); 1.6428 + if (nsIFrame* rootFrame = rootPresShell->GetRootFrame()) { 1.6429 + if (nsView* view = rootFrame->GetView()) { 1.6430 + LayoutDeviceToParentLayerScale parentResolution( 1.6431 + rootPresShell->GetCumulativeResolution().width 1.6432 + / rootPresShell->GetResolution().width); 1.6433 + int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel(); 1.6434 + nsRect viewBounds = view->GetBounds(); 1.6435 + LayerSize viewSize = ViewAs<LayerPixel>( 1.6436 + (LayoutDeviceRect::FromAppUnits(viewBounds, rootAUPerDevPixel) 1.6437 + * parentResolution).Size(), PixelCastJustification::ParentLayerToLayerForRootComposition); 1.6438 + nsIWidget* widget = 1.6439 +#ifdef MOZ_WIDGET_ANDROID 1.6440 + rootFrame->GetNearestWidget(); 1.6441 +#else 1.6442 + view->GetWidget(); 1.6443 +#endif 1.6444 + if (widget) { 1.6445 + nsIntRect widgetBounds; 1.6446 + widget->GetBounds(widgetBounds); 1.6447 + rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size())); 1.6448 +#ifdef MOZ_WIDGET_ANDROID 1.6449 + if (viewSize.height < rootCompositionSize.height) { 1.6450 + rootCompositionSize.height = viewSize.height; 1.6451 + } 1.6452 +#endif 1.6453 + } else { 1.6454 + rootCompositionSize = viewSize; 1.6455 + } 1.6456 + } 1.6457 + } 1.6458 + } else { 1.6459 + nsIWidget* widget = aFrame->GetNearestWidget(); 1.6460 + nsIntRect widgetBounds; 1.6461 + widget->GetBounds(widgetBounds); 1.6462 + rootCompositionSize = LayerSize(ViewAs<LayerPixel>(widgetBounds.Size())); 1.6463 + } 1.6464 + 1.6465 + // Adjust composition size for the size of scroll bars. 1.6466 + nsIFrame* rootRootScrollFrame = rootPresShell ? rootPresShell->GetRootScrollFrame() : nullptr; 1.6467 + nsIScrollableFrame* rootScrollableFrame = nullptr; 1.6468 + if (rootRootScrollFrame) { 1.6469 + rootScrollableFrame = rootRootScrollFrame->GetScrollTargetFrame(); 1.6470 + } 1.6471 + if (rootScrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { 1.6472 + CSSMargin margins = CSSMargin::FromAppUnits(rootScrollableFrame->GetActualScrollbarSizes()); 1.6473 + // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them. 1.6474 + rootCompositionSize.width -= margins.LeftRight(); 1.6475 + rootCompositionSize.height -= margins.TopBottom(); 1.6476 + } 1.6477 + 1.6478 + return rootCompositionSize / aMetrics.LayersPixelsPerCSSPixel(); 1.6479 +} 1.6480 + 1.6481 +/* static */ nsRect 1.6482 +nsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame* aScrollableFrame, nsIFrame* aRootFrame) 1.6483 +{ 1.6484 + nsRect contentBounds; 1.6485 + if (aScrollableFrame) { 1.6486 + contentBounds = aScrollableFrame->GetScrollRange(); 1.6487 + 1.6488 + // We ifndef the below code for Fennec because it requires special behaviour 1.6489 + // on the APZC side. Because Fennec has it's own PZC implementation which doesn't 1.6490 + // provide the special behaviour, this code will cause it to break. We can remove 1.6491 + // the ifndef once Fennec switches over to APZ or if we add the special handling 1.6492 + // to Fennec 1.6493 +#ifndef MOZ_WIDGET_ANDROID 1.6494 + nsPoint scrollPosition = aScrollableFrame->GetScrollPosition(); 1.6495 + if (aScrollableFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) { 1.6496 + contentBounds.y = scrollPosition.y; 1.6497 + contentBounds.height = 0; 1.6498 + } 1.6499 + if (aScrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) { 1.6500 + contentBounds.x = scrollPosition.x; 1.6501 + contentBounds.width = 0; 1.6502 + } 1.6503 +#endif 1.6504 + 1.6505 + contentBounds.width += aScrollableFrame->GetScrollPortRect().width; 1.6506 + contentBounds.height += aScrollableFrame->GetScrollPortRect().height; 1.6507 + } else { 1.6508 + contentBounds = aRootFrame->GetRect(); 1.6509 + } 1.6510 + return contentBounds; 1.6511 +} 1.6512 + 1.6513 +/* static */ nsRect 1.6514 +nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame* aFrame) 1.6515 +{ 1.6516 + nsRect scrollableRect = 1.6517 + CalculateScrollableRectForFrame(aFrame->GetScrollTargetFrame(), 1.6518 + aFrame->PresContext()->PresShell()->GetRootFrame()); 1.6519 + nsSize compSize = CalculateCompositionSizeForFrame(aFrame); 1.6520 + 1.6521 + if (aFrame == aFrame->PresContext()->PresShell()->GetRootScrollFrame()) { 1.6522 + // the composition size for the root scroll frame does not include the 1.6523 + // local resolution, so we adjust. 1.6524 + gfxSize res = aFrame->PresContext()->PresShell()->GetResolution(); 1.6525 + compSize.width = NSToCoordRound(compSize.width / ((float) res.width)); 1.6526 + compSize.height = NSToCoordRound(compSize.height / ((float) res.height)); 1.6527 + } 1.6528 + 1.6529 + if (scrollableRect.width < compSize.width) { 1.6530 + scrollableRect.x = std::max(0, 1.6531 + scrollableRect.x - (compSize.width - scrollableRect.width)); 1.6532 + scrollableRect.width = compSize.width; 1.6533 + } 1.6534 + 1.6535 + if (scrollableRect.height < compSize.height) { 1.6536 + scrollableRect.y = std::max(0, 1.6537 + scrollableRect.y - (compSize.height - scrollableRect.height)); 1.6538 + scrollableRect.height = compSize.height; 1.6539 + } 1.6540 + return scrollableRect; 1.6541 +} 1.6542 + 1.6543 +/* static */ bool 1.6544 +nsLayoutUtils::WantSubAPZC() 1.6545 +{ 1.6546 + // TODO Turn this on for inprocess OMTC on all platforms 1.6547 + bool wantSubAPZC = gfxPrefs::APZSubframeEnabled(); 1.6548 +#ifdef MOZ_WIDGET_GONK 1.6549 + if (XRE_GetProcessType() != GeckoProcessType_Content) { 1.6550 + wantSubAPZC = false; 1.6551 + } 1.6552 +#endif 1.6553 + return wantSubAPZC; 1.6554 + } 1.6555 + 1.6556 +nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult() 1.6557 + // Use safe default values here 1.6558 + : mIsWriteOnly(true) 1.6559 + , mIsStillLoading(false) 1.6560 + , mCORSUsed(false) 1.6561 + , mIsPremultiplied(true) 1.6562 +{ 1.6563 +} 1.6564 + 1.6565 +bool 1.6566 +nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame) 1.6567 +{ 1.6568 + return GetAsBlock(aFrame) && !aFrame->IsBlockWrapper(); 1.6569 +} 1.6570 + 1.6571 +bool 1.6572 +nsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext* aPresContext) 1.6573 +{ 1.6574 + return aPresContext->IsRootPaginatedDocument() && 1.6575 + (aPresContext->Type() == nsPresContext::eContext_PrintPreview || 1.6576 + aPresContext->Type() == nsPresContext::eContext_PageLayout); 1.6577 +} 1.6578 + 1.6579 +AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame *aFrame) 1.6580 +{ 1.6581 + // FIXME: Now that inflation calculations are based on the flow 1.6582 + // root's NCA's (nearest common ancestor of its inflatable 1.6583 + // descendants) width, we could probably disable inflation in 1.6584 + // fewer cases than we currently do. 1.6585 + if (aFrame->IsContainerForFontSizeInflation()) { 1.6586 + mPresContext = aFrame->PresContext(); 1.6587 + mOldValue = mPresContext->mInflationDisabledForShrinkWrap; 1.6588 + mPresContext->mInflationDisabledForShrinkWrap = true; 1.6589 + } else { 1.6590 + // indicate we have nothing to restore 1.6591 + mPresContext = nullptr; 1.6592 + } 1.6593 +} 1.6594 + 1.6595 +AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation() 1.6596 +{ 1.6597 + if (mPresContext) { 1.6598 + mPresContext->mInflationDisabledForShrinkWrap = mOldValue; 1.6599 + } 1.6600 +}