layout/base/nsLayoutUtils.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 }

mercurial