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