Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | |
michael@0 | 8 | /* |
michael@0 | 9 | * structures that represent things to be painted (ordered in z-order), |
michael@0 | 10 | * used during painting and hit testing |
michael@0 | 11 | */ |
michael@0 | 12 | |
michael@0 | 13 | #include "mozilla/dom/TabChild.h" |
michael@0 | 14 | #include "mozilla/layers/PLayerTransaction.h" |
michael@0 | 15 | |
michael@0 | 16 | #include "nsDisplayList.h" |
michael@0 | 17 | |
michael@0 | 18 | #include "nsCSSRendering.h" |
michael@0 | 19 | #include "nsRenderingContext.h" |
michael@0 | 20 | #include "nsISelectionController.h" |
michael@0 | 21 | #include "nsIPresShell.h" |
michael@0 | 22 | #include "nsRegion.h" |
michael@0 | 23 | #include "nsStyleStructInlines.h" |
michael@0 | 24 | #include "nsStyleTransformMatrix.h" |
michael@0 | 25 | #include "gfxMatrix.h" |
michael@0 | 26 | #include "nsSVGIntegrationUtils.h" |
michael@0 | 27 | #include "nsLayoutUtils.h" |
michael@0 | 28 | #include "nsIScrollableFrame.h" |
michael@0 | 29 | #include "nsIFrameInlines.h" |
michael@0 | 30 | #include "nsThemeConstants.h" |
michael@0 | 31 | #include "LayerTreeInvalidation.h" |
michael@0 | 32 | |
michael@0 | 33 | #include "imgIContainer.h" |
michael@0 | 34 | #include "BasicLayers.h" |
michael@0 | 35 | #include "nsBoxFrame.h" |
michael@0 | 36 | #include "nsViewportFrame.h" |
michael@0 | 37 | #include "nsSubDocumentFrame.h" |
michael@0 | 38 | #include "nsSVGEffects.h" |
michael@0 | 39 | #include "nsSVGElement.h" |
michael@0 | 40 | #include "nsSVGClipPathFrame.h" |
michael@0 | 41 | #include "GeckoProfiler.h" |
michael@0 | 42 | #include "nsAnimationManager.h" |
michael@0 | 43 | #include "nsTransitionManager.h" |
michael@0 | 44 | #include "nsViewManager.h" |
michael@0 | 45 | #include "ImageLayers.h" |
michael@0 | 46 | #include "ImageContainer.h" |
michael@0 | 47 | #include "nsCanvasFrame.h" |
michael@0 | 48 | #include "StickyScrollContainer.h" |
michael@0 | 49 | #include "mozilla/EventStates.h" |
michael@0 | 50 | #include "mozilla/LookAndFeel.h" |
michael@0 | 51 | #include "mozilla/Preferences.h" |
michael@0 | 52 | #include "ActiveLayerTracker.h" |
michael@0 | 53 | #include "nsContentUtils.h" |
michael@0 | 54 | #include "nsPrintfCString.h" |
michael@0 | 55 | #include "UnitTransforms.h" |
michael@0 | 56 | |
michael@0 | 57 | #include <stdint.h> |
michael@0 | 58 | #include <algorithm> |
michael@0 | 59 | |
michael@0 | 60 | using namespace mozilla; |
michael@0 | 61 | using namespace mozilla::css; |
michael@0 | 62 | using namespace mozilla::layers; |
michael@0 | 63 | using namespace mozilla::dom; |
michael@0 | 64 | typedef FrameMetrics::ViewID ViewID; |
michael@0 | 65 | |
michael@0 | 66 | static inline nsIFrame* |
michael@0 | 67 | GetTransformRootFrame(nsIFrame* aFrame) |
michael@0 | 68 | { |
michael@0 | 69 | return nsLayoutUtils::GetTransformRootFrame(aFrame); |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | static void AddTransformFunctions(nsCSSValueList* aList, |
michael@0 | 73 | nsStyleContext* aContext, |
michael@0 | 74 | nsPresContext* aPresContext, |
michael@0 | 75 | nsRect& aBounds, |
michael@0 | 76 | float aAppUnitsPerPixel, |
michael@0 | 77 | InfallibleTArray<TransformFunction>& aFunctions) |
michael@0 | 78 | { |
michael@0 | 79 | if (aList->mValue.GetUnit() == eCSSUnit_None) { |
michael@0 | 80 | return; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | for (const nsCSSValueList* curr = aList; curr; curr = curr->mNext) { |
michael@0 | 84 | const nsCSSValue& currElem = curr->mValue; |
michael@0 | 85 | NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function, |
michael@0 | 86 | "Stream should consist solely of functions!"); |
michael@0 | 87 | nsCSSValue::Array* array = currElem.GetArrayValue(); |
michael@0 | 88 | bool canStoreInRuleTree = true; |
michael@0 | 89 | switch (nsStyleTransformMatrix::TransformFunctionOf(array)) { |
michael@0 | 90 | case eCSSKeyword_rotatex: |
michael@0 | 91 | { |
michael@0 | 92 | double theta = array->Item(1).GetAngleValueInRadians(); |
michael@0 | 93 | aFunctions.AppendElement(RotationX(theta)); |
michael@0 | 94 | break; |
michael@0 | 95 | } |
michael@0 | 96 | case eCSSKeyword_rotatey: |
michael@0 | 97 | { |
michael@0 | 98 | double theta = array->Item(1).GetAngleValueInRadians(); |
michael@0 | 99 | aFunctions.AppendElement(RotationY(theta)); |
michael@0 | 100 | break; |
michael@0 | 101 | } |
michael@0 | 102 | case eCSSKeyword_rotatez: |
michael@0 | 103 | { |
michael@0 | 104 | double theta = array->Item(1).GetAngleValueInRadians(); |
michael@0 | 105 | aFunctions.AppendElement(RotationZ(theta)); |
michael@0 | 106 | break; |
michael@0 | 107 | } |
michael@0 | 108 | case eCSSKeyword_rotate: |
michael@0 | 109 | { |
michael@0 | 110 | double theta = array->Item(1).GetAngleValueInRadians(); |
michael@0 | 111 | aFunctions.AppendElement(Rotation(theta)); |
michael@0 | 112 | break; |
michael@0 | 113 | } |
michael@0 | 114 | case eCSSKeyword_rotate3d: |
michael@0 | 115 | { |
michael@0 | 116 | double x = array->Item(1).GetFloatValue(); |
michael@0 | 117 | double y = array->Item(2).GetFloatValue(); |
michael@0 | 118 | double z = array->Item(3).GetFloatValue(); |
michael@0 | 119 | double theta = array->Item(4).GetAngleValueInRadians(); |
michael@0 | 120 | aFunctions.AppendElement(Rotation3D(x, y, z, theta)); |
michael@0 | 121 | break; |
michael@0 | 122 | } |
michael@0 | 123 | case eCSSKeyword_scalex: |
michael@0 | 124 | { |
michael@0 | 125 | double x = array->Item(1).GetFloatValue(); |
michael@0 | 126 | aFunctions.AppendElement(Scale(x, 1, 1)); |
michael@0 | 127 | break; |
michael@0 | 128 | } |
michael@0 | 129 | case eCSSKeyword_scaley: |
michael@0 | 130 | { |
michael@0 | 131 | double y = array->Item(1).GetFloatValue(); |
michael@0 | 132 | aFunctions.AppendElement(Scale(1, y, 1)); |
michael@0 | 133 | break; |
michael@0 | 134 | } |
michael@0 | 135 | case eCSSKeyword_scalez: |
michael@0 | 136 | { |
michael@0 | 137 | double z = array->Item(1).GetFloatValue(); |
michael@0 | 138 | aFunctions.AppendElement(Scale(1, 1, z)); |
michael@0 | 139 | break; |
michael@0 | 140 | } |
michael@0 | 141 | case eCSSKeyword_scale: |
michael@0 | 142 | { |
michael@0 | 143 | double x = array->Item(1).GetFloatValue(); |
michael@0 | 144 | // scale(x) is shorthand for scale(x, x); |
michael@0 | 145 | double y = array->Count() == 2 ? x : array->Item(2).GetFloatValue(); |
michael@0 | 146 | aFunctions.AppendElement(Scale(x, y, 1)); |
michael@0 | 147 | break; |
michael@0 | 148 | } |
michael@0 | 149 | case eCSSKeyword_scale3d: |
michael@0 | 150 | { |
michael@0 | 151 | double x = array->Item(1).GetFloatValue(); |
michael@0 | 152 | double y = array->Item(2).GetFloatValue(); |
michael@0 | 153 | double z = array->Item(3).GetFloatValue(); |
michael@0 | 154 | aFunctions.AppendElement(Scale(x, y, z)); |
michael@0 | 155 | break; |
michael@0 | 156 | } |
michael@0 | 157 | case eCSSKeyword_translatex: |
michael@0 | 158 | { |
michael@0 | 159 | double x = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 160 | array->Item(1), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 161 | aBounds.Width(), aAppUnitsPerPixel); |
michael@0 | 162 | aFunctions.AppendElement(Translation(x, 0, 0)); |
michael@0 | 163 | break; |
michael@0 | 164 | } |
michael@0 | 165 | case eCSSKeyword_translatey: |
michael@0 | 166 | { |
michael@0 | 167 | double y = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 168 | array->Item(1), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 169 | aBounds.Height(), aAppUnitsPerPixel); |
michael@0 | 170 | aFunctions.AppendElement(Translation(0, y, 0)); |
michael@0 | 171 | break; |
michael@0 | 172 | } |
michael@0 | 173 | case eCSSKeyword_translatez: |
michael@0 | 174 | { |
michael@0 | 175 | double z = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 176 | array->Item(1), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 177 | 0, aAppUnitsPerPixel); |
michael@0 | 178 | aFunctions.AppendElement(Translation(0, 0, z)); |
michael@0 | 179 | break; |
michael@0 | 180 | } |
michael@0 | 181 | case eCSSKeyword_translate: |
michael@0 | 182 | { |
michael@0 | 183 | double x = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 184 | array->Item(1), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 185 | aBounds.Width(), aAppUnitsPerPixel); |
michael@0 | 186 | // translate(x) is shorthand for translate(x, 0) |
michael@0 | 187 | double y = 0; |
michael@0 | 188 | if (array->Count() == 3) { |
michael@0 | 189 | y = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 190 | array->Item(2), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 191 | aBounds.Height(), aAppUnitsPerPixel); |
michael@0 | 192 | } |
michael@0 | 193 | aFunctions.AppendElement(Translation(x, y, 0)); |
michael@0 | 194 | break; |
michael@0 | 195 | } |
michael@0 | 196 | case eCSSKeyword_translate3d: |
michael@0 | 197 | { |
michael@0 | 198 | double x = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 199 | array->Item(1), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 200 | aBounds.Width(), aAppUnitsPerPixel); |
michael@0 | 201 | double y = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 202 | array->Item(2), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 203 | aBounds.Height(), aAppUnitsPerPixel); |
michael@0 | 204 | double z = nsStyleTransformMatrix::ProcessTranslatePart( |
michael@0 | 205 | array->Item(3), aContext, aPresContext, canStoreInRuleTree, |
michael@0 | 206 | 0, aAppUnitsPerPixel); |
michael@0 | 207 | |
michael@0 | 208 | aFunctions.AppendElement(Translation(x, y, z)); |
michael@0 | 209 | break; |
michael@0 | 210 | } |
michael@0 | 211 | case eCSSKeyword_skewx: |
michael@0 | 212 | { |
michael@0 | 213 | double x = array->Item(1).GetAngleValueInRadians(); |
michael@0 | 214 | aFunctions.AppendElement(SkewX(x)); |
michael@0 | 215 | break; |
michael@0 | 216 | } |
michael@0 | 217 | case eCSSKeyword_skewy: |
michael@0 | 218 | { |
michael@0 | 219 | double y = array->Item(1).GetAngleValueInRadians(); |
michael@0 | 220 | aFunctions.AppendElement(SkewY(y)); |
michael@0 | 221 | break; |
michael@0 | 222 | } |
michael@0 | 223 | case eCSSKeyword_skew: |
michael@0 | 224 | { |
michael@0 | 225 | double x = array->Item(1).GetAngleValueInRadians(); |
michael@0 | 226 | // skew(x) is shorthand for skew(x, 0) |
michael@0 | 227 | double y = 0; |
michael@0 | 228 | if (array->Count() == 3) { |
michael@0 | 229 | y = array->Item(2).GetAngleValueInRadians(); |
michael@0 | 230 | } |
michael@0 | 231 | aFunctions.AppendElement(Skew(x, y)); |
michael@0 | 232 | break; |
michael@0 | 233 | } |
michael@0 | 234 | case eCSSKeyword_matrix: |
michael@0 | 235 | { |
michael@0 | 236 | gfx::Matrix4x4 matrix; |
michael@0 | 237 | matrix._11 = array->Item(1).GetFloatValue(); |
michael@0 | 238 | matrix._12 = array->Item(2).GetFloatValue(); |
michael@0 | 239 | matrix._13 = 0; |
michael@0 | 240 | matrix._14 = 0; |
michael@0 | 241 | matrix._21 = array->Item(3).GetFloatValue(); |
michael@0 | 242 | matrix._22 = array->Item(4).GetFloatValue(); |
michael@0 | 243 | matrix._23 = 0; |
michael@0 | 244 | matrix._24 = 0; |
michael@0 | 245 | matrix._31 = 0; |
michael@0 | 246 | matrix._32 = 0; |
michael@0 | 247 | matrix._33 = 1; |
michael@0 | 248 | matrix._34 = 0; |
michael@0 | 249 | matrix._41 = array->Item(5).GetFloatValue(); |
michael@0 | 250 | matrix._42 = array->Item(6).GetFloatValue(); |
michael@0 | 251 | matrix._43 = 0; |
michael@0 | 252 | matrix._44 = 1; |
michael@0 | 253 | aFunctions.AppendElement(TransformMatrix(matrix)); |
michael@0 | 254 | break; |
michael@0 | 255 | } |
michael@0 | 256 | case eCSSKeyword_matrix3d: |
michael@0 | 257 | { |
michael@0 | 258 | gfx::Matrix4x4 matrix; |
michael@0 | 259 | matrix._11 = array->Item(1).GetFloatValue(); |
michael@0 | 260 | matrix._12 = array->Item(2).GetFloatValue(); |
michael@0 | 261 | matrix._13 = array->Item(3).GetFloatValue(); |
michael@0 | 262 | matrix._14 = array->Item(4).GetFloatValue(); |
michael@0 | 263 | matrix._21 = array->Item(5).GetFloatValue(); |
michael@0 | 264 | matrix._22 = array->Item(6).GetFloatValue(); |
michael@0 | 265 | matrix._23 = array->Item(7).GetFloatValue(); |
michael@0 | 266 | matrix._24 = array->Item(8).GetFloatValue(); |
michael@0 | 267 | matrix._31 = array->Item(9).GetFloatValue(); |
michael@0 | 268 | matrix._32 = array->Item(10).GetFloatValue(); |
michael@0 | 269 | matrix._33 = array->Item(11).GetFloatValue(); |
michael@0 | 270 | matrix._34 = array->Item(12).GetFloatValue(); |
michael@0 | 271 | matrix._41 = array->Item(13).GetFloatValue(); |
michael@0 | 272 | matrix._42 = array->Item(14).GetFloatValue(); |
michael@0 | 273 | matrix._43 = array->Item(15).GetFloatValue(); |
michael@0 | 274 | matrix._44 = array->Item(16).GetFloatValue(); |
michael@0 | 275 | aFunctions.AppendElement(TransformMatrix(matrix)); |
michael@0 | 276 | break; |
michael@0 | 277 | } |
michael@0 | 278 | case eCSSKeyword_interpolatematrix: |
michael@0 | 279 | { |
michael@0 | 280 | gfx3DMatrix matrix; |
michael@0 | 281 | nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, array, |
michael@0 | 282 | aContext, |
michael@0 | 283 | aPresContext, |
michael@0 | 284 | canStoreInRuleTree, |
michael@0 | 285 | aBounds, |
michael@0 | 286 | aAppUnitsPerPixel); |
michael@0 | 287 | gfx::Matrix4x4 transform; |
michael@0 | 288 | gfx::ToMatrix4x4(matrix, transform); |
michael@0 | 289 | aFunctions.AppendElement(TransformMatrix(transform)); |
michael@0 | 290 | break; |
michael@0 | 291 | } |
michael@0 | 292 | case eCSSKeyword_perspective: |
michael@0 | 293 | { |
michael@0 | 294 | aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue())); |
michael@0 | 295 | break; |
michael@0 | 296 | } |
michael@0 | 297 | default: |
michael@0 | 298 | NS_ERROR("Function not handled yet!"); |
michael@0 | 299 | } |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | static TimingFunction |
michael@0 | 304 | ToTimingFunction(css::ComputedTimingFunction& aCTF) |
michael@0 | 305 | { |
michael@0 | 306 | if (aCTF.GetType() == nsTimingFunction::Function) { |
michael@0 | 307 | const nsSMILKeySpline* spline = aCTF.GetFunction(); |
michael@0 | 308 | return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(), |
michael@0 | 309 | spline->X2(), spline->Y2())); |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | uint32_t type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2; |
michael@0 | 313 | return TimingFunction(StepFunction(aCTF.GetSteps(), type)); |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | static void |
michael@0 | 317 | AddAnimationForProperty(nsIFrame* aFrame, nsCSSProperty aProperty, |
michael@0 | 318 | mozilla::StyleAnimation* ea, Layer* aLayer, |
michael@0 | 319 | AnimationData& aData, bool aPending) |
michael@0 | 320 | { |
michael@0 | 321 | NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer"); |
michael@0 | 322 | nsStyleContext* styleContext = aFrame->StyleContext(); |
michael@0 | 323 | nsPresContext* presContext = aFrame->PresContext(); |
michael@0 | 324 | nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); |
michael@0 | 325 | // all data passed directly to the compositor should be in css pixels |
michael@0 | 326 | float scale = nsDeviceContext::AppUnitsPerCSSPixel(); |
michael@0 | 327 | |
michael@0 | 328 | mozilla::layers::Animation* animation = |
michael@0 | 329 | aPending ? |
michael@0 | 330 | aLayer->AddAnimationForNextTransaction() : |
michael@0 | 331 | aLayer->AddAnimation(); |
michael@0 | 332 | |
michael@0 | 333 | animation->startTime() = ea->mStartTime + ea->mDelay; |
michael@0 | 334 | animation->duration() = ea->mIterationDuration; |
michael@0 | 335 | animation->numIterations() = |
michael@0 | 336 | ea->mIterationCount != NS_IEEEPositiveInfinity() ? ea->mIterationCount : -1; |
michael@0 | 337 | animation->direction() = ea->mDirection; |
michael@0 | 338 | animation->property() = aProperty; |
michael@0 | 339 | animation->data() = aData; |
michael@0 | 340 | |
michael@0 | 341 | for (uint32_t propIdx = 0; propIdx < ea->mProperties.Length(); propIdx++) { |
michael@0 | 342 | AnimationProperty* property = &ea->mProperties[propIdx]; |
michael@0 | 343 | |
michael@0 | 344 | if (aProperty != property->mProperty) { |
michael@0 | 345 | continue; |
michael@0 | 346 | } |
michael@0 | 347 | |
michael@0 | 348 | for (uint32_t segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) { |
michael@0 | 349 | AnimationPropertySegment* segment = &property->mSegments[segIdx]; |
michael@0 | 350 | |
michael@0 | 351 | AnimationSegment* animSegment = animation->segments().AppendElement(); |
michael@0 | 352 | if (aProperty == eCSSProperty_transform) { |
michael@0 | 353 | animSegment->startState() = InfallibleTArray<TransformFunction>(); |
michael@0 | 354 | animSegment->endState() = InfallibleTArray<TransformFunction>(); |
michael@0 | 355 | |
michael@0 | 356 | nsCSSValueSharedList* list = segment->mFromValue.GetCSSValueSharedListValue(); |
michael@0 | 357 | AddTransformFunctions(list->mHead, styleContext, presContext, bounds, scale, |
michael@0 | 358 | animSegment->startState().get_ArrayOfTransformFunction()); |
michael@0 | 359 | |
michael@0 | 360 | list = segment->mToValue.GetCSSValueSharedListValue(); |
michael@0 | 361 | AddTransformFunctions(list->mHead, styleContext, presContext, bounds, scale, |
michael@0 | 362 | animSegment->endState().get_ArrayOfTransformFunction()); |
michael@0 | 363 | } else if (aProperty == eCSSProperty_opacity) { |
michael@0 | 364 | animSegment->startState() = segment->mFromValue.GetFloatValue(); |
michael@0 | 365 | animSegment->endState() = segment->mToValue.GetFloatValue(); |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | animSegment->startPortion() = segment->mFromKey; |
michael@0 | 369 | animSegment->endPortion() = segment->mToKey; |
michael@0 | 370 | animSegment->sampleFn() = ToTimingFunction(segment->mTimingFunction); |
michael@0 | 371 | } |
michael@0 | 372 | } |
michael@0 | 373 | } |
michael@0 | 374 | |
michael@0 | 375 | template<class T> |
michael@0 | 376 | static void |
michael@0 | 377 | AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty, |
michael@0 | 378 | nsTArray<T>& aAnimations, |
michael@0 | 379 | Layer* aLayer, AnimationData& aData, |
michael@0 | 380 | bool aPending) { |
michael@0 | 381 | mozilla::TimeStamp currentTime = |
michael@0 | 382 | aFrame->PresContext()->RefreshDriver()->MostRecentRefresh(); |
michael@0 | 383 | for (uint32_t animIdx = 0; animIdx < aAnimations.Length(); animIdx++) { |
michael@0 | 384 | mozilla::StyleAnimation* anim = &aAnimations[animIdx]; |
michael@0 | 385 | if (!(anim->HasAnimationOfProperty(aProperty) && |
michael@0 | 386 | anim->IsRunningAt(currentTime))) { |
michael@0 | 387 | continue; |
michael@0 | 388 | } |
michael@0 | 389 | AddAnimationForProperty(aFrame, aProperty, anim, aLayer, aData, aPending); |
michael@0 | 390 | anim->mIsRunningOnCompositor = true; |
michael@0 | 391 | } |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | /* static */ void |
michael@0 | 395 | nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer, |
michael@0 | 396 | nsDisplayListBuilder* aBuilder, |
michael@0 | 397 | nsDisplayItem* aItem, |
michael@0 | 398 | nsIFrame* aFrame, |
michael@0 | 399 | nsCSSProperty aProperty) |
michael@0 | 400 | { |
michael@0 | 401 | // This function can be called in two ways: from |
michael@0 | 402 | // nsDisplay*::BuildLayer while constructing a layer (with all |
michael@0 | 403 | // pointers non-null), or from RestyleManager's handling of |
michael@0 | 404 | // UpdateOpacityLayer/UpdateTransformLayer hints. |
michael@0 | 405 | MOZ_ASSERT(!aBuilder == !aItem, |
michael@0 | 406 | "should only be called in two configurations, with both " |
michael@0 | 407 | "aBuilder and aItem, or with neither"); |
michael@0 | 408 | MOZ_ASSERT(!aItem || aFrame == aItem->Frame(), "frame mismatch"); |
michael@0 | 409 | |
michael@0 | 410 | bool pending = !aBuilder; |
michael@0 | 411 | |
michael@0 | 412 | if (pending) { |
michael@0 | 413 | aLayer->ClearAnimationsForNextTransaction(); |
michael@0 | 414 | } else { |
michael@0 | 415 | aLayer->ClearAnimations(); |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | nsIContent* content = aFrame->GetContent(); |
michael@0 | 419 | if (!content) { |
michael@0 | 420 | return; |
michael@0 | 421 | } |
michael@0 | 422 | ElementTransitions* et = |
michael@0 | 423 | nsTransitionManager::GetTransitionsForCompositor(content, aProperty); |
michael@0 | 424 | |
michael@0 | 425 | ElementAnimations* ea = |
michael@0 | 426 | nsAnimationManager::GetAnimationsForCompositor(content, aProperty); |
michael@0 | 427 | |
michael@0 | 428 | if (!ea && !et) { |
michael@0 | 429 | return; |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | // If the frame is not prerendered, bail out. |
michael@0 | 433 | // Do this check only during layer construction; during updating the |
michael@0 | 434 | // caller is required to check it appropriately. |
michael@0 | 435 | if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) { |
michael@0 | 436 | // AnimationManager or TransitionManager need to know that we refused to |
michael@0 | 437 | // run this animation asynchronously so that they will not throttle the |
michael@0 | 438 | // main thread animation. |
michael@0 | 439 | aFrame->Properties().Set(nsIFrame::RefusedAsyncAnimation(), |
michael@0 | 440 | reinterpret_cast<void*>(intptr_t(true))); |
michael@0 | 441 | |
michael@0 | 442 | // We need to schedule another refresh driver run so that AnimationManager |
michael@0 | 443 | // or TransitionManager get a chance to unthrottle the animation. |
michael@0 | 444 | aFrame->SchedulePaint(); |
michael@0 | 445 | return; |
michael@0 | 446 | } |
michael@0 | 447 | |
michael@0 | 448 | AnimationData data; |
michael@0 | 449 | if (aProperty == eCSSProperty_transform) { |
michael@0 | 450 | nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); |
michael@0 | 451 | // all data passed directly to the compositor should be in css pixels |
michael@0 | 452 | float scale = nsDeviceContext::AppUnitsPerCSSPixel(); |
michael@0 | 453 | gfxPoint3D offsetToTransformOrigin = |
michael@0 | 454 | nsDisplayTransform::GetDeltaToTransformOrigin(aFrame, scale, &bounds); |
michael@0 | 455 | gfxPoint3D offsetToPerspectiveOrigin = |
michael@0 | 456 | nsDisplayTransform::GetDeltaToPerspectiveOrigin(aFrame, scale); |
michael@0 | 457 | nscoord perspective = 0.0; |
michael@0 | 458 | nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent(); |
michael@0 | 459 | if (parentStyleContext) { |
michael@0 | 460 | const nsStyleDisplay* disp = parentStyleContext->StyleDisplay(); |
michael@0 | 461 | if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) { |
michael@0 | 462 | perspective = disp->mChildPerspective.GetCoordValue(); |
michael@0 | 463 | } |
michael@0 | 464 | } |
michael@0 | 465 | nsPoint origin; |
michael@0 | 466 | if (aItem) { |
michael@0 | 467 | origin = aItem->ToReferenceFrame(); |
michael@0 | 468 | } else { |
michael@0 | 469 | // transform display items used a reference frame computed from |
michael@0 | 470 | // their GetTransformRootFrame(). |
michael@0 | 471 | nsIFrame* referenceFrame = |
michael@0 | 472 | nsLayoutUtils::GetReferenceFrame(GetTransformRootFrame(aFrame)); |
michael@0 | 473 | origin = aFrame->GetOffsetToCrossDoc(referenceFrame); |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | data = TransformData(origin, offsetToTransformOrigin, |
michael@0 | 477 | offsetToPerspectiveOrigin, bounds, perspective, |
michael@0 | 478 | aFrame->PresContext()->AppUnitsPerDevPixel()); |
michael@0 | 479 | } else if (aProperty == eCSSProperty_opacity) { |
michael@0 | 480 | data = null_t(); |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | if (et) { |
michael@0 | 484 | AddAnimationsForProperty(aFrame, aProperty, et->mPropertyTransitions, |
michael@0 | 485 | aLayer, data, pending); |
michael@0 | 486 | aLayer->SetAnimationGeneration(et->mAnimationGeneration); |
michael@0 | 487 | } |
michael@0 | 488 | |
michael@0 | 489 | if (ea) { |
michael@0 | 490 | AddAnimationsForProperty(aFrame, aProperty, ea->mAnimations, |
michael@0 | 491 | aLayer, data, pending); |
michael@0 | 492 | aLayer->SetAnimationGeneration(ea->mAnimationGeneration); |
michael@0 | 493 | } |
michael@0 | 494 | } |
michael@0 | 495 | |
michael@0 | 496 | nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, |
michael@0 | 497 | Mode aMode, bool aBuildCaret) |
michael@0 | 498 | : mReferenceFrame(aReferenceFrame), |
michael@0 | 499 | mIgnoreScrollFrame(nullptr), |
michael@0 | 500 | mLayerEventRegions(nullptr), |
michael@0 | 501 | mCurrentTableItem(nullptr), |
michael@0 | 502 | mFinalTransparentRegion(nullptr), |
michael@0 | 503 | mCachedOffsetFrame(aReferenceFrame), |
michael@0 | 504 | mCachedReferenceFrame(aReferenceFrame), |
michael@0 | 505 | mCachedOffset(0, 0), |
michael@0 | 506 | mGlassDisplayItem(nullptr), |
michael@0 | 507 | mMode(aMode), |
michael@0 | 508 | mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID), |
michael@0 | 509 | mBuildCaret(aBuildCaret), |
michael@0 | 510 | mIgnoreSuppression(false), |
michael@0 | 511 | mHadToIgnoreSuppression(false), |
michael@0 | 512 | mIsAtRootOfPseudoStackingContext(false), |
michael@0 | 513 | mIncludeAllOutOfFlows(false), |
michael@0 | 514 | mDescendIntoSubdocuments(true), |
michael@0 | 515 | mSelectedFramesOnly(false), |
michael@0 | 516 | mAccurateVisibleRegions(false), |
michael@0 | 517 | mAllowMergingAndFlattening(true), |
michael@0 | 518 | mWillComputePluginGeometry(false), |
michael@0 | 519 | mInTransform(false), |
michael@0 | 520 | mInFixedPos(false), |
michael@0 | 521 | mSyncDecodeImages(false), |
michael@0 | 522 | mIsPaintingToWindow(false), |
michael@0 | 523 | mIsCompositingCheap(false), |
michael@0 | 524 | mContainsPluginItem(false), |
michael@0 | 525 | mContainsBlendMode(false), |
michael@0 | 526 | mAncestorHasTouchEventHandler(false), |
michael@0 | 527 | mHaveScrollableDisplayPort(false) |
michael@0 | 528 | { |
michael@0 | 529 | MOZ_COUNT_CTOR(nsDisplayListBuilder); |
michael@0 | 530 | PL_InitArenaPool(&mPool, "displayListArena", 1024, |
michael@0 | 531 | std::max(NS_ALIGNMENT_OF(void*),NS_ALIGNMENT_OF(double))-1); |
michael@0 | 532 | |
michael@0 | 533 | nsPresContext* pc = aReferenceFrame->PresContext(); |
michael@0 | 534 | nsIPresShell *shell = pc->PresShell(); |
michael@0 | 535 | if (pc->IsRenderingOnlySelection()) { |
michael@0 | 536 | nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell)); |
michael@0 | 537 | if (selcon) { |
michael@0 | 538 | selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, |
michael@0 | 539 | getter_AddRefs(mBoundingSelection)); |
michael@0 | 540 | } |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | nsCSSRendering::BeginFrameTreesLocked(); |
michael@0 | 544 | PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS)); |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | static void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) { |
michael@0 | 548 | for (nsIFrame* f = aFrame; f; |
michael@0 | 549 | f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) { |
michael@0 | 550 | if (f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) |
michael@0 | 551 | return; |
michael@0 | 552 | f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO); |
michael@0 | 553 | if (f == aStopAtFrame) { |
michael@0 | 554 | // we've reached a frame that we know will be painted, so we can stop. |
michael@0 | 555 | break; |
michael@0 | 556 | } |
michael@0 | 557 | } |
michael@0 | 558 | } |
michael@0 | 559 | |
michael@0 | 560 | void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, |
michael@0 | 561 | nsIFrame* aFrame, |
michael@0 | 562 | const nsRect& aDirtyRect) |
michael@0 | 563 | { |
michael@0 | 564 | nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect; |
michael@0 | 565 | if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame)) { |
michael@0 | 566 | NS_ASSERTION(aDirtyFrame == aFrame->GetParent(), "Dirty frame should be viewport frame"); |
michael@0 | 567 | // position: fixed items are reflowed into and only drawn inside the |
michael@0 | 568 | // viewport, or the scroll position clamping scrollport size, if one is |
michael@0 | 569 | // set. |
michael@0 | 570 | nsIPresShell* ps = aFrame->PresContext()->PresShell(); |
michael@0 | 571 | dirtyRectRelativeToDirtyFrame.MoveTo(0, 0); |
michael@0 | 572 | if (ps->IsScrollPositionClampingScrollPortSizeSet()) { |
michael@0 | 573 | dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetScrollPositionClampingScrollPortSize()); |
michael@0 | 574 | } else { |
michael@0 | 575 | dirtyRectRelativeToDirtyFrame.SizeTo(aDirtyFrame->GetSize()); |
michael@0 | 576 | } |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | nsRect dirty = dirtyRectRelativeToDirtyFrame - aFrame->GetOffsetTo(aDirtyFrame); |
michael@0 | 580 | nsRect overflowRect = aFrame->GetVisualOverflowRect(); |
michael@0 | 581 | |
michael@0 | 582 | if (aFrame->IsTransformed() && |
michael@0 | 583 | nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(), |
michael@0 | 584 | eCSSProperty_transform)) { |
michael@0 | 585 | /** |
michael@0 | 586 | * Add a fuzz factor to the overflow rectangle so that elements only just |
michael@0 | 587 | * out of view are pulled into the display list, so they can be |
michael@0 | 588 | * prerendered if necessary. |
michael@0 | 589 | */ |
michael@0 | 590 | overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32)); |
michael@0 | 591 | } |
michael@0 | 592 | |
michael@0 | 593 | if (!dirty.IntersectRect(dirty, overflowRect)) |
michael@0 | 594 | return; |
michael@0 | 595 | const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants(); |
michael@0 | 596 | OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty) |
michael@0 | 597 | : new OutOfFlowDisplayData(dirty); |
michael@0 | 598 | aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data); |
michael@0 | 599 | |
michael@0 | 600 | MarkFrameForDisplay(aFrame, aDirtyFrame); |
michael@0 | 601 | } |
michael@0 | 602 | |
michael@0 | 603 | static void UnmarkFrameForDisplay(nsIFrame* aFrame) { |
michael@0 | 604 | nsPresContext* presContext = aFrame->PresContext(); |
michael@0 | 605 | presContext->PropertyTable()-> |
michael@0 | 606 | Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty()); |
michael@0 | 607 | |
michael@0 | 608 | for (nsIFrame* f = aFrame; f; |
michael@0 | 609 | f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) { |
michael@0 | 610 | if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) |
michael@0 | 611 | return; |
michael@0 | 612 | f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO); |
michael@0 | 613 | } |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | static bool gPrintApzcTree = false; |
michael@0 | 617 | |
michael@0 | 618 | static bool GetApzcTreePrintPref() { |
michael@0 | 619 | static bool initialized = false; |
michael@0 | 620 | if (!initialized) { |
michael@0 | 621 | Preferences::AddBoolVarCache(&gPrintApzcTree, "apz.printtree", gPrintApzcTree); |
michael@0 | 622 | initialized = true; |
michael@0 | 623 | } |
michael@0 | 624 | return gPrintApzcTree; |
michael@0 | 625 | } |
michael@0 | 626 | |
michael@0 | 627 | static void RecordFrameMetrics(nsIFrame* aForFrame, |
michael@0 | 628 | nsIFrame* aScrollFrame, |
michael@0 | 629 | const nsIFrame* aReferenceFrame, |
michael@0 | 630 | ContainerLayer* aRoot, |
michael@0 | 631 | const nsRect& aVisibleRect, |
michael@0 | 632 | const nsRect& aViewport, |
michael@0 | 633 | nsRect* aDisplayPort, |
michael@0 | 634 | nsRect* aCriticalDisplayPort, |
michael@0 | 635 | ViewID aScrollId, |
michael@0 | 636 | bool aIsRoot, |
michael@0 | 637 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 638 | nsPresContext* presContext = aForFrame->PresContext(); |
michael@0 | 639 | int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); |
michael@0 | 640 | LayoutDeviceToLayerScale resolution(aContainerParameters.mXScale, aContainerParameters.mYScale); |
michael@0 | 641 | |
michael@0 | 642 | nsIntRect visible = aVisibleRect.ScaleToNearestPixels( |
michael@0 | 643 | resolution.scale, resolution.scale, auPerDevPixel); |
michael@0 | 644 | aRoot->SetVisibleRegion(visible); |
michael@0 | 645 | |
michael@0 | 646 | FrameMetrics metrics; |
michael@0 | 647 | metrics.mViewport = CSSRect::FromAppUnits(aViewport); |
michael@0 | 648 | if (aDisplayPort) { |
michael@0 | 649 | metrics.mDisplayPort = CSSRect::FromAppUnits(*aDisplayPort); |
michael@0 | 650 | if (aCriticalDisplayPort) { |
michael@0 | 651 | metrics.mCriticalDisplayPort = CSSRect::FromAppUnits(*aCriticalDisplayPort); |
michael@0 | 652 | } |
michael@0 | 653 | } |
michael@0 | 654 | |
michael@0 | 655 | nsIScrollableFrame* scrollableFrame = nullptr; |
michael@0 | 656 | if (aScrollFrame) |
michael@0 | 657 | scrollableFrame = aScrollFrame->GetScrollTargetFrame(); |
michael@0 | 658 | |
michael@0 | 659 | metrics.mScrollableRect = CSSRect::FromAppUnits( |
michael@0 | 660 | nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame)); |
michael@0 | 661 | |
michael@0 | 662 | if (scrollableFrame) { |
michael@0 | 663 | nsPoint scrollPosition = scrollableFrame->GetScrollPosition(); |
michael@0 | 664 | metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition)); |
michael@0 | 665 | |
michael@0 | 666 | // If the frame was scrolled since the last layers update, and by |
michael@0 | 667 | // something other than the APZ code, we want to tell the APZ to update |
michael@0 | 668 | // its scroll offset. |
michael@0 | 669 | nsIAtom* originOfLastScroll = scrollableFrame->OriginOfLastScroll(); |
michael@0 | 670 | if (originOfLastScroll && originOfLastScroll != nsGkAtoms::apz) { |
michael@0 | 671 | metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration()); |
michael@0 | 672 | } |
michael@0 | 673 | } |
michael@0 | 674 | |
michael@0 | 675 | metrics.SetScrollId(aScrollId); |
michael@0 | 676 | metrics.mIsRoot = aIsRoot; |
michael@0 | 677 | |
michael@0 | 678 | // Only the root scrollable frame for a given presShell should pick up |
michael@0 | 679 | // the presShell's resolution. All the other frames are 1.0. |
michael@0 | 680 | nsIPresShell* presShell = presContext->GetPresShell(); |
michael@0 | 681 | if (aScrollFrame == presShell->GetRootScrollFrame()) { |
michael@0 | 682 | metrics.mResolution = ParentLayerToLayerScale(presShell->GetXResolution(), |
michael@0 | 683 | presShell->GetYResolution()); |
michael@0 | 684 | } else { |
michael@0 | 685 | metrics.mResolution = ParentLayerToLayerScale(1.0f); |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | // For the cumulateive resolution, multiply the resolutions of all the |
michael@0 | 689 | // presShells back up to the root |
michael@0 | 690 | metrics.mCumulativeResolution = LayoutDeviceToLayerScale(1.0f); |
michael@0 | 691 | nsIPresShell* curPresShell = presShell; |
michael@0 | 692 | while (curPresShell != nullptr) { |
michael@0 | 693 | ParentLayerToLayerScale presShellResolution(curPresShell->GetXResolution(), |
michael@0 | 694 | curPresShell->GetYResolution()); |
michael@0 | 695 | metrics.mCumulativeResolution.scale *= presShellResolution.scale; |
michael@0 | 696 | nsPresContext* parentContext = curPresShell->GetPresContext()->GetParentPresContext(); |
michael@0 | 697 | curPresShell = parentContext ? parentContext->GetPresShell() : nullptr; |
michael@0 | 698 | } |
michael@0 | 699 | |
michael@0 | 700 | metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale( |
michael@0 | 701 | (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel); |
michael@0 | 702 | |
michael@0 | 703 | // Initially, AsyncPanZoomController should render the content to the screen |
michael@0 | 704 | // at the painted resolution. |
michael@0 | 705 | const LayerToScreenScale layerToScreenScale(1.0f); |
michael@0 | 706 | metrics.SetZoom(metrics.mCumulativeResolution * metrics.mDevPixelsPerCSSPixel |
michael@0 | 707 | * layerToScreenScale); |
michael@0 | 708 | |
michael@0 | 709 | if (presShell) { |
michael@0 | 710 | nsIDocument* document = nullptr; |
michael@0 | 711 | document = presShell->GetDocument(); |
michael@0 | 712 | if (document) { |
michael@0 | 713 | nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow()); |
michael@0 | 714 | if (innerWin) { |
michael@0 | 715 | metrics.mMayHaveTouchListeners = innerWin->HasTouchEventListeners(); |
michael@0 | 716 | } |
michael@0 | 717 | } |
michael@0 | 718 | } |
michael@0 | 719 | |
michael@0 | 720 | LayoutDeviceToParentLayerScale layoutToParentLayerScale = |
michael@0 | 721 | // The ScreenToParentLayerScale should be mTransformScale which is not calculated yet, |
michael@0 | 722 | // but we don't yet handle CSS transforms, so we assume it's 1 here. |
michael@0 | 723 | metrics.mCumulativeResolution * LayerToScreenScale(1.0) * ScreenToParentLayerScale(1.0); |
michael@0 | 724 | |
michael@0 | 725 | // Calculate the composition bounds as the size of the scroll frame and |
michael@0 | 726 | // its origin relative to the reference frame. |
michael@0 | 727 | // If aScrollFrame is null, we are in a document without a root scroll frame, |
michael@0 | 728 | // so it's a xul document. In this case, use the size of the viewport frame. |
michael@0 | 729 | nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame; |
michael@0 | 730 | nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame), |
michael@0 | 731 | frameForCompositionBoundsCalculation->GetSize()); |
michael@0 | 732 | metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel) |
michael@0 | 733 | * layoutToParentLayerScale); |
michael@0 | 734 | |
michael@0 | 735 | |
michael@0 | 736 | // For the root scroll frame of the root content document, the above calculation |
michael@0 | 737 | // will yield the size of the viewport frame as the composition bounds, which |
michael@0 | 738 | // doesn't actually correspond to what is visible when |
michael@0 | 739 | // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of |
michael@0 | 740 | // the prescontext that the viewport frame is reflowed into. In that case if our |
michael@0 | 741 | // document has a widget then the widget's bounds will correspond to what is |
michael@0 | 742 | // visible. If we don't have a widget the root view's bounds correspond to what |
michael@0 | 743 | // would be visible because they don't get modified by setCSSViewport. |
michael@0 | 744 | bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument() |
michael@0 | 745 | && aScrollFrame == presShell->GetRootScrollFrame(); |
michael@0 | 746 | if (isRootContentDocRootScrollFrame) { |
michael@0 | 747 | if (nsIFrame* rootFrame = presShell->GetRootFrame()) { |
michael@0 | 748 | if (nsView* view = rootFrame->GetView()) { |
michael@0 | 749 | nsRect viewBoundsAppUnits = view->GetBounds() + rootFrame->GetOffsetToCrossDoc(aReferenceFrame); |
michael@0 | 750 | ParentLayerIntRect viewBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(viewBoundsAppUnits, auPerDevPixel) |
michael@0 | 751 | * layoutToParentLayerScale); |
michael@0 | 752 | |
michael@0 | 753 | // On Android, we need to do things a bit differently to get things |
michael@0 | 754 | // right (see bug 983208, bug 988882). We use the bounds of the nearest |
michael@0 | 755 | // widget, but clamp the height to the view bounds height. This clamping |
michael@0 | 756 | // is done to get correct results for a page where the page is sized to |
michael@0 | 757 | // the screen and thus the dynamic toolbar never disappears. In such a |
michael@0 | 758 | // case, we want the composition bounds to exclude the toolbar height, |
michael@0 | 759 | // but the widget bounds includes it. We don't currently have a good way |
michael@0 | 760 | // of knowing about the toolbar height, but clamping to the view bounds |
michael@0 | 761 | // height gives the correct answer in the cases we care about. |
michael@0 | 762 | nsIWidget* widget = |
michael@0 | 763 | #ifdef MOZ_WIDGET_ANDROID |
michael@0 | 764 | rootFrame->GetNearestWidget(); |
michael@0 | 765 | #else |
michael@0 | 766 | view->GetWidget(); |
michael@0 | 767 | #endif |
michael@0 | 768 | if (widget) { |
michael@0 | 769 | nsIntRect widgetBounds; |
michael@0 | 770 | widget->GetBounds(widgetBounds); |
michael@0 | 771 | metrics.mCompositionBounds = ViewAs<ParentLayerPixel>(widgetBounds); |
michael@0 | 772 | #ifdef MOZ_WIDGET_ANDROID |
michael@0 | 773 | if (viewBounds.height < metrics.mCompositionBounds.height) { |
michael@0 | 774 | metrics.mCompositionBounds.height = viewBounds.height; |
michael@0 | 775 | } |
michael@0 | 776 | #endif |
michael@0 | 777 | } else { |
michael@0 | 778 | metrics.mCompositionBounds = viewBounds; |
michael@0 | 779 | } |
michael@0 | 780 | } |
michael@0 | 781 | } |
michael@0 | 782 | } |
michael@0 | 783 | |
michael@0 | 784 | // Adjust composition bounds for the size of scroll bars. |
michael@0 | 785 | if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { |
michael@0 | 786 | nsMargin sizes = scrollableFrame->GetActualScrollbarSizes(); |
michael@0 | 787 | // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them. |
michael@0 | 788 | ParentLayerIntMargin boundMargins = RoundedToInt(CSSMargin::FromAppUnits(sizes) * CSSToParentLayerScale(1.0f)); |
michael@0 | 789 | metrics.mCompositionBounds.Deflate(boundMargins); |
michael@0 | 790 | } |
michael@0 | 791 | |
michael@0 | 792 | metrics.SetRootCompositionSize( |
michael@0 | 793 | nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame, |
michael@0 | 794 | isRootContentDocRootScrollFrame, metrics)); |
michael@0 | 795 | |
michael@0 | 796 | if (GetApzcTreePrintPref()) { |
michael@0 | 797 | if (nsIContent* content = frameForCompositionBoundsCalculation->GetContent()) { |
michael@0 | 798 | nsAutoString contentDescription; |
michael@0 | 799 | content->Describe(contentDescription); |
michael@0 | 800 | metrics.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription).get()); |
michael@0 | 801 | } |
michael@0 | 802 | } |
michael@0 | 803 | |
michael@0 | 804 | metrics.mPresShellId = presShell->GetPresShellId(); |
michael@0 | 805 | |
michael@0 | 806 | // If the scroll frame's content is marked 'scrollgrab', record this |
michael@0 | 807 | // in the FrameMetrics so APZ knows to provide the scroll grabbing |
michael@0 | 808 | // behaviour. |
michael@0 | 809 | if (aScrollFrame && nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())) { |
michael@0 | 810 | metrics.mHasScrollgrab = true; |
michael@0 | 811 | } |
michael@0 | 812 | |
michael@0 | 813 | aRoot->SetFrameMetrics(metrics); |
michael@0 | 814 | } |
michael@0 | 815 | |
michael@0 | 816 | nsDisplayListBuilder::~nsDisplayListBuilder() { |
michael@0 | 817 | NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0, |
michael@0 | 818 | "All frames should have been unmarked"); |
michael@0 | 819 | NS_ASSERTION(mPresShellStates.Length() == 0, |
michael@0 | 820 | "All presshells should have been exited"); |
michael@0 | 821 | NS_ASSERTION(!mCurrentTableItem, "No table item should be active"); |
michael@0 | 822 | |
michael@0 | 823 | nsCSSRendering::EndFrameTreesLocked(); |
michael@0 | 824 | |
michael@0 | 825 | for (uint32_t i = 0; i < mDisplayItemClipsToDestroy.Length(); ++i) { |
michael@0 | 826 | mDisplayItemClipsToDestroy[i]->DisplayItemClip::~DisplayItemClip(); |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | PL_FinishArenaPool(&mPool); |
michael@0 | 830 | MOZ_COUNT_DTOR(nsDisplayListBuilder); |
michael@0 | 831 | } |
michael@0 | 832 | |
michael@0 | 833 | uint32_t |
michael@0 | 834 | nsDisplayListBuilder::GetBackgroundPaintFlags() { |
michael@0 | 835 | uint32_t flags = 0; |
michael@0 | 836 | if (mSyncDecodeImages) { |
michael@0 | 837 | flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES; |
michael@0 | 838 | } |
michael@0 | 839 | if (mIsPaintingToWindow) { |
michael@0 | 840 | flags |= nsCSSRendering::PAINTBG_TO_WINDOW; |
michael@0 | 841 | } |
michael@0 | 842 | return flags; |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | void |
michael@0 | 846 | nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion, |
michael@0 | 847 | const nsRegion& aRegion) |
michael@0 | 848 | { |
michael@0 | 849 | if (aRegion.IsEmpty()) |
michael@0 | 850 | return; |
michael@0 | 851 | |
michael@0 | 852 | nsRegion tmp; |
michael@0 | 853 | tmp.Sub(*aVisibleRegion, aRegion); |
michael@0 | 854 | // Don't let *aVisibleRegion get too complex, but don't let it fluff out |
michael@0 | 855 | // to its bounds either, which can be very bad (see bug 516740). |
michael@0 | 856 | // Do let aVisibleRegion get more complex if by doing so we reduce its |
michael@0 | 857 | // area by at least half. |
michael@0 | 858 | if (GetAccurateVisibleRegions() || tmp.GetNumRects() <= 15 || |
michael@0 | 859 | tmp.Area() <= aVisibleRegion->Area()/2) { |
michael@0 | 860 | *aVisibleRegion = tmp; |
michael@0 | 861 | } |
michael@0 | 862 | } |
michael@0 | 863 | |
michael@0 | 864 | nsCaret * |
michael@0 | 865 | nsDisplayListBuilder::GetCaret() { |
michael@0 | 866 | nsRefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret(); |
michael@0 | 867 | return caret; |
michael@0 | 868 | } |
michael@0 | 869 | |
michael@0 | 870 | void |
michael@0 | 871 | nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame, |
michael@0 | 872 | const nsRect& aDirtyRect) { |
michael@0 | 873 | PresShellState* state = mPresShellStates.AppendElement(); |
michael@0 | 874 | if (!state) |
michael@0 | 875 | return; |
michael@0 | 876 | state->mPresShell = aReferenceFrame->PresContext()->PresShell(); |
michael@0 | 877 | state->mCaretFrame = nullptr; |
michael@0 | 878 | state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length(); |
michael@0 | 879 | |
michael@0 | 880 | state->mPresShell->UpdateCanvasBackground(); |
michael@0 | 881 | |
michael@0 | 882 | if (mIsPaintingToWindow) { |
michael@0 | 883 | mReferenceFrame->AddPaintedPresShell(state->mPresShell); |
michael@0 | 884 | |
michael@0 | 885 | state->mPresShell->IncrementPaintCount(); |
michael@0 | 886 | } |
michael@0 | 887 | |
michael@0 | 888 | bool buildCaret = mBuildCaret; |
michael@0 | 889 | if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) { |
michael@0 | 890 | if (state->mPresShell->IsPaintingSuppressed()) { |
michael@0 | 891 | mHadToIgnoreSuppression = true; |
michael@0 | 892 | } |
michael@0 | 893 | state->mIsBackgroundOnly = false; |
michael@0 | 894 | } else { |
michael@0 | 895 | state->mIsBackgroundOnly = true; |
michael@0 | 896 | buildCaret = false; |
michael@0 | 897 | } |
michael@0 | 898 | |
michael@0 | 899 | if (!buildCaret) |
michael@0 | 900 | return; |
michael@0 | 901 | |
michael@0 | 902 | nsRefPtr<nsCaret> caret = state->mPresShell->GetCaret(); |
michael@0 | 903 | state->mCaretFrame = caret->GetCaretFrame(); |
michael@0 | 904 | NS_ASSERTION(state->mCaretFrame == caret->GetCaretFrame(), |
michael@0 | 905 | "GetCaretFrame() is unstable"); |
michael@0 | 906 | |
michael@0 | 907 | if (state->mCaretFrame) { |
michael@0 | 908 | // Check if the dirty rect intersects with the caret's dirty rect. |
michael@0 | 909 | nsRect caretRect = |
michael@0 | 910 | caret->GetCaretRect() + state->mCaretFrame->GetOffsetTo(aReferenceFrame); |
michael@0 | 911 | if (caretRect.Intersects(aDirtyRect)) { |
michael@0 | 912 | // Okay, our rects intersect, let's mark the frame and all of its ancestors. |
michael@0 | 913 | mFramesMarkedForDisplay.AppendElement(state->mCaretFrame); |
michael@0 | 914 | MarkFrameForDisplay(state->mCaretFrame, nullptr); |
michael@0 | 915 | } |
michael@0 | 916 | } |
michael@0 | 917 | } |
michael@0 | 918 | |
michael@0 | 919 | void |
michael@0 | 920 | nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame, |
michael@0 | 921 | const nsRect& aDirtyRect) { |
michael@0 | 922 | if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) { |
michael@0 | 923 | // Must have not allocated a state for this presshell, presumably due |
michael@0 | 924 | // to OOM. |
michael@0 | 925 | return; |
michael@0 | 926 | } |
michael@0 | 927 | |
michael@0 | 928 | ResetMarkedFramesForDisplayList(); |
michael@0 | 929 | mPresShellStates.SetLength(mPresShellStates.Length() - 1); |
michael@0 | 930 | } |
michael@0 | 931 | |
michael@0 | 932 | void |
michael@0 | 933 | nsDisplayListBuilder::ResetMarkedFramesForDisplayList() |
michael@0 | 934 | { |
michael@0 | 935 | // Unmark and pop off the frames marked for display in this pres shell. |
michael@0 | 936 | uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay; |
michael@0 | 937 | for (uint32_t i = firstFrameForShell; |
michael@0 | 938 | i < mFramesMarkedForDisplay.Length(); ++i) { |
michael@0 | 939 | UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]); |
michael@0 | 940 | } |
michael@0 | 941 | mFramesMarkedForDisplay.SetLength(firstFrameForShell); |
michael@0 | 942 | } |
michael@0 | 943 | |
michael@0 | 944 | void |
michael@0 | 945 | nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, |
michael@0 | 946 | const nsFrameList& aFrames, |
michael@0 | 947 | const nsRect& aDirtyRect) { |
michael@0 | 948 | mFramesMarkedForDisplay.SetCapacity(mFramesMarkedForDisplay.Length() + aFrames.GetLength()); |
michael@0 | 949 | for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) { |
michael@0 | 950 | mFramesMarkedForDisplay.AppendElement(e.get()); |
michael@0 | 951 | MarkOutOfFlowFrameForDisplay(aDirtyFrame, e.get(), aDirtyRect); |
michael@0 | 952 | } |
michael@0 | 953 | } |
michael@0 | 954 | |
michael@0 | 955 | void |
michael@0 | 956 | nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect) |
michael@0 | 957 | { |
michael@0 | 958 | nsAutoTArray<nsIFrame::ChildList,4> childListArray; |
michael@0 | 959 | aDirtyFrame->GetChildLists(&childListArray); |
michael@0 | 960 | nsIFrame::ChildListArrayIterator lists(childListArray); |
michael@0 | 961 | for (; !lists.IsDone(); lists.Next()) { |
michael@0 | 962 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
michael@0 | 963 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
michael@0 | 964 | nsIFrame *child = childFrames.get(); |
michael@0 | 965 | if (child->Preserves3D()) { |
michael@0 | 966 | mFramesMarkedForDisplay.AppendElement(child); |
michael@0 | 967 | nsRect dirty = aDirtyRect - child->GetOffsetTo(aDirtyFrame); |
michael@0 | 968 | |
michael@0 | 969 | child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(), |
michael@0 | 970 | new nsRect(dirty)); |
michael@0 | 971 | |
michael@0 | 972 | MarkFrameForDisplay(child, aDirtyFrame); |
michael@0 | 973 | } |
michael@0 | 974 | } |
michael@0 | 975 | } |
michael@0 | 976 | } |
michael@0 | 977 | |
michael@0 | 978 | void* |
michael@0 | 979 | nsDisplayListBuilder::Allocate(size_t aSize) { |
michael@0 | 980 | void *tmp; |
michael@0 | 981 | PL_ARENA_ALLOCATE(tmp, &mPool, aSize); |
michael@0 | 982 | if (!tmp) { |
michael@0 | 983 | NS_RUNTIMEABORT("out of memory"); |
michael@0 | 984 | } |
michael@0 | 985 | return tmp; |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | const DisplayItemClip* |
michael@0 | 989 | nsDisplayListBuilder::AllocateDisplayItemClip(const DisplayItemClip& aOriginal) |
michael@0 | 990 | { |
michael@0 | 991 | void* p = Allocate(sizeof(DisplayItemClip)); |
michael@0 | 992 | if (!aOriginal.GetRoundedRectCount()) { |
michael@0 | 993 | memcpy(p, &aOriginal, sizeof(DisplayItemClip)); |
michael@0 | 994 | return static_cast<DisplayItemClip*>(p); |
michael@0 | 995 | } |
michael@0 | 996 | |
michael@0 | 997 | DisplayItemClip* c = new (p) DisplayItemClip(aOriginal); |
michael@0 | 998 | mDisplayItemClipsToDestroy.AppendElement(c); |
michael@0 | 999 | return c; |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const |
michael@0 | 1003 | { |
michael@0 | 1004 | aDestination.BorderBackground()->AppendToTop(BorderBackground()); |
michael@0 | 1005 | aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds()); |
michael@0 | 1006 | aDestination.Floats()->AppendToTop(Floats()); |
michael@0 | 1007 | aDestination.Content()->AppendToTop(Content()); |
michael@0 | 1008 | aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants()); |
michael@0 | 1009 | aDestination.Outlines()->AppendToTop(Outlines()); |
michael@0 | 1010 | } |
michael@0 | 1011 | |
michael@0 | 1012 | void |
michael@0 | 1013 | nsDisplayList::FlattenTo(nsTArray<nsDisplayItem*>* aElements) { |
michael@0 | 1014 | nsDisplayItem* item; |
michael@0 | 1015 | while ((item = RemoveBottom()) != nullptr) { |
michael@0 | 1016 | if (item->GetType() == nsDisplayItem::TYPE_WRAP_LIST) { |
michael@0 | 1017 | item->GetSameCoordinateSystemChildren()->FlattenTo(aElements); |
michael@0 | 1018 | item->~nsDisplayItem(); |
michael@0 | 1019 | } else { |
michael@0 | 1020 | aElements->AppendElement(item); |
michael@0 | 1021 | } |
michael@0 | 1022 | } |
michael@0 | 1023 | } |
michael@0 | 1024 | |
michael@0 | 1025 | nsRect |
michael@0 | 1026 | nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const { |
michael@0 | 1027 | nsRect bounds; |
michael@0 | 1028 | for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) { |
michael@0 | 1029 | bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder)); |
michael@0 | 1030 | } |
michael@0 | 1031 | return bounds; |
michael@0 | 1032 | } |
michael@0 | 1033 | |
michael@0 | 1034 | bool |
michael@0 | 1035 | nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder, |
michael@0 | 1036 | nsRegion* aVisibleRegion, |
michael@0 | 1037 | nsIFrame* aDisplayPortFrame) { |
michael@0 | 1038 | PROFILER_LABEL("nsDisplayList", "ComputeVisibilityForRoot"); |
michael@0 | 1039 | nsRegion r; |
michael@0 | 1040 | r.And(*aVisibleRegion, GetBounds(aBuilder)); |
michael@0 | 1041 | return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, |
michael@0 | 1042 | r.GetBounds(), r.GetBounds(), |
michael@0 | 1043 | aDisplayPortFrame); |
michael@0 | 1044 | } |
michael@0 | 1045 | |
michael@0 | 1046 | static nsRegion |
michael@0 | 1047 | TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) |
michael@0 | 1048 | { |
michael@0 | 1049 | bool snap; |
michael@0 | 1050 | nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap); |
michael@0 | 1051 | if (aBuilder->IsForPluginGeometry()) { |
michael@0 | 1052 | // Treat all leaf chrome items as opaque, unless their frames are opacity:0. |
michael@0 | 1053 | // Since opacity:0 frames generate an nsDisplayOpacity, that item will |
michael@0 | 1054 | // not be treated as opaque here, so opacity:0 chrome content will be |
michael@0 | 1055 | // effectively ignored, as it should be. |
michael@0 | 1056 | // We treat leaf chrome items as opaque to ensure that they cover |
michael@0 | 1057 | // content plugins, for security reasons. |
michael@0 | 1058 | // Non-leaf chrome items don't render contents of their own so shouldn't |
michael@0 | 1059 | // be treated as opaque (and their bounds is just the union of their |
michael@0 | 1060 | // children, which might be a large area their contents don't really cover). |
michael@0 | 1061 | nsIFrame* f = aItem->Frame(); |
michael@0 | 1062 | if (f->PresContext()->IsChrome() && !aItem->GetChildren() && |
michael@0 | 1063 | f->StyleDisplay()->mOpacity != 0.0) { |
michael@0 | 1064 | opaque = aItem->GetBounds(aBuilder, &snap); |
michael@0 | 1065 | } |
michael@0 | 1066 | } |
michael@0 | 1067 | if (opaque.IsEmpty()) { |
michael@0 | 1068 | return opaque; |
michael@0 | 1069 | } |
michael@0 | 1070 | nsRegion opaqueClipped; |
michael@0 | 1071 | nsRegionRectIterator iter(opaque); |
michael@0 | 1072 | for (const nsRect* r = iter.Next(); r; r = iter.Next()) { |
michael@0 | 1073 | opaqueClipped.Or(opaqueClipped, aItem->GetClip().ApproximateIntersectInward(*r)); |
michael@0 | 1074 | } |
michael@0 | 1075 | return opaqueClipped; |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | /* Checks if aPotentialScrollItem is a scroll layer item and aPotentialScrollbarItem |
michael@0 | 1079 | * is an overlay scrollbar item for the same scroll frame. |
michael@0 | 1080 | */ |
michael@0 | 1081 | static bool |
michael@0 | 1082 | IsScrollLayerItemAndOverlayScrollbarForScrollFrame( |
michael@0 | 1083 | nsDisplayItem* aPotentialScrollItem, nsDisplayItem* aPotentialScrollbarItem) |
michael@0 | 1084 | { |
michael@0 | 1085 | if (aPotentialScrollItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER && |
michael@0 | 1086 | aPotentialScrollbarItem && |
michael@0 | 1087 | aPotentialScrollbarItem->GetType() == nsDisplayItem::TYPE_OWN_LAYER && |
michael@0 | 1088 | LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { |
michael@0 | 1089 | nsDisplayScrollLayer* scrollItem = |
michael@0 | 1090 | static_cast<nsDisplayScrollLayer*>(aPotentialScrollItem); |
michael@0 | 1091 | nsDisplayOwnLayer* layerItem = |
michael@0 | 1092 | static_cast<nsDisplayOwnLayer*>(aPotentialScrollbarItem); |
michael@0 | 1093 | if ((layerItem->GetFlags() & |
michael@0 | 1094 | (nsDisplayOwnLayer::VERTICAL_SCROLLBAR | |
michael@0 | 1095 | nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR)) && |
michael@0 | 1096 | layerItem->Frame()->GetParent() == scrollItem->GetScrollFrame()) { |
michael@0 | 1097 | return true; |
michael@0 | 1098 | } |
michael@0 | 1099 | } |
michael@0 | 1100 | return false; |
michael@0 | 1101 | } |
michael@0 | 1102 | |
michael@0 | 1103 | bool |
michael@0 | 1104 | nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, |
michael@0 | 1105 | nsRegion* aVisibleRegion, |
michael@0 | 1106 | const nsRect& aListVisibleBounds, |
michael@0 | 1107 | const nsRect& aAllowVisibleRegionExpansion, |
michael@0 | 1108 | nsIFrame* aDisplayPortFrame) { |
michael@0 | 1109 | #ifdef DEBUG |
michael@0 | 1110 | nsRegion r; |
michael@0 | 1111 | r.And(*aVisibleRegion, GetBounds(aBuilder)); |
michael@0 | 1112 | NS_ASSERTION(r.GetBounds().IsEqualInterior(aListVisibleBounds), |
michael@0 | 1113 | "bad aListVisibleBounds"); |
michael@0 | 1114 | #endif |
michael@0 | 1115 | |
michael@0 | 1116 | mVisibleRect = aListVisibleBounds; |
michael@0 | 1117 | bool anyVisible = false; |
michael@0 | 1118 | |
michael@0 | 1119 | nsAutoTArray<nsDisplayItem*, 512> elements; |
michael@0 | 1120 | FlattenTo(&elements); |
michael@0 | 1121 | |
michael@0 | 1122 | bool forceTransparentSurface = false; |
michael@0 | 1123 | |
michael@0 | 1124 | for (int32_t i = elements.Length() - 1; i >= 0; --i) { |
michael@0 | 1125 | nsDisplayItem* item = elements[i]; |
michael@0 | 1126 | nsDisplayItem* belowItem = i < 1 ? nullptr : elements[i - 1]; |
michael@0 | 1127 | |
michael@0 | 1128 | nsDisplayList* list = item->GetSameCoordinateSystemChildren(); |
michael@0 | 1129 | if (aBuilder->AllowMergingAndFlattening()) { |
michael@0 | 1130 | if (belowItem && item->TryMerge(aBuilder, belowItem)) { |
michael@0 | 1131 | belowItem->~nsDisplayItem(); |
michael@0 | 1132 | elements.ReplaceElementsAt(i - 1, 1, item); |
michael@0 | 1133 | continue; |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | // If an overlay scrollbar item is between a scroll layer item and the |
michael@0 | 1137 | // other scroll layer items that we need to merge with just move the |
michael@0 | 1138 | // scrollbar item up, that way it will be on top of the scrolled content |
michael@0 | 1139 | // and we can try to merge all the scroll layer items. |
michael@0 | 1140 | if (IsScrollLayerItemAndOverlayScrollbarForScrollFrame(item, belowItem)) { |
michael@0 | 1141 | elements[i] = belowItem; |
michael@0 | 1142 | elements[i-1] = item; |
michael@0 | 1143 | i++; |
michael@0 | 1144 | continue; |
michael@0 | 1145 | } |
michael@0 | 1146 | |
michael@0 | 1147 | if (list && item->ShouldFlattenAway(aBuilder)) { |
michael@0 | 1148 | // The elements on the list >= i no longer serve any use. |
michael@0 | 1149 | elements.SetLength(i); |
michael@0 | 1150 | list->FlattenTo(&elements); |
michael@0 | 1151 | i = elements.Length(); |
michael@0 | 1152 | item->~nsDisplayItem(); |
michael@0 | 1153 | continue; |
michael@0 | 1154 | } |
michael@0 | 1155 | } |
michael@0 | 1156 | |
michael@0 | 1157 | nsRect bounds = item->GetClippedBounds(aBuilder); |
michael@0 | 1158 | |
michael@0 | 1159 | nsRegion itemVisible; |
michael@0 | 1160 | itemVisible.And(*aVisibleRegion, bounds); |
michael@0 | 1161 | item->mVisibleRect = itemVisible.GetBounds(); |
michael@0 | 1162 | |
michael@0 | 1163 | if (item->ComputeVisibility(aBuilder, aVisibleRegion, |
michael@0 | 1164 | aAllowVisibleRegionExpansion.Intersect(bounds))) { |
michael@0 | 1165 | anyVisible = true; |
michael@0 | 1166 | |
michael@0 | 1167 | // If we're in a displayport, we need to make sure that fixed position |
michael@0 | 1168 | // items do not subtract from the visible region, as async scrolling |
michael@0 | 1169 | // may expose these occluded areas. |
michael@0 | 1170 | // If the item is fixed pos in the same document as the displayport |
michael@0 | 1171 | // then don't let it occlude this list. The only other case possible |
michael@0 | 1172 | // is that the fixed pos content is in a child document, in which it |
michael@0 | 1173 | // would scroll with the rest of the content. |
michael@0 | 1174 | bool occlude = true; |
michael@0 | 1175 | if (aDisplayPortFrame && item->IsInFixedPos()) { |
michael@0 | 1176 | if (item->Frame()->PresContext() == aDisplayPortFrame->PresContext()) { |
michael@0 | 1177 | occlude = false; |
michael@0 | 1178 | } |
michael@0 | 1179 | } |
michael@0 | 1180 | |
michael@0 | 1181 | if (occlude) { |
michael@0 | 1182 | nsRegion opaque = TreatAsOpaque(item, aBuilder); |
michael@0 | 1183 | // Subtract opaque item from the visible region |
michael@0 | 1184 | aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); |
michael@0 | 1185 | } |
michael@0 | 1186 | |
michael@0 | 1187 | if (aBuilder->NeedToForceTransparentSurfaceForItem(item) || |
michael@0 | 1188 | (list && list->NeedsTransparentSurface())) { |
michael@0 | 1189 | forceTransparentSurface = true; |
michael@0 | 1190 | } |
michael@0 | 1191 | } |
michael@0 | 1192 | AppendToBottom(item); |
michael@0 | 1193 | } |
michael@0 | 1194 | |
michael@0 | 1195 | mIsOpaque = !aVisibleRegion->Intersects(mVisibleRect); |
michael@0 | 1196 | mForceTransparentSurface = forceTransparentSurface; |
michael@0 | 1197 | #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING) |
michael@0 | 1198 | mDidComputeVisibility = true; |
michael@0 | 1199 | #endif |
michael@0 | 1200 | return anyVisible; |
michael@0 | 1201 | } |
michael@0 | 1202 | |
michael@0 | 1203 | void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, |
michael@0 | 1204 | nsRenderingContext* aCtx, |
michael@0 | 1205 | uint32_t aFlags) const { |
michael@0 | 1206 | PROFILER_LABEL("nsDisplayList", "PaintRoot"); |
michael@0 | 1207 | PaintForFrame(aBuilder, aCtx, aBuilder->RootReferenceFrame(), aFlags); |
michael@0 | 1208 | } |
michael@0 | 1209 | |
michael@0 | 1210 | /** |
michael@0 | 1211 | * We paint by executing a layer manager transaction, constructing a |
michael@0 | 1212 | * single layer representing the display list, and then making it the |
michael@0 | 1213 | * root of the layer manager, drawing into the ThebesLayers. |
michael@0 | 1214 | */ |
michael@0 | 1215 | void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder, |
michael@0 | 1216 | nsRenderingContext* aCtx, |
michael@0 | 1217 | nsIFrame* aForFrame, |
michael@0 | 1218 | uint32_t aFlags) const { |
michael@0 | 1219 | NS_ASSERTION(mDidComputeVisibility, |
michael@0 | 1220 | "Must call ComputeVisibility before calling Paint"); |
michael@0 | 1221 | |
michael@0 | 1222 | nsRefPtr<LayerManager> layerManager; |
michael@0 | 1223 | bool widgetTransaction = false; |
michael@0 | 1224 | bool allowRetaining = false; |
michael@0 | 1225 | bool doBeginTransaction = true; |
michael@0 | 1226 | nsView *view = nullptr; |
michael@0 | 1227 | if (aFlags & PAINT_USE_WIDGET_LAYERS) { |
michael@0 | 1228 | nsIFrame* rootReferenceFrame = aBuilder->RootReferenceFrame(); |
michael@0 | 1229 | view = rootReferenceFrame->GetView(); |
michael@0 | 1230 | NS_ASSERTION(rootReferenceFrame == nsLayoutUtils::GetDisplayRootFrame(rootReferenceFrame), |
michael@0 | 1231 | "Reference frame must be a display root for us to use the layer manager"); |
michael@0 | 1232 | nsIWidget* window = rootReferenceFrame->GetNearestWidget(); |
michael@0 | 1233 | if (window) { |
michael@0 | 1234 | layerManager = window->GetLayerManager(&allowRetaining); |
michael@0 | 1235 | if (layerManager) { |
michael@0 | 1236 | doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION); |
michael@0 | 1237 | widgetTransaction = true; |
michael@0 | 1238 | } |
michael@0 | 1239 | } |
michael@0 | 1240 | } |
michael@0 | 1241 | if (!layerManager) { |
michael@0 | 1242 | if (!aCtx) { |
michael@0 | 1243 | NS_WARNING("Nowhere to paint into"); |
michael@0 | 1244 | return; |
michael@0 | 1245 | } |
michael@0 | 1246 | layerManager = new BasicLayerManager(); |
michael@0 | 1247 | } |
michael@0 | 1248 | |
michael@0 | 1249 | // Store the existing layer builder to reinstate it on return. |
michael@0 | 1250 | FrameLayerBuilder *oldBuilder = layerManager->GetLayerBuilder(); |
michael@0 | 1251 | |
michael@0 | 1252 | FrameLayerBuilder *layerBuilder = new FrameLayerBuilder(); |
michael@0 | 1253 | layerBuilder->Init(aBuilder, layerManager); |
michael@0 | 1254 | |
michael@0 | 1255 | if (aFlags & PAINT_COMPRESSED) { |
michael@0 | 1256 | layerBuilder->SetLayerTreeCompressionMode(); |
michael@0 | 1257 | } |
michael@0 | 1258 | |
michael@0 | 1259 | if (aFlags & PAINT_FLUSH_LAYERS) { |
michael@0 | 1260 | FrameLayerBuilder::InvalidateAllLayers(layerManager); |
michael@0 | 1261 | } |
michael@0 | 1262 | |
michael@0 | 1263 | if (doBeginTransaction) { |
michael@0 | 1264 | if (aCtx) { |
michael@0 | 1265 | layerManager->BeginTransactionWithTarget(aCtx->ThebesContext()); |
michael@0 | 1266 | } else { |
michael@0 | 1267 | layerManager->BeginTransaction(); |
michael@0 | 1268 | } |
michael@0 | 1269 | } |
michael@0 | 1270 | if (widgetTransaction) { |
michael@0 | 1271 | layerBuilder->DidBeginRetainedLayerTransaction(layerManager); |
michael@0 | 1272 | } |
michael@0 | 1273 | |
michael@0 | 1274 | nsPresContext* presContext = aForFrame->PresContext(); |
michael@0 | 1275 | nsIPresShell* presShell = presContext->GetPresShell(); |
michael@0 | 1276 | |
michael@0 | 1277 | NotifySubDocInvalidationFunc computeInvalidFunc = |
michael@0 | 1278 | presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0; |
michael@0 | 1279 | bool computeInvalidRect = (computeInvalidFunc || |
michael@0 | 1280 | (!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) && |
michael@0 | 1281 | widgetTransaction; |
michael@0 | 1282 | |
michael@0 | 1283 | nsAutoPtr<LayerProperties> props(computeInvalidRect ? |
michael@0 | 1284 | LayerProperties::CloneFrom(layerManager->GetRoot()) : |
michael@0 | 1285 | nullptr); |
michael@0 | 1286 | |
michael@0 | 1287 | ContainerLayerParameters containerParameters |
michael@0 | 1288 | (presShell->GetXResolution(), presShell->GetYResolution()); |
michael@0 | 1289 | nsRefPtr<ContainerLayer> root = layerBuilder-> |
michael@0 | 1290 | BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, *this, |
michael@0 | 1291 | containerParameters, nullptr); |
michael@0 | 1292 | |
michael@0 | 1293 | nsIDocument* document = nullptr; |
michael@0 | 1294 | if (presShell) { |
michael@0 | 1295 | document = presShell->GetDocument(); |
michael@0 | 1296 | } |
michael@0 | 1297 | |
michael@0 | 1298 | if (widgetTransaction || |
michael@0 | 1299 | // SVG-as-an-image docs don't paint as part of the retained layer tree, |
michael@0 | 1300 | // but they still need the invalidation state bits cleared in order for |
michael@0 | 1301 | // invalidation for CSS/SMIL animation to work properly. |
michael@0 | 1302 | (document && document->IsBeingUsedAsImage())) { |
michael@0 | 1303 | aForFrame->ClearInvalidationStateBits(); |
michael@0 | 1304 | } |
michael@0 | 1305 | |
michael@0 | 1306 | if (!root) { |
michael@0 | 1307 | layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder); |
michael@0 | 1308 | return; |
michael@0 | 1309 | } |
michael@0 | 1310 | // Root is being scaled up by the X/Y resolution. Scale it back down. |
michael@0 | 1311 | root->SetPostScale(1.0f/containerParameters.mXScale, |
michael@0 | 1312 | 1.0f/containerParameters.mYScale); |
michael@0 | 1313 | |
michael@0 | 1314 | ViewID id = FrameMetrics::NULL_SCROLL_ID; |
michael@0 | 1315 | bool isRoot = presContext->IsRootContentDocument(); |
michael@0 | 1316 | |
michael@0 | 1317 | nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); |
michael@0 | 1318 | nsRect displayport, criticalDisplayport; |
michael@0 | 1319 | bool usingDisplayport = false; |
michael@0 | 1320 | bool usingCriticalDisplayport = false; |
michael@0 | 1321 | if (rootScrollFrame) { |
michael@0 | 1322 | nsIContent* content = rootScrollFrame->GetContent(); |
michael@0 | 1323 | if (content) { |
michael@0 | 1324 | usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport); |
michael@0 | 1325 | usingCriticalDisplayport = |
michael@0 | 1326 | nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport); |
michael@0 | 1327 | |
michael@0 | 1328 | // If this is the root content document, we want it to have a scroll id. |
michael@0 | 1329 | if (isRoot) { |
michael@0 | 1330 | id = nsLayoutUtils::FindOrCreateIDFor(content); |
michael@0 | 1331 | } |
michael@0 | 1332 | } |
michael@0 | 1333 | } |
michael@0 | 1334 | |
michael@0 | 1335 | nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize()); |
michael@0 | 1336 | |
michael@0 | 1337 | RecordFrameMetrics(aForFrame, rootScrollFrame, |
michael@0 | 1338 | aBuilder->FindReferenceFrameFor(aForFrame), |
michael@0 | 1339 | root, mVisibleRect, viewport, |
michael@0 | 1340 | (usingDisplayport ? &displayport : nullptr), |
michael@0 | 1341 | (usingCriticalDisplayport ? &criticalDisplayport : nullptr), |
michael@0 | 1342 | id, isRoot, containerParameters); |
michael@0 | 1343 | if (usingDisplayport && |
michael@0 | 1344 | !(root->GetContentFlags() & Layer::CONTENT_OPAQUE)) { |
michael@0 | 1345 | // See bug 693938, attachment 567017 |
michael@0 | 1346 | NS_WARNING("Transparent content with displayports can be expensive."); |
michael@0 | 1347 | } |
michael@0 | 1348 | |
michael@0 | 1349 | layerManager->SetRoot(root); |
michael@0 | 1350 | layerBuilder->WillEndTransaction(); |
michael@0 | 1351 | bool temp = aBuilder->SetIsCompositingCheap(layerManager->IsCompositingCheap()); |
michael@0 | 1352 | LayerManager::EndTransactionFlags flags = LayerManager::END_DEFAULT; |
michael@0 | 1353 | if (layerManager->NeedsWidgetInvalidation()) { |
michael@0 | 1354 | if (aFlags & PAINT_NO_COMPOSITE) { |
michael@0 | 1355 | flags = LayerManager::END_NO_COMPOSITE; |
michael@0 | 1356 | } |
michael@0 | 1357 | } else { |
michael@0 | 1358 | // Client layer managers never composite directly, so |
michael@0 | 1359 | // we don't need to worry about END_NO_COMPOSITE. |
michael@0 | 1360 | if (aBuilder->WillComputePluginGeometry()) { |
michael@0 | 1361 | flags = LayerManager::END_NO_REMOTE_COMPOSITE; |
michael@0 | 1362 | } |
michael@0 | 1363 | } |
michael@0 | 1364 | |
michael@0 | 1365 | layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, |
michael@0 | 1366 | aBuilder, flags); |
michael@0 | 1367 | aBuilder->SetIsCompositingCheap(temp); |
michael@0 | 1368 | layerBuilder->DidEndTransaction(); |
michael@0 | 1369 | |
michael@0 | 1370 | nsIntRegion invalid; |
michael@0 | 1371 | if (props) { |
michael@0 | 1372 | invalid = props->ComputeDifferences(root, computeInvalidFunc); |
michael@0 | 1373 | } else if (widgetTransaction) { |
michael@0 | 1374 | LayerProperties::ClearInvalidations(root); |
michael@0 | 1375 | } |
michael@0 | 1376 | |
michael@0 | 1377 | bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); |
michael@0 | 1378 | if (view) { |
michael@0 | 1379 | if (props) { |
michael@0 | 1380 | if (!invalid.IsEmpty()) { |
michael@0 | 1381 | nsIntRect bounds = invalid.GetBounds(); |
michael@0 | 1382 | nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), |
michael@0 | 1383 | presContext->DevPixelsToAppUnits(bounds.y), |
michael@0 | 1384 | presContext->DevPixelsToAppUnits(bounds.width), |
michael@0 | 1385 | presContext->DevPixelsToAppUnits(bounds.height)); |
michael@0 | 1386 | if (shouldInvalidate) { |
michael@0 | 1387 | view->GetViewManager()->InvalidateViewNoSuppression(view, rect); |
michael@0 | 1388 | } |
michael@0 | 1389 | presContext->NotifyInvalidation(bounds, 0); |
michael@0 | 1390 | } |
michael@0 | 1391 | } else if (shouldInvalidate) { |
michael@0 | 1392 | view->GetViewManager()->InvalidateView(view); |
michael@0 | 1393 | } |
michael@0 | 1394 | } |
michael@0 | 1395 | |
michael@0 | 1396 | if (aFlags & PAINT_FLUSH_LAYERS) { |
michael@0 | 1397 | FrameLayerBuilder::InvalidateAllLayers(layerManager); |
michael@0 | 1398 | } |
michael@0 | 1399 | |
michael@0 | 1400 | layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder); |
michael@0 | 1401 | } |
michael@0 | 1402 | |
michael@0 | 1403 | uint32_t nsDisplayList::Count() const { |
michael@0 | 1404 | uint32_t count = 0; |
michael@0 | 1405 | for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) { |
michael@0 | 1406 | ++count; |
michael@0 | 1407 | } |
michael@0 | 1408 | return count; |
michael@0 | 1409 | } |
michael@0 | 1410 | |
michael@0 | 1411 | nsDisplayItem* nsDisplayList::RemoveBottom() { |
michael@0 | 1412 | nsDisplayItem* item = mSentinel.mAbove; |
michael@0 | 1413 | if (!item) |
michael@0 | 1414 | return nullptr; |
michael@0 | 1415 | mSentinel.mAbove = item->mAbove; |
michael@0 | 1416 | if (item == mTop) { |
michael@0 | 1417 | // must have been the only item |
michael@0 | 1418 | mTop = &mSentinel; |
michael@0 | 1419 | } |
michael@0 | 1420 | item->mAbove = nullptr; |
michael@0 | 1421 | return item; |
michael@0 | 1422 | } |
michael@0 | 1423 | |
michael@0 | 1424 | void nsDisplayList::DeleteAll() { |
michael@0 | 1425 | nsDisplayItem* item; |
michael@0 | 1426 | while ((item = RemoveBottom()) != nullptr) { |
michael@0 | 1427 | item->~nsDisplayItem(); |
michael@0 | 1428 | } |
michael@0 | 1429 | } |
michael@0 | 1430 | |
michael@0 | 1431 | static bool |
michael@0 | 1432 | GetMouseThrough(const nsIFrame* aFrame) |
michael@0 | 1433 | { |
michael@0 | 1434 | if (!aFrame->IsBoxFrame()) |
michael@0 | 1435 | return false; |
michael@0 | 1436 | |
michael@0 | 1437 | const nsIFrame* frame = aFrame; |
michael@0 | 1438 | while (frame) { |
michael@0 | 1439 | if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_ALWAYS) { |
michael@0 | 1440 | return true; |
michael@0 | 1441 | } else if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_NEVER) { |
michael@0 | 1442 | return false; |
michael@0 | 1443 | } |
michael@0 | 1444 | frame = frame->GetParentBox(); |
michael@0 | 1445 | } |
michael@0 | 1446 | return false; |
michael@0 | 1447 | } |
michael@0 | 1448 | |
michael@0 | 1449 | static bool |
michael@0 | 1450 | IsFrameReceivingPointerEvents(nsIFrame* aFrame) |
michael@0 | 1451 | { |
michael@0 | 1452 | nsSubDocumentFrame* frame = do_QueryFrame(aFrame); |
michael@0 | 1453 | if (frame && frame->PassPointerEventsToChildren()) { |
michael@0 | 1454 | return true; |
michael@0 | 1455 | } |
michael@0 | 1456 | return NS_STYLE_POINTER_EVENTS_NONE != |
michael@0 | 1457 | aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame); |
michael@0 | 1458 | } |
michael@0 | 1459 | |
michael@0 | 1460 | // A list of frames, and their z depth. Used for sorting |
michael@0 | 1461 | // the results of hit testing. |
michael@0 | 1462 | struct FramesWithDepth |
michael@0 | 1463 | { |
michael@0 | 1464 | FramesWithDepth(float aDepth) : |
michael@0 | 1465 | mDepth(aDepth) |
michael@0 | 1466 | {} |
michael@0 | 1467 | |
michael@0 | 1468 | bool operator<(const FramesWithDepth& aOther) const { |
michael@0 | 1469 | if (mDepth != aOther.mDepth) { |
michael@0 | 1470 | // We want to sort so that the shallowest item (highest depth value) is first |
michael@0 | 1471 | return mDepth > aOther.mDepth; |
michael@0 | 1472 | } |
michael@0 | 1473 | return this < &aOther; |
michael@0 | 1474 | } |
michael@0 | 1475 | bool operator==(const FramesWithDepth& aOther) const { |
michael@0 | 1476 | return this == &aOther; |
michael@0 | 1477 | } |
michael@0 | 1478 | |
michael@0 | 1479 | float mDepth; |
michael@0 | 1480 | nsTArray<nsIFrame*> mFrames; |
michael@0 | 1481 | }; |
michael@0 | 1482 | |
michael@0 | 1483 | // Sort the frames by depth and then moves all the contained frames to the destination |
michael@0 | 1484 | void FlushFramesArray(nsTArray<FramesWithDepth>& aSource, nsTArray<nsIFrame*>* aDest) |
michael@0 | 1485 | { |
michael@0 | 1486 | if (aSource.IsEmpty()) { |
michael@0 | 1487 | return; |
michael@0 | 1488 | } |
michael@0 | 1489 | aSource.Sort(); |
michael@0 | 1490 | uint32_t length = aSource.Length(); |
michael@0 | 1491 | for (uint32_t i = 0; i < length; i++) { |
michael@0 | 1492 | aDest->MoveElementsFrom(aSource[i].mFrames); |
michael@0 | 1493 | } |
michael@0 | 1494 | aSource.Clear(); |
michael@0 | 1495 | } |
michael@0 | 1496 | |
michael@0 | 1497 | void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
michael@0 | 1498 | nsDisplayItem::HitTestState* aState, |
michael@0 | 1499 | nsTArray<nsIFrame*> *aOutFrames) const { |
michael@0 | 1500 | int32_t itemBufferStart = aState->mItemBuffer.Length(); |
michael@0 | 1501 | nsDisplayItem* item; |
michael@0 | 1502 | for (item = GetBottom(); item; item = item->GetAbove()) { |
michael@0 | 1503 | aState->mItemBuffer.AppendElement(item); |
michael@0 | 1504 | } |
michael@0 | 1505 | nsAutoTArray<FramesWithDepth, 16> temp; |
michael@0 | 1506 | for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) { |
michael@0 | 1507 | // Pop element off the end of the buffer. We want to shorten the buffer |
michael@0 | 1508 | // so that recursive calls to HitTest have more buffer space. |
michael@0 | 1509 | item = aState->mItemBuffer[i]; |
michael@0 | 1510 | aState->mItemBuffer.SetLength(i); |
michael@0 | 1511 | |
michael@0 | 1512 | bool snap; |
michael@0 | 1513 | nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect); |
michael@0 | 1514 | if (item->GetClip().MayIntersect(r)) { |
michael@0 | 1515 | nsAutoTArray<nsIFrame*, 16> outFrames; |
michael@0 | 1516 | item->HitTest(aBuilder, aRect, aState, &outFrames); |
michael@0 | 1517 | |
michael@0 | 1518 | // For 3d transforms with preserve-3d we add hit frames into the temp list |
michael@0 | 1519 | // so we can sort them later, otherwise we add them directly to the output list. |
michael@0 | 1520 | nsTArray<nsIFrame*> *writeFrames = aOutFrames; |
michael@0 | 1521 | if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM && |
michael@0 | 1522 | item->Frame()->Preserves3D()) { |
michael@0 | 1523 | if (outFrames.Length()) { |
michael@0 | 1524 | nsDisplayTransform *transform = static_cast<nsDisplayTransform*>(item); |
michael@0 | 1525 | nsPoint point = aRect.TopLeft(); |
michael@0 | 1526 | // A 1x1 rect means a point, otherwise use the center of the rect |
michael@0 | 1527 | if (aRect.width != 1 || aRect.height != 1) { |
michael@0 | 1528 | point = aRect.Center(); |
michael@0 | 1529 | } |
michael@0 | 1530 | temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point))); |
michael@0 | 1531 | writeFrames = &temp[temp.Length() - 1].mFrames; |
michael@0 | 1532 | } |
michael@0 | 1533 | } else { |
michael@0 | 1534 | // We may have just finished a run of consecutive preserve-3d transforms, |
michael@0 | 1535 | // so flush these into the destination array before processing our frame list. |
michael@0 | 1536 | FlushFramesArray(temp, aOutFrames); |
michael@0 | 1537 | } |
michael@0 | 1538 | |
michael@0 | 1539 | for (uint32_t j = 0; j < outFrames.Length(); j++) { |
michael@0 | 1540 | nsIFrame *f = outFrames.ElementAt(j); |
michael@0 | 1541 | // Handle the XUL 'mousethrough' feature and 'pointer-events'. |
michael@0 | 1542 | if (!GetMouseThrough(f) && IsFrameReceivingPointerEvents(f)) { |
michael@0 | 1543 | writeFrames->AppendElement(f); |
michael@0 | 1544 | } |
michael@0 | 1545 | } |
michael@0 | 1546 | } |
michael@0 | 1547 | } |
michael@0 | 1548 | // Clear any remaining preserve-3d transforms. |
michael@0 | 1549 | FlushFramesArray(temp, aOutFrames); |
michael@0 | 1550 | NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart), |
michael@0 | 1551 | "How did we forget to pop some elements?"); |
michael@0 | 1552 | } |
michael@0 | 1553 | |
michael@0 | 1554 | static void Sort(nsDisplayList* aList, int32_t aCount, nsDisplayList::SortLEQ aCmp, |
michael@0 | 1555 | void* aClosure) { |
michael@0 | 1556 | if (aCount < 2) |
michael@0 | 1557 | return; |
michael@0 | 1558 | |
michael@0 | 1559 | nsDisplayList list1; |
michael@0 | 1560 | nsDisplayList list2; |
michael@0 | 1561 | int i; |
michael@0 | 1562 | int32_t half = aCount/2; |
michael@0 | 1563 | bool sorted = true; |
michael@0 | 1564 | nsDisplayItem* prev = nullptr; |
michael@0 | 1565 | for (i = 0; i < aCount; ++i) { |
michael@0 | 1566 | nsDisplayItem* item = aList->RemoveBottom(); |
michael@0 | 1567 | (i < half ? &list1 : &list2)->AppendToTop(item); |
michael@0 | 1568 | if (sorted && prev && !aCmp(prev, item, aClosure)) { |
michael@0 | 1569 | sorted = false; |
michael@0 | 1570 | } |
michael@0 | 1571 | prev = item; |
michael@0 | 1572 | } |
michael@0 | 1573 | if (sorted) { |
michael@0 | 1574 | aList->AppendToTop(&list1); |
michael@0 | 1575 | aList->AppendToTop(&list2); |
michael@0 | 1576 | return; |
michael@0 | 1577 | } |
michael@0 | 1578 | |
michael@0 | 1579 | Sort(&list1, half, aCmp, aClosure); |
michael@0 | 1580 | Sort(&list2, aCount - half, aCmp, aClosure); |
michael@0 | 1581 | |
michael@0 | 1582 | for (i = 0; i < aCount; ++i) { |
michael@0 | 1583 | if (list1.GetBottom() && |
michael@0 | 1584 | (!list2.GetBottom() || |
michael@0 | 1585 | aCmp(list1.GetBottom(), list2.GetBottom(), aClosure))) { |
michael@0 | 1586 | aList->AppendToTop(list1.RemoveBottom()); |
michael@0 | 1587 | } else { |
michael@0 | 1588 | aList->AppendToTop(list2.RemoveBottom()); |
michael@0 | 1589 | } |
michael@0 | 1590 | } |
michael@0 | 1591 | } |
michael@0 | 1592 | |
michael@0 | 1593 | static nsIContent* FindContentInDocument(nsDisplayItem* aItem, nsIDocument* aDoc) { |
michael@0 | 1594 | nsIFrame* f = aItem->Frame(); |
michael@0 | 1595 | while (f) { |
michael@0 | 1596 | nsPresContext* pc = f->PresContext(); |
michael@0 | 1597 | if (pc->Document() == aDoc) { |
michael@0 | 1598 | return f->GetContent(); |
michael@0 | 1599 | } |
michael@0 | 1600 | f = nsLayoutUtils::GetCrossDocParentFrame(pc->PresShell()->GetRootFrame()); |
michael@0 | 1601 | } |
michael@0 | 1602 | return nullptr; |
michael@0 | 1603 | } |
michael@0 | 1604 | |
michael@0 | 1605 | static bool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, |
michael@0 | 1606 | void* aClosure) { |
michael@0 | 1607 | nsIContent* commonAncestor = static_cast<nsIContent*>(aClosure); |
michael@0 | 1608 | // It's possible that the nsIContent for aItem1 or aItem2 is in a subdocument |
michael@0 | 1609 | // of commonAncestor, because display items for subdocuments have been |
michael@0 | 1610 | // mixed into the same list. Ensure that we're looking at content |
michael@0 | 1611 | // in commonAncestor's document. |
michael@0 | 1612 | nsIDocument* commonAncestorDoc = commonAncestor->OwnerDoc(); |
michael@0 | 1613 | nsIContent* content1 = FindContentInDocument(aItem1, commonAncestorDoc); |
michael@0 | 1614 | nsIContent* content2 = FindContentInDocument(aItem2, commonAncestorDoc); |
michael@0 | 1615 | if (!content1 || !content2) { |
michael@0 | 1616 | NS_ERROR("Document trees are mixed up!"); |
michael@0 | 1617 | // Something weird going on |
michael@0 | 1618 | return true; |
michael@0 | 1619 | } |
michael@0 | 1620 | return nsLayoutUtils::CompareTreePosition(content1, content2, commonAncestor) <= 0; |
michael@0 | 1621 | } |
michael@0 | 1622 | |
michael@0 | 1623 | static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, |
michael@0 | 1624 | void* aClosure) { |
michael@0 | 1625 | // Note that we can't just take the difference of the two |
michael@0 | 1626 | // z-indices here, because that might overflow a 32-bit int. |
michael@0 | 1627 | return aItem1->ZIndex() <= aItem2->ZIndex(); |
michael@0 | 1628 | } |
michael@0 | 1629 | |
michael@0 | 1630 | void nsDisplayList::SortByZOrder(nsDisplayListBuilder* aBuilder, |
michael@0 | 1631 | nsIContent* aCommonAncestor) { |
michael@0 | 1632 | Sort(aBuilder, IsZOrderLEQ, aCommonAncestor); |
michael@0 | 1633 | } |
michael@0 | 1634 | |
michael@0 | 1635 | void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder, |
michael@0 | 1636 | nsIContent* aCommonAncestor) { |
michael@0 | 1637 | Sort(aBuilder, IsContentLEQ, aCommonAncestor); |
michael@0 | 1638 | } |
michael@0 | 1639 | |
michael@0 | 1640 | void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder, |
michael@0 | 1641 | SortLEQ aCmp, void* aClosure) { |
michael@0 | 1642 | ::Sort(this, Count(), aCmp, aClosure); |
michael@0 | 1643 | } |
michael@0 | 1644 | |
michael@0 | 1645 | void |
michael@0 | 1646 | nsDisplayItem::AddInvalidRegionForSyncDecodeBackgroundImages( |
michael@0 | 1647 | nsDisplayListBuilder* aBuilder, |
michael@0 | 1648 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 1649 | nsRegion* aInvalidRegion) |
michael@0 | 1650 | { |
michael@0 | 1651 | if (aBuilder->ShouldSyncDecodeImages()) { |
michael@0 | 1652 | if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) { |
michael@0 | 1653 | bool snap; |
michael@0 | 1654 | aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
michael@0 | 1655 | } |
michael@0 | 1656 | } |
michael@0 | 1657 | } |
michael@0 | 1658 | |
michael@0 | 1659 | /* static */ bool |
michael@0 | 1660 | nsDisplayItem::ForceActiveLayers() |
michael@0 | 1661 | { |
michael@0 | 1662 | static bool sForce = false; |
michael@0 | 1663 | static bool sForceCached = false; |
michael@0 | 1664 | |
michael@0 | 1665 | if (!sForceCached) { |
michael@0 | 1666 | Preferences::AddBoolVarCache(&sForce, "layers.force-active", false); |
michael@0 | 1667 | sForceCached = true; |
michael@0 | 1668 | } |
michael@0 | 1669 | |
michael@0 | 1670 | return sForce; |
michael@0 | 1671 | } |
michael@0 | 1672 | |
michael@0 | 1673 | /* static */ int32_t |
michael@0 | 1674 | nsDisplayItem::MaxActiveLayers() |
michael@0 | 1675 | { |
michael@0 | 1676 | static int32_t sMaxLayers = false; |
michael@0 | 1677 | static bool sMaxLayersCached = false; |
michael@0 | 1678 | |
michael@0 | 1679 | if (!sMaxLayersCached) { |
michael@0 | 1680 | Preferences::AddIntVarCache(&sMaxLayers, "layers.max-active", -1); |
michael@0 | 1681 | sMaxLayersCached = true; |
michael@0 | 1682 | } |
michael@0 | 1683 | |
michael@0 | 1684 | return sMaxLayers; |
michael@0 | 1685 | } |
michael@0 | 1686 | |
michael@0 | 1687 | int32_t |
michael@0 | 1688 | nsDisplayItem::ZIndex() const |
michael@0 | 1689 | { |
michael@0 | 1690 | if (!mFrame->IsPositioned() && !mFrame->IsFlexItem()) |
michael@0 | 1691 | return 0; |
michael@0 | 1692 | |
michael@0 | 1693 | const nsStylePosition* position = mFrame->StylePosition(); |
michael@0 | 1694 | if (position->mZIndex.GetUnit() == eStyleUnit_Integer) |
michael@0 | 1695 | return position->mZIndex.GetIntValue(); |
michael@0 | 1696 | |
michael@0 | 1697 | // sort the auto and 0 elements together |
michael@0 | 1698 | return 0; |
michael@0 | 1699 | } |
michael@0 | 1700 | |
michael@0 | 1701 | bool |
michael@0 | 1702 | nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 1703 | nsRegion* aVisibleRegion) { |
michael@0 | 1704 | nsRect bounds = GetClippedBounds(aBuilder); |
michael@0 | 1705 | |
michael@0 | 1706 | nsRegion itemVisible; |
michael@0 | 1707 | itemVisible.And(*aVisibleRegion, bounds); |
michael@0 | 1708 | mVisibleRect = itemVisible.GetBounds(); |
michael@0 | 1709 | |
michael@0 | 1710 | // When we recompute visibility within layers we don't need to |
michael@0 | 1711 | // expand the visible region for content behind plugins (the plugin |
michael@0 | 1712 | // is not in the layer). |
michael@0 | 1713 | if (!ComputeVisibility(aBuilder, aVisibleRegion, nsRect())) |
michael@0 | 1714 | return false; |
michael@0 | 1715 | |
michael@0 | 1716 | nsRegion opaque = TreatAsOpaque(this, aBuilder); |
michael@0 | 1717 | aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); |
michael@0 | 1718 | return true; |
michael@0 | 1719 | } |
michael@0 | 1720 | |
michael@0 | 1721 | nsRect |
michael@0 | 1722 | nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) |
michael@0 | 1723 | { |
michael@0 | 1724 | bool snap; |
michael@0 | 1725 | nsRect r = GetBounds(aBuilder, &snap); |
michael@0 | 1726 | return GetClip().ApplyNonRoundedIntersection(r); |
michael@0 | 1727 | } |
michael@0 | 1728 | |
michael@0 | 1729 | nsRect |
michael@0 | 1730 | nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
michael@0 | 1731 | { |
michael@0 | 1732 | *aSnap = true; |
michael@0 | 1733 | return mBounds; |
michael@0 | 1734 | } |
michael@0 | 1735 | |
michael@0 | 1736 | void |
michael@0 | 1737 | nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 1738 | nsRenderingContext* aCtx) |
michael@0 | 1739 | { |
michael@0 | 1740 | aCtx->SetColor(mColor); |
michael@0 | 1741 | aCtx->FillRect(mVisibleRect); |
michael@0 | 1742 | } |
michael@0 | 1743 | |
michael@0 | 1744 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 1745 | void |
michael@0 | 1746 | nsDisplaySolidColor::WriteDebugInfo(nsACString& aTo) |
michael@0 | 1747 | { |
michael@0 | 1748 | aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)", |
michael@0 | 1749 | NS_GET_R(mColor), NS_GET_G(mColor), |
michael@0 | 1750 | NS_GET_B(mColor), NS_GET_A(mColor)); |
michael@0 | 1751 | } |
michael@0 | 1752 | #endif |
michael@0 | 1753 | |
michael@0 | 1754 | static void |
michael@0 | 1755 | RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
michael@0 | 1756 | { |
michael@0 | 1757 | nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame); |
michael@0 | 1758 | |
michael@0 | 1759 | for (nsIFrame* f = aFrame; f; f = f->GetParent()) { |
michael@0 | 1760 | // Bail out if we're in a transformed subtree |
michael@0 | 1761 | if (f->IsTransformed()) |
michael@0 | 1762 | return; |
michael@0 | 1763 | // Bail out if we're not in the displayRoot's document |
michael@0 | 1764 | if (!f->GetParent() && f != displayRoot) |
michael@0 | 1765 | return; |
michael@0 | 1766 | } |
michael@0 | 1767 | |
michael@0 | 1768 | nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize()); |
michael@0 | 1769 | aBuilder->RegisterThemeGeometry(aFrame->StyleDisplay()->mAppearance, |
michael@0 | 1770 | borderBox.ToNearestPixels(aFrame->PresContext()->AppUnitsPerDevPixel())); |
michael@0 | 1771 | } |
michael@0 | 1772 | |
michael@0 | 1773 | nsDisplayBackgroundImage::nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder, |
michael@0 | 1774 | nsIFrame* aFrame, |
michael@0 | 1775 | uint32_t aLayer, |
michael@0 | 1776 | const nsStyleBackground* aBackgroundStyle) |
michael@0 | 1777 | : nsDisplayImageContainer(aBuilder, aFrame) |
michael@0 | 1778 | , mBackgroundStyle(aBackgroundStyle) |
michael@0 | 1779 | , mLayer(aLayer) |
michael@0 | 1780 | { |
michael@0 | 1781 | MOZ_COUNT_CTOR(nsDisplayBackgroundImage); |
michael@0 | 1782 | |
michael@0 | 1783 | mBounds = GetBoundsInternal(aBuilder); |
michael@0 | 1784 | } |
michael@0 | 1785 | |
michael@0 | 1786 | nsDisplayBackgroundImage::~nsDisplayBackgroundImage() |
michael@0 | 1787 | { |
michael@0 | 1788 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 1789 | MOZ_COUNT_DTOR(nsDisplayBackgroundImage); |
michael@0 | 1790 | #endif |
michael@0 | 1791 | } |
michael@0 | 1792 | |
michael@0 | 1793 | static nsStyleContext* GetBackgroundStyleContext(nsIFrame* aFrame) |
michael@0 | 1794 | { |
michael@0 | 1795 | nsStyleContext *sc; |
michael@0 | 1796 | if (!nsCSSRendering::FindBackground(aFrame, &sc)) { |
michael@0 | 1797 | // We don't want to bail out if moz-appearance is set on a root |
michael@0 | 1798 | // node. If it has a parent content node, bail because it's not |
michael@0 | 1799 | // a root, other wise keep going in order to let the theme stuff |
michael@0 | 1800 | // draw the background. The canvas really should be drawing the |
michael@0 | 1801 | // bg, but there's no way to hook that up via css. |
michael@0 | 1802 | if (!aFrame->StyleDisplay()->mAppearance) { |
michael@0 | 1803 | return nullptr; |
michael@0 | 1804 | } |
michael@0 | 1805 | |
michael@0 | 1806 | nsIContent* content = aFrame->GetContent(); |
michael@0 | 1807 | if (!content || content->GetParent()) { |
michael@0 | 1808 | return nullptr; |
michael@0 | 1809 | } |
michael@0 | 1810 | |
michael@0 | 1811 | sc = aFrame->StyleContext(); |
michael@0 | 1812 | } |
michael@0 | 1813 | return sc; |
michael@0 | 1814 | } |
michael@0 | 1815 | |
michael@0 | 1816 | /*static*/ bool |
michael@0 | 1817 | nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder, |
michael@0 | 1818 | nsIFrame* aFrame, |
michael@0 | 1819 | nsDisplayList* aList) |
michael@0 | 1820 | { |
michael@0 | 1821 | nsStyleContext* bgSC = nullptr; |
michael@0 | 1822 | const nsStyleBackground* bg = nullptr; |
michael@0 | 1823 | nsPresContext* presContext = aFrame->PresContext(); |
michael@0 | 1824 | bool isThemed = aFrame->IsThemed(); |
michael@0 | 1825 | if (!isThemed) { |
michael@0 | 1826 | bgSC = GetBackgroundStyleContext(aFrame); |
michael@0 | 1827 | if (bgSC) { |
michael@0 | 1828 | bg = bgSC->StyleBackground(); |
michael@0 | 1829 | } |
michael@0 | 1830 | } |
michael@0 | 1831 | |
michael@0 | 1832 | bool drawBackgroundColor = false; |
michael@0 | 1833 | nscolor color; |
michael@0 | 1834 | if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) { |
michael@0 | 1835 | bool drawBackgroundImage; |
michael@0 | 1836 | color = |
michael@0 | 1837 | nsCSSRendering::DetermineBackgroundColor(presContext, bgSC, aFrame, |
michael@0 | 1838 | drawBackgroundImage, drawBackgroundColor); |
michael@0 | 1839 | } |
michael@0 | 1840 | |
michael@0 | 1841 | // An auxiliary list is necessary in case we have background blending; if that |
michael@0 | 1842 | // is the case, background items need to be wrapped by a blend container to |
michael@0 | 1843 | // isolate blending to the background |
michael@0 | 1844 | nsDisplayList bgItemList; |
michael@0 | 1845 | // Even if we don't actually have a background color to paint, we may still need |
michael@0 | 1846 | // to create an item for hit testing. |
michael@0 | 1847 | if ((drawBackgroundColor && color != NS_RGBA(0,0,0,0)) || |
michael@0 | 1848 | aBuilder->IsForEventDelivery()) { |
michael@0 | 1849 | bgItemList.AppendNewToTop( |
michael@0 | 1850 | new (aBuilder) nsDisplayBackgroundColor(aBuilder, aFrame, bg, |
michael@0 | 1851 | drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0))); |
michael@0 | 1852 | } |
michael@0 | 1853 | |
michael@0 | 1854 | if (isThemed) { |
michael@0 | 1855 | nsDisplayThemedBackground* bgItem = |
michael@0 | 1856 | new (aBuilder) nsDisplayThemedBackground(aBuilder, aFrame); |
michael@0 | 1857 | bgItemList.AppendNewToTop(bgItem); |
michael@0 | 1858 | aList->AppendToTop(&bgItemList); |
michael@0 | 1859 | return true; |
michael@0 | 1860 | } |
michael@0 | 1861 | |
michael@0 | 1862 | if (!bg) { |
michael@0 | 1863 | aList->AppendToTop(&bgItemList); |
michael@0 | 1864 | return false; |
michael@0 | 1865 | } |
michael@0 | 1866 | |
michael@0 | 1867 | bool needBlendContainer = false; |
michael@0 | 1868 | |
michael@0 | 1869 | // Passing bg == nullptr in this macro will result in one iteration with |
michael@0 | 1870 | // i = 0. |
michael@0 | 1871 | NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { |
michael@0 | 1872 | if (bg->mLayers[i].mImage.IsEmpty()) { |
michael@0 | 1873 | continue; |
michael@0 | 1874 | } |
michael@0 | 1875 | |
michael@0 | 1876 | if (bg->mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) { |
michael@0 | 1877 | needBlendContainer = true; |
michael@0 | 1878 | } |
michael@0 | 1879 | |
michael@0 | 1880 | nsDisplayBackgroundImage* bgItem = |
michael@0 | 1881 | new (aBuilder) nsDisplayBackgroundImage(aBuilder, aFrame, i, bg); |
michael@0 | 1882 | bgItemList.AppendNewToTop(bgItem); |
michael@0 | 1883 | } |
michael@0 | 1884 | |
michael@0 | 1885 | if (needBlendContainer) { |
michael@0 | 1886 | bgItemList.AppendNewToTop( |
michael@0 | 1887 | new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, &bgItemList)); |
michael@0 | 1888 | } |
michael@0 | 1889 | |
michael@0 | 1890 | aList->AppendToTop(&bgItemList); |
michael@0 | 1891 | return false; |
michael@0 | 1892 | } |
michael@0 | 1893 | |
michael@0 | 1894 | // Check that the rounded border of aFrame, added to aToReferenceFrame, |
michael@0 | 1895 | // intersects aRect. Assumes that the unrounded border has already |
michael@0 | 1896 | // been checked for intersection. |
michael@0 | 1897 | static bool |
michael@0 | 1898 | RoundedBorderIntersectsRect(nsIFrame* aFrame, |
michael@0 | 1899 | const nsPoint& aFrameToReferenceFrame, |
michael@0 | 1900 | const nsRect& aTestRect) |
michael@0 | 1901 | { |
michael@0 | 1902 | if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize()).Intersects(aTestRect)) |
michael@0 | 1903 | return false; |
michael@0 | 1904 | |
michael@0 | 1905 | nscoord radii[8]; |
michael@0 | 1906 | return !aFrame->GetBorderRadii(radii) || |
michael@0 | 1907 | nsLayoutUtils::RoundedRectIntersectsRect(nsRect(aFrameToReferenceFrame, |
michael@0 | 1908 | aFrame->GetSize()), |
michael@0 | 1909 | radii, aTestRect); |
michael@0 | 1910 | } |
michael@0 | 1911 | |
michael@0 | 1912 | // Returns TRUE if aContainedRect is guaranteed to be contained in |
michael@0 | 1913 | // the rounded rect defined by aRoundedRect and aRadii. Complex cases are |
michael@0 | 1914 | // handled conservatively by returning FALSE in some situations where |
michael@0 | 1915 | // a more thorough analysis could return TRUE. |
michael@0 | 1916 | // |
michael@0 | 1917 | // See also RoundedRectIntersectsRect. |
michael@0 | 1918 | static bool RoundedRectContainsRect(const nsRect& aRoundedRect, |
michael@0 | 1919 | const nscoord aRadii[8], |
michael@0 | 1920 | const nsRect& aContainedRect) { |
michael@0 | 1921 | nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect); |
michael@0 | 1922 | return rgn.Contains(aContainedRect); |
michael@0 | 1923 | } |
michael@0 | 1924 | |
michael@0 | 1925 | bool |
michael@0 | 1926 | nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder, |
michael@0 | 1927 | const nsRect& aClipRect, |
michael@0 | 1928 | gfxRect* aDestRect) |
michael@0 | 1929 | { |
michael@0 | 1930 | if (!mBackgroundStyle) |
michael@0 | 1931 | return false; |
michael@0 | 1932 | |
michael@0 | 1933 | if (mBackgroundStyle->mLayers.Length() != 1) |
michael@0 | 1934 | return false; |
michael@0 | 1935 | |
michael@0 | 1936 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 1937 | uint32_t flags = aBuilder->GetBackgroundPaintFlags(); |
michael@0 | 1938 | nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 1939 | const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; |
michael@0 | 1940 | |
michael@0 | 1941 | if (layer.mAttachment != NS_STYLE_BG_ATTACHMENT_FIXED) |
michael@0 | 1942 | return false; |
michael@0 | 1943 | |
michael@0 | 1944 | nsBackgroundLayerState state = |
michael@0 | 1945 | nsCSSRendering::PrepareBackgroundLayer(presContext, |
michael@0 | 1946 | mFrame, |
michael@0 | 1947 | flags, |
michael@0 | 1948 | borderArea, |
michael@0 | 1949 | aClipRect, |
michael@0 | 1950 | *mBackgroundStyle, |
michael@0 | 1951 | layer); |
michael@0 | 1952 | |
michael@0 | 1953 | nsImageRenderer* imageRenderer = &state.mImageRenderer; |
michael@0 | 1954 | // We only care about images here, not gradients. |
michael@0 | 1955 | if (!imageRenderer->IsRasterImage()) |
michael@0 | 1956 | return false; |
michael@0 | 1957 | |
michael@0 | 1958 | int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); |
michael@0 | 1959 | *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel); |
michael@0 | 1960 | |
michael@0 | 1961 | return true; |
michael@0 | 1962 | } |
michael@0 | 1963 | |
michael@0 | 1964 | bool |
michael@0 | 1965 | nsDisplayBackgroundImage::TryOptimizeToImageLayer(LayerManager* aManager, |
michael@0 | 1966 | nsDisplayListBuilder* aBuilder) |
michael@0 | 1967 | { |
michael@0 | 1968 | if (!mBackgroundStyle) |
michael@0 | 1969 | return false; |
michael@0 | 1970 | |
michael@0 | 1971 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 1972 | uint32_t flags = aBuilder->GetBackgroundPaintFlags(); |
michael@0 | 1973 | nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 1974 | const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; |
michael@0 | 1975 | |
michael@0 | 1976 | if (layer.mClip != NS_STYLE_BG_CLIP_BORDER) { |
michael@0 | 1977 | return false; |
michael@0 | 1978 | } |
michael@0 | 1979 | nscoord radii[8]; |
michael@0 | 1980 | if (mFrame->GetBorderRadii(radii)) { |
michael@0 | 1981 | return false; |
michael@0 | 1982 | } |
michael@0 | 1983 | |
michael@0 | 1984 | nsBackgroundLayerState state = |
michael@0 | 1985 | nsCSSRendering::PrepareBackgroundLayer(presContext, |
michael@0 | 1986 | mFrame, |
michael@0 | 1987 | flags, |
michael@0 | 1988 | borderArea, |
michael@0 | 1989 | borderArea, |
michael@0 | 1990 | *mBackgroundStyle, |
michael@0 | 1991 | layer); |
michael@0 | 1992 | |
michael@0 | 1993 | nsImageRenderer* imageRenderer = &state.mImageRenderer; |
michael@0 | 1994 | // We only care about images here, not gradients. |
michael@0 | 1995 | if (!imageRenderer->IsRasterImage()) |
michael@0 | 1996 | return false; |
michael@0 | 1997 | |
michael@0 | 1998 | nsRefPtr<ImageContainer> imageContainer = imageRenderer->GetContainer(aManager); |
michael@0 | 1999 | // Image is not ready to be made into a layer yet |
michael@0 | 2000 | if (!imageContainer) |
michael@0 | 2001 | return false; |
michael@0 | 2002 | |
michael@0 | 2003 | // We currently can't handle tiled or partial backgrounds. |
michael@0 | 2004 | if (!state.mDestArea.IsEqualEdges(state.mFillArea)) { |
michael@0 | 2005 | return false; |
michael@0 | 2006 | } |
michael@0 | 2007 | |
michael@0 | 2008 | // XXX Ignoring state.mAnchor. ImageLayer drawing snaps mDestArea edges to |
michael@0 | 2009 | // layer pixel boundaries. This should be OK for now. |
michael@0 | 2010 | |
michael@0 | 2011 | int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); |
michael@0 | 2012 | mDestRect = nsLayoutUtils::RectToGfxRect(state.mDestArea, appUnitsPerDevPixel); |
michael@0 | 2013 | mImageContainer = imageContainer; |
michael@0 | 2014 | |
michael@0 | 2015 | // Ok, we can turn this into a layer if needed. |
michael@0 | 2016 | return true; |
michael@0 | 2017 | } |
michael@0 | 2018 | |
michael@0 | 2019 | already_AddRefed<ImageContainer> |
michael@0 | 2020 | nsDisplayBackgroundImage::GetContainer(LayerManager* aManager, |
michael@0 | 2021 | nsDisplayListBuilder *aBuilder) |
michael@0 | 2022 | { |
michael@0 | 2023 | if (!TryOptimizeToImageLayer(aManager, aBuilder)) { |
michael@0 | 2024 | return nullptr; |
michael@0 | 2025 | } |
michael@0 | 2026 | |
michael@0 | 2027 | nsRefPtr<ImageContainer> container = mImageContainer; |
michael@0 | 2028 | |
michael@0 | 2029 | return container.forget(); |
michael@0 | 2030 | } |
michael@0 | 2031 | |
michael@0 | 2032 | LayerState |
michael@0 | 2033 | nsDisplayBackgroundImage::GetLayerState(nsDisplayListBuilder* aBuilder, |
michael@0 | 2034 | LayerManager* aManager, |
michael@0 | 2035 | const ContainerLayerParameters& aParameters) |
michael@0 | 2036 | { |
michael@0 | 2037 | bool animated = false; |
michael@0 | 2038 | if (mBackgroundStyle) { |
michael@0 | 2039 | const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; |
michael@0 | 2040 | const nsStyleImage* image = &layer.mImage; |
michael@0 | 2041 | if (image->GetType() == eStyleImageType_Image) { |
michael@0 | 2042 | imgIRequest* imgreq = image->GetImageData(); |
michael@0 | 2043 | nsCOMPtr<imgIContainer> image; |
michael@0 | 2044 | if (NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) && image) { |
michael@0 | 2045 | if (NS_FAILED(image->GetAnimated(&animated))) { |
michael@0 | 2046 | animated = false; |
michael@0 | 2047 | } |
michael@0 | 2048 | } |
michael@0 | 2049 | } |
michael@0 | 2050 | } |
michael@0 | 2051 | |
michael@0 | 2052 | if (!animated || |
michael@0 | 2053 | !nsLayoutUtils::AnimatedImageLayersEnabled()) { |
michael@0 | 2054 | if (!aManager->IsCompositingCheap() || |
michael@0 | 2055 | !nsLayoutUtils::GPUImageScalingEnabled()) { |
michael@0 | 2056 | return LAYER_NONE; |
michael@0 | 2057 | } |
michael@0 | 2058 | } |
michael@0 | 2059 | |
michael@0 | 2060 | if (!TryOptimizeToImageLayer(aManager, aBuilder)) { |
michael@0 | 2061 | return LAYER_NONE; |
michael@0 | 2062 | } |
michael@0 | 2063 | |
michael@0 | 2064 | if (!animated) { |
michael@0 | 2065 | mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize(); |
michael@0 | 2066 | NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!"); |
michael@0 | 2067 | |
michael@0 | 2068 | gfxRect destRect = mDestRect; |
michael@0 | 2069 | |
michael@0 | 2070 | destRect.width *= aParameters.mXScale; |
michael@0 | 2071 | destRect.height *= aParameters.mYScale; |
michael@0 | 2072 | |
michael@0 | 2073 | // Calculate the scaling factor for the frame. |
michael@0 | 2074 | gfxSize scale = gfxSize(destRect.width / imageSize.width, destRect.height / imageSize.height); |
michael@0 | 2075 | |
michael@0 | 2076 | // If we are not scaling at all, no point in separating this into a layer. |
michael@0 | 2077 | if (scale.width == 1.0f && scale.height == 1.0f) { |
michael@0 | 2078 | return LAYER_NONE; |
michael@0 | 2079 | } |
michael@0 | 2080 | |
michael@0 | 2081 | // If the target size is pretty small, no point in using a layer. |
michael@0 | 2082 | if (destRect.width * destRect.height < 64 * 64) { |
michael@0 | 2083 | return LAYER_NONE; |
michael@0 | 2084 | } |
michael@0 | 2085 | } |
michael@0 | 2086 | |
michael@0 | 2087 | return LAYER_ACTIVE; |
michael@0 | 2088 | } |
michael@0 | 2089 | |
michael@0 | 2090 | already_AddRefed<Layer> |
michael@0 | 2091 | nsDisplayBackgroundImage::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 2092 | LayerManager* aManager, |
michael@0 | 2093 | const ContainerLayerParameters& aParameters) |
michael@0 | 2094 | { |
michael@0 | 2095 | nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*> |
michael@0 | 2096 | (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this)); |
michael@0 | 2097 | if (!layer) { |
michael@0 | 2098 | layer = aManager->CreateImageLayer(); |
michael@0 | 2099 | if (!layer) |
michael@0 | 2100 | return nullptr; |
michael@0 | 2101 | } |
michael@0 | 2102 | layer->SetContainer(mImageContainer); |
michael@0 | 2103 | ConfigureLayer(layer, aParameters.mOffset); |
michael@0 | 2104 | return layer.forget(); |
michael@0 | 2105 | } |
michael@0 | 2106 | |
michael@0 | 2107 | void |
michael@0 | 2108 | nsDisplayBackgroundImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset) |
michael@0 | 2109 | { |
michael@0 | 2110 | aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame)); |
michael@0 | 2111 | |
michael@0 | 2112 | mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize(); |
michael@0 | 2113 | NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!"); |
michael@0 | 2114 | |
michael@0 | 2115 | gfxPoint p = mDestRect.TopLeft() + aOffset; |
michael@0 | 2116 | gfx::Matrix transform; |
michael@0 | 2117 | transform.Translate(p.x, p.y); |
michael@0 | 2118 | transform.Scale(mDestRect.width/imageSize.width, |
michael@0 | 2119 | mDestRect.height/imageSize.height); |
michael@0 | 2120 | aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); |
michael@0 | 2121 | aLayer->SetVisibleRegion(nsIntRect(0, 0, imageSize.width, imageSize.height)); |
michael@0 | 2122 | } |
michael@0 | 2123 | |
michael@0 | 2124 | void |
michael@0 | 2125 | nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder, |
michael@0 | 2126 | const nsRect& aRect, |
michael@0 | 2127 | HitTestState* aState, |
michael@0 | 2128 | nsTArray<nsIFrame*> *aOutFrames) |
michael@0 | 2129 | { |
michael@0 | 2130 | if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { |
michael@0 | 2131 | aOutFrames->AppendElement(mFrame); |
michael@0 | 2132 | } |
michael@0 | 2133 | } |
michael@0 | 2134 | |
michael@0 | 2135 | bool |
michael@0 | 2136 | nsDisplayBackgroundImage::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 2137 | nsRegion* aVisibleRegion, |
michael@0 | 2138 | const nsRect& aAllowVisibleRegionExpansion) |
michael@0 | 2139 | { |
michael@0 | 2140 | if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, |
michael@0 | 2141 | aAllowVisibleRegionExpansion)) { |
michael@0 | 2142 | return false; |
michael@0 | 2143 | } |
michael@0 | 2144 | |
michael@0 | 2145 | // Return false if the background was propagated away from this |
michael@0 | 2146 | // frame. We don't want this display item to show up and confuse |
michael@0 | 2147 | // anything. |
michael@0 | 2148 | return mBackgroundStyle; |
michael@0 | 2149 | } |
michael@0 | 2150 | |
michael@0 | 2151 | /* static */ nsRegion |
michael@0 | 2152 | nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem, |
michael@0 | 2153 | nsPresContext* aPresContext, |
michael@0 | 2154 | uint8_t aClip, const nsRect& aRect, |
michael@0 | 2155 | bool* aSnap) |
michael@0 | 2156 | { |
michael@0 | 2157 | nsRegion result; |
michael@0 | 2158 | if (aRect.IsEmpty()) |
michael@0 | 2159 | return result; |
michael@0 | 2160 | |
michael@0 | 2161 | nsIFrame *frame = aItem->Frame(); |
michael@0 | 2162 | |
michael@0 | 2163 | nscoord radii[8]; |
michael@0 | 2164 | nsRect clipRect; |
michael@0 | 2165 | bool haveRadii; |
michael@0 | 2166 | switch (aClip) { |
michael@0 | 2167 | case NS_STYLE_BG_CLIP_BORDER: |
michael@0 | 2168 | haveRadii = frame->GetBorderRadii(radii); |
michael@0 | 2169 | clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize()); |
michael@0 | 2170 | break; |
michael@0 | 2171 | case NS_STYLE_BG_CLIP_PADDING: |
michael@0 | 2172 | haveRadii = frame->GetPaddingBoxBorderRadii(radii); |
michael@0 | 2173 | clipRect = frame->GetPaddingRect() - frame->GetPosition() + aItem->ToReferenceFrame(); |
michael@0 | 2174 | break; |
michael@0 | 2175 | case NS_STYLE_BG_CLIP_CONTENT: |
michael@0 | 2176 | haveRadii = frame->GetContentBoxBorderRadii(radii); |
michael@0 | 2177 | clipRect = frame->GetContentRect() - frame->GetPosition() + aItem->ToReferenceFrame(); |
michael@0 | 2178 | break; |
michael@0 | 2179 | default: |
michael@0 | 2180 | NS_NOTREACHED("Unknown clip type"); |
michael@0 | 2181 | return result; |
michael@0 | 2182 | } |
michael@0 | 2183 | |
michael@0 | 2184 | if (haveRadii) { |
michael@0 | 2185 | *aSnap = false; |
michael@0 | 2186 | result = nsLayoutUtils::RoundedRectIntersectRect(clipRect, radii, aRect); |
michael@0 | 2187 | } else { |
michael@0 | 2188 | result = clipRect.Intersect(aRect); |
michael@0 | 2189 | } |
michael@0 | 2190 | return result; |
michael@0 | 2191 | } |
michael@0 | 2192 | |
michael@0 | 2193 | nsRegion |
michael@0 | 2194 | nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 2195 | bool* aSnap) { |
michael@0 | 2196 | nsRegion result; |
michael@0 | 2197 | *aSnap = false; |
michael@0 | 2198 | |
michael@0 | 2199 | if (!mBackgroundStyle) |
michael@0 | 2200 | return result; |
michael@0 | 2201 | |
michael@0 | 2202 | |
michael@0 | 2203 | *aSnap = true; |
michael@0 | 2204 | |
michael@0 | 2205 | // For policies other than EACH_BOX, don't try to optimize here, since |
michael@0 | 2206 | // this could easily lead to O(N^2) behavior inside InlineBackgroundData, |
michael@0 | 2207 | // which expects frames to be sent to it in content order, not reverse |
michael@0 | 2208 | // content order which we'll produce here. |
michael@0 | 2209 | // Of course, if there's only one frame in the flow, it doesn't matter. |
michael@0 | 2210 | if (mBackgroundStyle->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX || |
michael@0 | 2211 | (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) { |
michael@0 | 2212 | const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer]; |
michael@0 | 2213 | if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL) { |
michael@0 | 2214 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 2215 | result = GetInsideClipRegion(this, presContext, layer.mClip, mBounds, aSnap); |
michael@0 | 2216 | } |
michael@0 | 2217 | } |
michael@0 | 2218 | |
michael@0 | 2219 | return result; |
michael@0 | 2220 | } |
michael@0 | 2221 | |
michael@0 | 2222 | bool |
michael@0 | 2223 | nsDisplayBackgroundImage::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { |
michael@0 | 2224 | if (!mBackgroundStyle) { |
michael@0 | 2225 | *aColor = NS_RGBA(0,0,0,0); |
michael@0 | 2226 | return true; |
michael@0 | 2227 | } |
michael@0 | 2228 | return false; |
michael@0 | 2229 | } |
michael@0 | 2230 | |
michael@0 | 2231 | bool |
michael@0 | 2232 | nsDisplayBackgroundImage::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, |
michael@0 | 2233 | nsIFrame* aFrame) |
michael@0 | 2234 | { |
michael@0 | 2235 | if (!mBackgroundStyle) |
michael@0 | 2236 | return false; |
michael@0 | 2237 | if (!mBackgroundStyle->HasFixedBackground()) |
michael@0 | 2238 | return false; |
michael@0 | 2239 | |
michael@0 | 2240 | // If aFrame is mFrame or an ancestor in this document, and aFrame is |
michael@0 | 2241 | // not the viewport frame, then moving aFrame will move mFrame |
michael@0 | 2242 | // relative to the viewport, so our fixed-pos background will change. |
michael@0 | 2243 | return aFrame->GetParent() && |
michael@0 | 2244 | (aFrame == mFrame || |
michael@0 | 2245 | nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame)); |
michael@0 | 2246 | } |
michael@0 | 2247 | |
michael@0 | 2248 | nsRect |
michael@0 | 2249 | nsDisplayBackgroundImage::GetPositioningArea() |
michael@0 | 2250 | { |
michael@0 | 2251 | if (!mBackgroundStyle) { |
michael@0 | 2252 | return nsRect(); |
michael@0 | 2253 | } |
michael@0 | 2254 | nsIFrame* attachedToFrame; |
michael@0 | 2255 | return nsCSSRendering::ComputeBackgroundPositioningArea( |
michael@0 | 2256 | mFrame->PresContext(), mFrame, |
michael@0 | 2257 | nsRect(ToReferenceFrame(), mFrame->GetSize()), |
michael@0 | 2258 | *mBackgroundStyle, mBackgroundStyle->mLayers[mLayer], |
michael@0 | 2259 | &attachedToFrame) + ToReferenceFrame(); |
michael@0 | 2260 | } |
michael@0 | 2261 | |
michael@0 | 2262 | bool |
michael@0 | 2263 | nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange() |
michael@0 | 2264 | { |
michael@0 | 2265 | if (!mBackgroundStyle) |
michael@0 | 2266 | return false; |
michael@0 | 2267 | |
michael@0 | 2268 | nscoord radii[8]; |
michael@0 | 2269 | if (mFrame->GetBorderRadii(radii)) { |
michael@0 | 2270 | // A change in the size of the positioning area might change the position |
michael@0 | 2271 | // of the rounded corners. |
michael@0 | 2272 | return true; |
michael@0 | 2273 | } |
michael@0 | 2274 | |
michael@0 | 2275 | const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; |
michael@0 | 2276 | if (layer.RenderingMightDependOnPositioningAreaSizeChange()) { |
michael@0 | 2277 | return true; |
michael@0 | 2278 | } |
michael@0 | 2279 | return false; |
michael@0 | 2280 | } |
michael@0 | 2281 | |
michael@0 | 2282 | static void CheckForBorderItem(nsDisplayItem *aItem, uint32_t& aFlags) |
michael@0 | 2283 | { |
michael@0 | 2284 | nsDisplayItem* nextItem = aItem->GetAbove(); |
michael@0 | 2285 | while (nextItem && nextItem->GetType() == nsDisplayItem::TYPE_BACKGROUND) { |
michael@0 | 2286 | nextItem = nextItem->GetAbove(); |
michael@0 | 2287 | } |
michael@0 | 2288 | if (nextItem && |
michael@0 | 2289 | nextItem->Frame() == aItem->Frame() && |
michael@0 | 2290 | nextItem->GetType() == nsDisplayItem::TYPE_BORDER) { |
michael@0 | 2291 | aFlags |= nsCSSRendering::PAINTBG_WILL_PAINT_BORDER; |
michael@0 | 2292 | } |
michael@0 | 2293 | } |
michael@0 | 2294 | |
michael@0 | 2295 | void |
michael@0 | 2296 | nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2297 | nsRenderingContext* aCtx) { |
michael@0 | 2298 | PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr); |
michael@0 | 2299 | } |
michael@0 | 2300 | |
michael@0 | 2301 | void |
michael@0 | 2302 | nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder, |
michael@0 | 2303 | nsRenderingContext* aCtx, const nsRect& aBounds, |
michael@0 | 2304 | nsRect* aClipRect) { |
michael@0 | 2305 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 2306 | uint32_t flags = aBuilder->GetBackgroundPaintFlags(); |
michael@0 | 2307 | CheckForBorderItem(this, flags); |
michael@0 | 2308 | |
michael@0 | 2309 | nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame, |
michael@0 | 2310 | aBounds, |
michael@0 | 2311 | nsRect(offset, mFrame->GetSize()), |
michael@0 | 2312 | flags, aClipRect, mLayer); |
michael@0 | 2313 | |
michael@0 | 2314 | } |
michael@0 | 2315 | |
michael@0 | 2316 | void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 2317 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 2318 | nsRegion* aInvalidRegion) |
michael@0 | 2319 | { |
michael@0 | 2320 | if (!mBackgroundStyle) { |
michael@0 | 2321 | return; |
michael@0 | 2322 | } |
michael@0 | 2323 | |
michael@0 | 2324 | const nsDisplayBackgroundGeometry* geometry = static_cast<const nsDisplayBackgroundGeometry*>(aGeometry); |
michael@0 | 2325 | |
michael@0 | 2326 | bool snap; |
michael@0 | 2327 | nsRect bounds = GetBounds(aBuilder, &snap); |
michael@0 | 2328 | nsRect positioningArea = GetPositioningArea(); |
michael@0 | 2329 | if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() || |
michael@0 | 2330 | (positioningArea.Size() != geometry->mPositioningArea.Size() && |
michael@0 | 2331 | RenderingMightDependOnPositioningAreaSizeChange())) { |
michael@0 | 2332 | // Positioning area changed in a way that could cause everything to change, |
michael@0 | 2333 | // so invalidate everything (both old and new painting areas). |
michael@0 | 2334 | aInvalidRegion->Or(bounds, geometry->mBounds); |
michael@0 | 2335 | |
michael@0 | 2336 | if (positioningArea.Size() != geometry->mPositioningArea.Size()) { |
michael@0 | 2337 | NotifyRenderingChanged(); |
michael@0 | 2338 | } |
michael@0 | 2339 | return; |
michael@0 | 2340 | } |
michael@0 | 2341 | if (aBuilder->ShouldSyncDecodeImages()) { |
michael@0 | 2342 | if (mBackgroundStyle && |
michael@0 | 2343 | !nsCSSRendering::IsBackgroundImageDecodedForStyleContextAndLayer(mBackgroundStyle, mLayer)) { |
michael@0 | 2344 | aInvalidRegion->Or(*aInvalidRegion, bounds); |
michael@0 | 2345 | |
michael@0 | 2346 | NotifyRenderingChanged(); |
michael@0 | 2347 | } |
michael@0 | 2348 | } |
michael@0 | 2349 | if (!bounds.IsEqualInterior(geometry->mBounds)) { |
michael@0 | 2350 | // Positioning area is unchanged, so invalidate just the change in the |
michael@0 | 2351 | // painting area. |
michael@0 | 2352 | aInvalidRegion->Xor(bounds, geometry->mBounds); |
michael@0 | 2353 | |
michael@0 | 2354 | NotifyRenderingChanged(); |
michael@0 | 2355 | } |
michael@0 | 2356 | } |
michael@0 | 2357 | |
michael@0 | 2358 | nsRect |
michael@0 | 2359 | nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
michael@0 | 2360 | *aSnap = true; |
michael@0 | 2361 | return mBounds; |
michael@0 | 2362 | } |
michael@0 | 2363 | |
michael@0 | 2364 | nsRect |
michael@0 | 2365 | nsDisplayBackgroundImage::GetBoundsInternal(nsDisplayListBuilder* aBuilder) { |
michael@0 | 2366 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 2367 | |
michael@0 | 2368 | if (!mBackgroundStyle) { |
michael@0 | 2369 | return nsRect(); |
michael@0 | 2370 | } |
michael@0 | 2371 | |
michael@0 | 2372 | nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 2373 | nsRect clipRect = borderBox; |
michael@0 | 2374 | if (mFrame->GetType() == nsGkAtoms::canvasFrame) { |
michael@0 | 2375 | nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame); |
michael@0 | 2376 | clipRect = frame->CanvasArea() + ToReferenceFrame(); |
michael@0 | 2377 | } |
michael@0 | 2378 | const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer]; |
michael@0 | 2379 | return nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame, |
michael@0 | 2380 | borderBox, clipRect, |
michael@0 | 2381 | *mBackgroundStyle, layer, |
michael@0 | 2382 | aBuilder->GetBackgroundPaintFlags()); |
michael@0 | 2383 | } |
michael@0 | 2384 | |
michael@0 | 2385 | uint32_t |
michael@0 | 2386 | nsDisplayBackgroundImage::GetPerFrameKey() |
michael@0 | 2387 | { |
michael@0 | 2388 | return (mLayer << nsDisplayItem::TYPE_BITS) | |
michael@0 | 2389 | nsDisplayItem::GetPerFrameKey(); |
michael@0 | 2390 | } |
michael@0 | 2391 | |
michael@0 | 2392 | nsDisplayThemedBackground::nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder, |
michael@0 | 2393 | nsIFrame* aFrame) |
michael@0 | 2394 | : nsDisplayItem(aBuilder, aFrame) |
michael@0 | 2395 | { |
michael@0 | 2396 | MOZ_COUNT_CTOR(nsDisplayThemedBackground); |
michael@0 | 2397 | |
michael@0 | 2398 | const nsStyleDisplay* disp = mFrame->StyleDisplay(); |
michael@0 | 2399 | mAppearance = disp->mAppearance; |
michael@0 | 2400 | mFrame->IsThemed(disp, &mThemeTransparency); |
michael@0 | 2401 | |
michael@0 | 2402 | // Perform necessary RegisterThemeGeometry |
michael@0 | 2403 | switch (disp->mAppearance) { |
michael@0 | 2404 | case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: |
michael@0 | 2405 | case NS_THEME_TOOLBAR: |
michael@0 | 2406 | case NS_THEME_WINDOW_TITLEBAR: |
michael@0 | 2407 | case NS_THEME_WINDOW_BUTTON_BOX: |
michael@0 | 2408 | case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON: |
michael@0 | 2409 | case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED: |
michael@0 | 2410 | RegisterThemeGeometry(aBuilder, aFrame); |
michael@0 | 2411 | break; |
michael@0 | 2412 | case NS_THEME_WIN_BORDERLESS_GLASS: |
michael@0 | 2413 | case NS_THEME_WIN_GLASS: |
michael@0 | 2414 | aBuilder->SetGlassDisplayItem(this); |
michael@0 | 2415 | break; |
michael@0 | 2416 | } |
michael@0 | 2417 | |
michael@0 | 2418 | mBounds = GetBoundsInternal(); |
michael@0 | 2419 | } |
michael@0 | 2420 | |
michael@0 | 2421 | nsDisplayThemedBackground::~nsDisplayThemedBackground() |
michael@0 | 2422 | { |
michael@0 | 2423 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 2424 | MOZ_COUNT_DTOR(nsDisplayThemedBackground); |
michael@0 | 2425 | #endif |
michael@0 | 2426 | } |
michael@0 | 2427 | |
michael@0 | 2428 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 2429 | void |
michael@0 | 2430 | nsDisplayThemedBackground::WriteDebugInfo(nsACString& aTo) |
michael@0 | 2431 | { |
michael@0 | 2432 | aTo += nsPrintfCString(" (themed, appearance:%d)", mAppearance); |
michael@0 | 2433 | } |
michael@0 | 2434 | #endif |
michael@0 | 2435 | |
michael@0 | 2436 | void |
michael@0 | 2437 | nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder, |
michael@0 | 2438 | const nsRect& aRect, |
michael@0 | 2439 | HitTestState* aState, |
michael@0 | 2440 | nsTArray<nsIFrame*> *aOutFrames) |
michael@0 | 2441 | { |
michael@0 | 2442 | // Assume that any point in our border rect is a hit. |
michael@0 | 2443 | if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) { |
michael@0 | 2444 | aOutFrames->AppendElement(mFrame); |
michael@0 | 2445 | } |
michael@0 | 2446 | } |
michael@0 | 2447 | |
michael@0 | 2448 | nsRegion |
michael@0 | 2449 | nsDisplayThemedBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 2450 | bool* aSnap) { |
michael@0 | 2451 | nsRegion result; |
michael@0 | 2452 | *aSnap = false; |
michael@0 | 2453 | |
michael@0 | 2454 | if (mThemeTransparency == nsITheme::eOpaque) { |
michael@0 | 2455 | result = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 2456 | } |
michael@0 | 2457 | return result; |
michael@0 | 2458 | } |
michael@0 | 2459 | |
michael@0 | 2460 | bool |
michael@0 | 2461 | nsDisplayThemedBackground::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { |
michael@0 | 2462 | if (mAppearance == NS_THEME_WIN_BORDERLESS_GLASS || |
michael@0 | 2463 | mAppearance == NS_THEME_WIN_GLASS) { |
michael@0 | 2464 | *aColor = NS_RGBA(0,0,0,0); |
michael@0 | 2465 | return true; |
michael@0 | 2466 | } |
michael@0 | 2467 | return false; |
michael@0 | 2468 | } |
michael@0 | 2469 | |
michael@0 | 2470 | nsRect |
michael@0 | 2471 | nsDisplayThemedBackground::GetPositioningArea() |
michael@0 | 2472 | { |
michael@0 | 2473 | return nsRect(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 2474 | } |
michael@0 | 2475 | |
michael@0 | 2476 | void |
michael@0 | 2477 | nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2478 | nsRenderingContext* aCtx) |
michael@0 | 2479 | { |
michael@0 | 2480 | PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr); |
michael@0 | 2481 | } |
michael@0 | 2482 | |
michael@0 | 2483 | |
michael@0 | 2484 | void |
michael@0 | 2485 | nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder, |
michael@0 | 2486 | nsRenderingContext* aCtx, const nsRect& aBounds, |
michael@0 | 2487 | nsRect* aClipRect) |
michael@0 | 2488 | { |
michael@0 | 2489 | // XXXzw this ignores aClipRect. |
michael@0 | 2490 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 2491 | nsITheme *theme = presContext->GetTheme(); |
michael@0 | 2492 | nsRect borderArea(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 2493 | nsRect drawing(borderArea); |
michael@0 | 2494 | theme->GetWidgetOverflow(presContext->DeviceContext(), mFrame, mAppearance, |
michael@0 | 2495 | &drawing); |
michael@0 | 2496 | drawing.IntersectRect(drawing, aBounds); |
michael@0 | 2497 | theme->DrawWidgetBackground(aCtx, mFrame, mAppearance, borderArea, drawing); |
michael@0 | 2498 | } |
michael@0 | 2499 | |
michael@0 | 2500 | bool nsDisplayThemedBackground::IsWindowActive() |
michael@0 | 2501 | { |
michael@0 | 2502 | EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState(); |
michael@0 | 2503 | return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE); |
michael@0 | 2504 | } |
michael@0 | 2505 | |
michael@0 | 2506 | void nsDisplayThemedBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 2507 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 2508 | nsRegion* aInvalidRegion) |
michael@0 | 2509 | { |
michael@0 | 2510 | const nsDisplayThemedBackgroundGeometry* geometry = static_cast<const nsDisplayThemedBackgroundGeometry*>(aGeometry); |
michael@0 | 2511 | |
michael@0 | 2512 | bool snap; |
michael@0 | 2513 | nsRect bounds = GetBounds(aBuilder, &snap); |
michael@0 | 2514 | nsRect positioningArea = GetPositioningArea(); |
michael@0 | 2515 | if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) { |
michael@0 | 2516 | // Invalidate everything (both old and new painting areas). |
michael@0 | 2517 | aInvalidRegion->Or(bounds, geometry->mBounds); |
michael@0 | 2518 | return; |
michael@0 | 2519 | } |
michael@0 | 2520 | if (!bounds.IsEqualInterior(geometry->mBounds)) { |
michael@0 | 2521 | // Positioning area is unchanged, so invalidate just the change in the |
michael@0 | 2522 | // painting area. |
michael@0 | 2523 | aInvalidRegion->Xor(bounds, geometry->mBounds); |
michael@0 | 2524 | } |
michael@0 | 2525 | nsITheme* theme = mFrame->PresContext()->GetTheme(); |
michael@0 | 2526 | if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) && |
michael@0 | 2527 | IsWindowActive() != geometry->mWindowIsActive) { |
michael@0 | 2528 | aInvalidRegion->Or(*aInvalidRegion, bounds); |
michael@0 | 2529 | } |
michael@0 | 2530 | } |
michael@0 | 2531 | |
michael@0 | 2532 | nsRect |
michael@0 | 2533 | nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
michael@0 | 2534 | *aSnap = true; |
michael@0 | 2535 | return mBounds; |
michael@0 | 2536 | } |
michael@0 | 2537 | |
michael@0 | 2538 | nsRect |
michael@0 | 2539 | nsDisplayThemedBackground::GetBoundsInternal() { |
michael@0 | 2540 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 2541 | |
michael@0 | 2542 | nsRect r(nsPoint(0,0), mFrame->GetSize()); |
michael@0 | 2543 | presContext->GetTheme()-> |
michael@0 | 2544 | GetWidgetOverflow(presContext->DeviceContext(), mFrame, |
michael@0 | 2545 | mFrame->StyleDisplay()->mAppearance, &r); |
michael@0 | 2546 | #ifdef XP_MACOSX |
michael@0 | 2547 | // Bug 748219 |
michael@0 | 2548 | r.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel()); |
michael@0 | 2549 | #endif |
michael@0 | 2550 | |
michael@0 | 2551 | return r + ToReferenceFrame(); |
michael@0 | 2552 | } |
michael@0 | 2553 | |
michael@0 | 2554 | void |
michael@0 | 2555 | nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2556 | nsRenderingContext* aCtx) |
michael@0 | 2557 | { |
michael@0 | 2558 | if (mColor == NS_RGBA(0, 0, 0, 0)) { |
michael@0 | 2559 | return; |
michael@0 | 2560 | } |
michael@0 | 2561 | |
michael@0 | 2562 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 2563 | uint32_t flags = aBuilder->GetBackgroundPaintFlags(); |
michael@0 | 2564 | CheckForBorderItem(this, flags); |
michael@0 | 2565 | nsCSSRendering::PaintBackgroundColor(mFrame->PresContext(), *aCtx, mFrame, |
michael@0 | 2566 | mVisibleRect, |
michael@0 | 2567 | nsRect(offset, mFrame->GetSize()), |
michael@0 | 2568 | flags); |
michael@0 | 2569 | } |
michael@0 | 2570 | |
michael@0 | 2571 | nsRegion |
michael@0 | 2572 | nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 2573 | bool* aSnap) |
michael@0 | 2574 | { |
michael@0 | 2575 | if (NS_GET_A(mColor) != 255) { |
michael@0 | 2576 | return nsRegion(); |
michael@0 | 2577 | } |
michael@0 | 2578 | |
michael@0 | 2579 | if (!mBackgroundStyle) |
michael@0 | 2580 | return nsRegion(); |
michael@0 | 2581 | |
michael@0 | 2582 | *aSnap = true; |
michael@0 | 2583 | |
michael@0 | 2584 | const nsStyleBackground::Layer& bottomLayer = mBackgroundStyle->BottomLayer(); |
michael@0 | 2585 | nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 2586 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 2587 | return nsDisplayBackgroundImage::GetInsideClipRegion(this, presContext, bottomLayer.mClip, borderBox, aSnap); |
michael@0 | 2588 | } |
michael@0 | 2589 | |
michael@0 | 2590 | bool |
michael@0 | 2591 | nsDisplayBackgroundColor::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) |
michael@0 | 2592 | { |
michael@0 | 2593 | *aColor = mColor; |
michael@0 | 2594 | |
michael@0 | 2595 | if (!mBackgroundStyle) |
michael@0 | 2596 | return true; |
michael@0 | 2597 | |
michael@0 | 2598 | return (!nsLayoutUtils::HasNonZeroCorner(mFrame->StyleBorder()->mBorderRadius) && |
michael@0 | 2599 | mBackgroundStyle->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER); |
michael@0 | 2600 | } |
michael@0 | 2601 | |
michael@0 | 2602 | void |
michael@0 | 2603 | nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder, |
michael@0 | 2604 | const nsRect& aRect, |
michael@0 | 2605 | HitTestState* aState, |
michael@0 | 2606 | nsTArray<nsIFrame*> *aOutFrames) |
michael@0 | 2607 | { |
michael@0 | 2608 | if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { |
michael@0 | 2609 | // aRect doesn't intersect our border-radius curve. |
michael@0 | 2610 | return; |
michael@0 | 2611 | } |
michael@0 | 2612 | |
michael@0 | 2613 | aOutFrames->AppendElement(mFrame); |
michael@0 | 2614 | } |
michael@0 | 2615 | |
michael@0 | 2616 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 2617 | void |
michael@0 | 2618 | nsDisplayBackgroundColor::WriteDebugInfo(nsACString& aTo) |
michael@0 | 2619 | { |
michael@0 | 2620 | aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)", |
michael@0 | 2621 | NS_GET_R(mColor), NS_GET_G(mColor), |
michael@0 | 2622 | NS_GET_B(mColor), NS_GET_A(mColor)); |
michael@0 | 2623 | } |
michael@0 | 2624 | #endif |
michael@0 | 2625 | |
michael@0 | 2626 | nsRect |
michael@0 | 2627 | nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
michael@0 | 2628 | *aSnap = false; |
michael@0 | 2629 | return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); |
michael@0 | 2630 | } |
michael@0 | 2631 | |
michael@0 | 2632 | void |
michael@0 | 2633 | nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2634 | nsRenderingContext* aCtx) { |
michael@0 | 2635 | // TODO join outlines together |
michael@0 | 2636 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 2637 | nsCSSRendering::PaintOutline(mFrame->PresContext(), *aCtx, mFrame, |
michael@0 | 2638 | mVisibleRect, |
michael@0 | 2639 | nsRect(offset, mFrame->GetSize()), |
michael@0 | 2640 | mFrame->StyleContext()); |
michael@0 | 2641 | } |
michael@0 | 2642 | |
michael@0 | 2643 | bool |
michael@0 | 2644 | nsDisplayOutline::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 2645 | nsRegion* aVisibleRegion, |
michael@0 | 2646 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 2647 | if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, |
michael@0 | 2648 | aAllowVisibleRegionExpansion)) { |
michael@0 | 2649 | return false; |
michael@0 | 2650 | } |
michael@0 | 2651 | |
michael@0 | 2652 | const nsStyleOutline* outline = mFrame->StyleOutline(); |
michael@0 | 2653 | nsRect borderBox(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 2654 | if (borderBox.Contains(aVisibleRegion->GetBounds()) && |
michael@0 | 2655 | !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) { |
michael@0 | 2656 | if (outline->mOutlineOffset >= 0) { |
michael@0 | 2657 | // the visible region is entirely inside the border-rect, and the outline |
michael@0 | 2658 | // isn't rendered inside the border-rect, so the outline is not visible |
michael@0 | 2659 | return false; |
michael@0 | 2660 | } |
michael@0 | 2661 | } |
michael@0 | 2662 | |
michael@0 | 2663 | return true; |
michael@0 | 2664 | } |
michael@0 | 2665 | |
michael@0 | 2666 | void |
michael@0 | 2667 | nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder, |
michael@0 | 2668 | const nsRect& aRect, |
michael@0 | 2669 | HitTestState* aState, |
michael@0 | 2670 | nsTArray<nsIFrame*> *aOutFrames) |
michael@0 | 2671 | { |
michael@0 | 2672 | if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { |
michael@0 | 2673 | // aRect doesn't intersect our border-radius curve. |
michael@0 | 2674 | return; |
michael@0 | 2675 | } |
michael@0 | 2676 | |
michael@0 | 2677 | aOutFrames->AppendElement(mFrame); |
michael@0 | 2678 | } |
michael@0 | 2679 | |
michael@0 | 2680 | void |
michael@0 | 2681 | nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder, |
michael@0 | 2682 | nsIFrame* aFrame) |
michael@0 | 2683 | { |
michael@0 | 2684 | NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame), |
michael@0 | 2685 | "Reference frame mismatch"); |
michael@0 | 2686 | uint8_t pointerEvents = aFrame->StyleVisibility()->mPointerEvents; |
michael@0 | 2687 | if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) { |
michael@0 | 2688 | return; |
michael@0 | 2689 | } |
michael@0 | 2690 | // XXX handle other pointerEvents values for SVG |
michael@0 | 2691 | // XXX Do something clever here for the common case where the border box |
michael@0 | 2692 | // is obviously entirely inside mHitRegion. |
michael@0 | 2693 | nsRect borderBox(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()); |
michael@0 | 2694 | const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder); |
michael@0 | 2695 | bool borderBoxHasRoundedCorners = |
michael@0 | 2696 | nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius); |
michael@0 | 2697 | if (clip) { |
michael@0 | 2698 | borderBox = clip->ApplyNonRoundedIntersection(borderBox); |
michael@0 | 2699 | if (clip->GetRoundedRectCount() > 0) { |
michael@0 | 2700 | borderBoxHasRoundedCorners = true; |
michael@0 | 2701 | } |
michael@0 | 2702 | } |
michael@0 | 2703 | if (borderBoxHasRoundedCorners || |
michael@0 | 2704 | (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { |
michael@0 | 2705 | mMaybeHitRegion.Or(mMaybeHitRegion, borderBox); |
michael@0 | 2706 | } else { |
michael@0 | 2707 | mHitRegion.Or(mHitRegion, borderBox); |
michael@0 | 2708 | } |
michael@0 | 2709 | if (aBuilder->GetAncestorHasTouchEventHandler()) { |
michael@0 | 2710 | mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox); |
michael@0 | 2711 | } |
michael@0 | 2712 | } |
michael@0 | 2713 | |
michael@0 | 2714 | void |
michael@0 | 2715 | nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2716 | nsRenderingContext* aCtx) { |
michael@0 | 2717 | // Note: Because we exist, we know that the caret is visible, so we don't |
michael@0 | 2718 | // need to check for the caret's visibility. |
michael@0 | 2719 | mCaret->PaintCaret(aBuilder, aCtx, mFrame, ToReferenceFrame()); |
michael@0 | 2720 | } |
michael@0 | 2721 | |
michael@0 | 2722 | bool |
michael@0 | 2723 | nsDisplayBorder::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 2724 | nsRegion* aVisibleRegion, |
michael@0 | 2725 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 2726 | if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, |
michael@0 | 2727 | aAllowVisibleRegionExpansion)) { |
michael@0 | 2728 | return false; |
michael@0 | 2729 | } |
michael@0 | 2730 | |
michael@0 | 2731 | nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() + |
michael@0 | 2732 | ToReferenceFrame(); |
michael@0 | 2733 | const nsStyleBorder *styleBorder; |
michael@0 | 2734 | if (paddingRect.Contains(aVisibleRegion->GetBounds()) && |
michael@0 | 2735 | !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded() && |
michael@0 | 2736 | !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) { |
michael@0 | 2737 | // the visible region is entirely inside the content rect, and no part |
michael@0 | 2738 | // of the border is rendered inside the content rect, so we are not |
michael@0 | 2739 | // visible |
michael@0 | 2740 | // Skip this if there's a border-image (which draws a background |
michael@0 | 2741 | // too) or if there is a border-radius (which makes the border draw |
michael@0 | 2742 | // further in). |
michael@0 | 2743 | return false; |
michael@0 | 2744 | } |
michael@0 | 2745 | |
michael@0 | 2746 | return true; |
michael@0 | 2747 | } |
michael@0 | 2748 | |
michael@0 | 2749 | nsDisplayItemGeometry* |
michael@0 | 2750 | nsDisplayBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder) |
michael@0 | 2751 | { |
michael@0 | 2752 | return new nsDisplayBorderGeometry(this, aBuilder); |
michael@0 | 2753 | } |
michael@0 | 2754 | |
michael@0 | 2755 | void |
michael@0 | 2756 | nsDisplayBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 2757 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 2758 | nsRegion* aInvalidRegion) |
michael@0 | 2759 | { |
michael@0 | 2760 | const nsDisplayBorderGeometry* geometry = static_cast<const nsDisplayBorderGeometry*>(aGeometry); |
michael@0 | 2761 | bool snap; |
michael@0 | 2762 | if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) || |
michael@0 | 2763 | !geometry->mContentRect.IsEqualInterior(GetContentRect())) { |
michael@0 | 2764 | // We can probably get away with only invalidating the difference |
michael@0 | 2765 | // between the border and padding rects, but the XUL ui at least |
michael@0 | 2766 | // is apparently painting a background with this? |
michael@0 | 2767 | aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds); |
michael@0 | 2768 | } |
michael@0 | 2769 | } |
michael@0 | 2770 | |
michael@0 | 2771 | void |
michael@0 | 2772 | nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2773 | nsRenderingContext* aCtx) { |
michael@0 | 2774 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 2775 | nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame, |
michael@0 | 2776 | mVisibleRect, |
michael@0 | 2777 | nsRect(offset, mFrame->GetSize()), |
michael@0 | 2778 | mFrame->StyleContext(), |
michael@0 | 2779 | mFrame->GetSkipSides()); |
michael@0 | 2780 | } |
michael@0 | 2781 | |
michael@0 | 2782 | nsRect |
michael@0 | 2783 | nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
michael@0 | 2784 | { |
michael@0 | 2785 | *aSnap = true; |
michael@0 | 2786 | return CalculateBounds(*mFrame->StyleBorder()); |
michael@0 | 2787 | } |
michael@0 | 2788 | |
michael@0 | 2789 | nsRect |
michael@0 | 2790 | nsDisplayBorder::CalculateBounds(const nsStyleBorder& aStyleBorder) |
michael@0 | 2791 | { |
michael@0 | 2792 | nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize()); |
michael@0 | 2793 | if (aStyleBorder.IsBorderImageLoaded()) { |
michael@0 | 2794 | borderBounds.Inflate(aStyleBorder.GetImageOutset()); |
michael@0 | 2795 | return borderBounds; |
michael@0 | 2796 | } else { |
michael@0 | 2797 | nsMargin border = aStyleBorder.GetComputedBorder(); |
michael@0 | 2798 | nsRect result; |
michael@0 | 2799 | if (border.top > 0) { |
michael@0 | 2800 | result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(), border.top); |
michael@0 | 2801 | } |
michael@0 | 2802 | if (border.right > 0) { |
michael@0 | 2803 | result.UnionRect(result, nsRect(borderBounds.XMost() - border.right, borderBounds.Y(), border.right, borderBounds.Height())); |
michael@0 | 2804 | } |
michael@0 | 2805 | if (border.bottom > 0) { |
michael@0 | 2806 | result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.YMost() - border.bottom, borderBounds.Width(), border.bottom)); |
michael@0 | 2807 | } |
michael@0 | 2808 | if (border.left > 0) { |
michael@0 | 2809 | result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.Y(), border.left, borderBounds.Height())); |
michael@0 | 2810 | } |
michael@0 | 2811 | |
michael@0 | 2812 | return result; |
michael@0 | 2813 | } |
michael@0 | 2814 | } |
michael@0 | 2815 | |
michael@0 | 2816 | // Given a region, compute a conservative approximation to it as a list |
michael@0 | 2817 | // of rectangles that aren't vertically adjacent (i.e., vertically |
michael@0 | 2818 | // adjacent or overlapping rectangles are combined). |
michael@0 | 2819 | // Right now this is only approximate, some vertically overlapping rectangles |
michael@0 | 2820 | // aren't guaranteed to be combined. |
michael@0 | 2821 | static void |
michael@0 | 2822 | ComputeDisjointRectangles(const nsRegion& aRegion, |
michael@0 | 2823 | nsTArray<nsRect>* aRects) { |
michael@0 | 2824 | nscoord accumulationMargin = nsPresContext::CSSPixelsToAppUnits(25); |
michael@0 | 2825 | nsRect accumulated; |
michael@0 | 2826 | nsRegionRectIterator iter(aRegion); |
michael@0 | 2827 | while (true) { |
michael@0 | 2828 | const nsRect* r = iter.Next(); |
michael@0 | 2829 | if (r && !accumulated.IsEmpty() && |
michael@0 | 2830 | accumulated.YMost() >= r->y - accumulationMargin) { |
michael@0 | 2831 | accumulated.UnionRect(accumulated, *r); |
michael@0 | 2832 | continue; |
michael@0 | 2833 | } |
michael@0 | 2834 | |
michael@0 | 2835 | if (!accumulated.IsEmpty()) { |
michael@0 | 2836 | aRects->AppendElement(accumulated); |
michael@0 | 2837 | accumulated.SetEmpty(); |
michael@0 | 2838 | } |
michael@0 | 2839 | |
michael@0 | 2840 | if (!r) |
michael@0 | 2841 | break; |
michael@0 | 2842 | |
michael@0 | 2843 | accumulated = *r; |
michael@0 | 2844 | } |
michael@0 | 2845 | } |
michael@0 | 2846 | |
michael@0 | 2847 | void |
michael@0 | 2848 | nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2849 | nsRenderingContext* aCtx) { |
michael@0 | 2850 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 2851 | nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset; |
michael@0 | 2852 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 2853 | nsAutoTArray<nsRect,10> rects; |
michael@0 | 2854 | ComputeDisjointRectangles(mVisibleRegion, &rects); |
michael@0 | 2855 | |
michael@0 | 2856 | PROFILER_LABEL("nsDisplayBoxShadowOuter", "Paint"); |
michael@0 | 2857 | for (uint32_t i = 0; i < rects.Length(); ++i) { |
michael@0 | 2858 | aCtx->PushState(); |
michael@0 | 2859 | aCtx->IntersectClip(rects[i]); |
michael@0 | 2860 | nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, |
michael@0 | 2861 | borderRect, rects[i], mOpacity); |
michael@0 | 2862 | aCtx->PopState(); |
michael@0 | 2863 | } |
michael@0 | 2864 | } |
michael@0 | 2865 | |
michael@0 | 2866 | nsRect |
michael@0 | 2867 | nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
michael@0 | 2868 | *aSnap = false; |
michael@0 | 2869 | return mBounds; |
michael@0 | 2870 | } |
michael@0 | 2871 | |
michael@0 | 2872 | nsRect |
michael@0 | 2873 | nsDisplayBoxShadowOuter::GetBoundsInternal() { |
michael@0 | 2874 | return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) + |
michael@0 | 2875 | ToReferenceFrame(); |
michael@0 | 2876 | } |
michael@0 | 2877 | |
michael@0 | 2878 | bool |
michael@0 | 2879 | nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 2880 | nsRegion* aVisibleRegion, |
michael@0 | 2881 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 2882 | if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, |
michael@0 | 2883 | aAllowVisibleRegionExpansion)) { |
michael@0 | 2884 | return false; |
michael@0 | 2885 | } |
michael@0 | 2886 | |
michael@0 | 2887 | // Store the actual visible region |
michael@0 | 2888 | mVisibleRegion.And(*aVisibleRegion, mVisibleRect); |
michael@0 | 2889 | |
michael@0 | 2890 | nsPoint origin = ToReferenceFrame(); |
michael@0 | 2891 | nsRect visibleBounds = aVisibleRegion->GetBounds(); |
michael@0 | 2892 | nsRect frameRect(origin, mFrame->GetSize()); |
michael@0 | 2893 | if (!frameRect.Contains(visibleBounds)) |
michael@0 | 2894 | return true; |
michael@0 | 2895 | |
michael@0 | 2896 | // the visible region is entirely inside the border-rect, and box shadows |
michael@0 | 2897 | // never render within the border-rect (unless there's a border radius). |
michael@0 | 2898 | nscoord twipsRadii[8]; |
michael@0 | 2899 | bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii); |
michael@0 | 2900 | if (!hasBorderRadii) |
michael@0 | 2901 | return false; |
michael@0 | 2902 | |
michael@0 | 2903 | return !RoundedRectContainsRect(frameRect, twipsRadii, visibleBounds); |
michael@0 | 2904 | } |
michael@0 | 2905 | |
michael@0 | 2906 | void |
michael@0 | 2907 | nsDisplayBoxShadowOuter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 2908 | const nsDisplayItemGeometry* aGeometry, |
michael@0 | 2909 | nsRegion* aInvalidRegion) |
michael@0 | 2910 | { |
michael@0 | 2911 | const nsDisplayItemGenericGeometry* geometry = |
michael@0 | 2912 | static_cast<const nsDisplayItemGenericGeometry*>(aGeometry); |
michael@0 | 2913 | bool snap; |
michael@0 | 2914 | if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) || |
michael@0 | 2915 | !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) { |
michael@0 | 2916 | nsRegion oldShadow, newShadow; |
michael@0 | 2917 | nscoord dontCare[8]; |
michael@0 | 2918 | bool hasBorderRadius = mFrame->GetBorderRadii(dontCare); |
michael@0 | 2919 | if (hasBorderRadius) { |
michael@0 | 2920 | // If we have rounded corners then we need to invalidate the frame area |
michael@0 | 2921 | // too since we paint into it. |
michael@0 | 2922 | oldShadow = geometry->mBounds; |
michael@0 | 2923 | newShadow = GetBounds(aBuilder, &snap); |
michael@0 | 2924 | } else { |
michael@0 | 2925 | oldShadow = oldShadow.Sub(geometry->mBounds, geometry->mBorderRect); |
michael@0 | 2926 | newShadow = newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect()); |
michael@0 | 2927 | } |
michael@0 | 2928 | aInvalidRegion->Or(oldShadow, newShadow); |
michael@0 | 2929 | } |
michael@0 | 2930 | } |
michael@0 | 2931 | |
michael@0 | 2932 | |
michael@0 | 2933 | void |
michael@0 | 2934 | nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 2935 | nsRenderingContext* aCtx) { |
michael@0 | 2936 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 2937 | nsRect borderRect = nsRect(offset, mFrame->GetSize()); |
michael@0 | 2938 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 2939 | nsAutoTArray<nsRect,10> rects; |
michael@0 | 2940 | ComputeDisjointRectangles(mVisibleRegion, &rects); |
michael@0 | 2941 | |
michael@0 | 2942 | PROFILER_LABEL("nsDisplayBoxShadowInner", "Paint"); |
michael@0 | 2943 | for (uint32_t i = 0; i < rects.Length(); ++i) { |
michael@0 | 2944 | aCtx->PushState(); |
michael@0 | 2945 | aCtx->IntersectClip(rects[i]); |
michael@0 | 2946 | nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, |
michael@0 | 2947 | borderRect, rects[i]); |
michael@0 | 2948 | aCtx->PopState(); |
michael@0 | 2949 | } |
michael@0 | 2950 | } |
michael@0 | 2951 | |
michael@0 | 2952 | bool |
michael@0 | 2953 | nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 2954 | nsRegion* aVisibleRegion, |
michael@0 | 2955 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 2956 | if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, |
michael@0 | 2957 | aAllowVisibleRegionExpansion)) { |
michael@0 | 2958 | return false; |
michael@0 | 2959 | } |
michael@0 | 2960 | |
michael@0 | 2961 | // Store the actual visible region |
michael@0 | 2962 | mVisibleRegion.And(*aVisibleRegion, mVisibleRect); |
michael@0 | 2963 | return true; |
michael@0 | 2964 | } |
michael@0 | 2965 | |
michael@0 | 2966 | nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, |
michael@0 | 2967 | nsIFrame* aFrame, nsDisplayList* aList) |
michael@0 | 2968 | : nsDisplayItem(aBuilder, aFrame) |
michael@0 | 2969 | , mOverrideZIndex(0) |
michael@0 | 2970 | { |
michael@0 | 2971 | mList.AppendToTop(aList); |
michael@0 | 2972 | UpdateBounds(aBuilder); |
michael@0 | 2973 | |
michael@0 | 2974 | if (!aFrame || !aFrame->IsTransformed()) { |
michael@0 | 2975 | return; |
michael@0 | 2976 | } |
michael@0 | 2977 | |
michael@0 | 2978 | // If the frame is a preserve-3d parent, then we will create transforms |
michael@0 | 2979 | // inside this list afterwards (see WrapPreserve3DList in nsFrame.cpp). |
michael@0 | 2980 | // In this case we will always be outside of the transform, so share |
michael@0 | 2981 | // our parents reference frame. |
michael@0 | 2982 | if (aFrame->Preserves3DChildren()) { |
michael@0 | 2983 | mReferenceFrame = |
michael@0 | 2984 | aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); |
michael@0 | 2985 | mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); |
michael@0 | 2986 | return; |
michael@0 | 2987 | } |
michael@0 | 2988 | |
michael@0 | 2989 | // If we're a transformed frame, then we need to find out if we're inside |
michael@0 | 2990 | // the nsDisplayTransform or outside of it. Frames inside the transform |
michael@0 | 2991 | // need mReferenceFrame == mFrame, outside needs the next ancestor |
michael@0 | 2992 | // reference frame. |
michael@0 | 2993 | // If we're inside the transform, then the nsDisplayItem constructor |
michael@0 | 2994 | // will have done the right thing. |
michael@0 | 2995 | // If we're outside the transform, then we should have only one child |
michael@0 | 2996 | // (since nsDisplayTransform wraps all actual content), and that child |
michael@0 | 2997 | // will have the correct reference frame set (since nsDisplayTransform |
michael@0 | 2998 | // handles this explictly). |
michael@0 | 2999 | // |
michael@0 | 3000 | // Preserve-3d can cause us to have multiple nsDisplayTransform |
michael@0 | 3001 | // children. |
michael@0 | 3002 | nsDisplayItem *i = mList.GetBottom(); |
michael@0 | 3003 | if (i && (!i->GetAbove() || i->GetType() == TYPE_TRANSFORM) && |
michael@0 | 3004 | i->Frame() == mFrame) { |
michael@0 | 3005 | mReferenceFrame = i->ReferenceFrame(); |
michael@0 | 3006 | mToReferenceFrame = i->ToReferenceFrame(); |
michael@0 | 3007 | } |
michael@0 | 3008 | } |
michael@0 | 3009 | |
michael@0 | 3010 | nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, |
michael@0 | 3011 | nsIFrame* aFrame, nsDisplayItem* aItem) |
michael@0 | 3012 | : nsDisplayItem(aBuilder, aFrame) |
michael@0 | 3013 | , mOverrideZIndex(0) |
michael@0 | 3014 | { |
michael@0 | 3015 | mList.AppendToTop(aItem); |
michael@0 | 3016 | UpdateBounds(aBuilder); |
michael@0 | 3017 | |
michael@0 | 3018 | if (!aFrame || !aFrame->IsTransformed()) { |
michael@0 | 3019 | return; |
michael@0 | 3020 | } |
michael@0 | 3021 | |
michael@0 | 3022 | if (aFrame->Preserves3DChildren()) { |
michael@0 | 3023 | mReferenceFrame = |
michael@0 | 3024 | aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); |
michael@0 | 3025 | mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); |
michael@0 | 3026 | return; |
michael@0 | 3027 | } |
michael@0 | 3028 | |
michael@0 | 3029 | // See the previous nsDisplayWrapList constructor |
michael@0 | 3030 | if (aItem->Frame() == aFrame) { |
michael@0 | 3031 | mReferenceFrame = aItem->ReferenceFrame(); |
michael@0 | 3032 | mToReferenceFrame = aItem->ToReferenceFrame(); |
michael@0 | 3033 | } |
michael@0 | 3034 | } |
michael@0 | 3035 | |
michael@0 | 3036 | nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, |
michael@0 | 3037 | nsIFrame* aFrame, nsDisplayItem* aItem, |
michael@0 | 3038 | const nsIFrame* aReferenceFrame, |
michael@0 | 3039 | const nsPoint& aToReferenceFrame) |
michael@0 | 3040 | : nsDisplayItem(aBuilder, aFrame, aReferenceFrame, aToReferenceFrame) |
michael@0 | 3041 | , mOverrideZIndex(0) |
michael@0 | 3042 | { |
michael@0 | 3043 | mList.AppendToTop(aItem); |
michael@0 | 3044 | mBounds = mList.GetBounds(aBuilder); |
michael@0 | 3045 | } |
michael@0 | 3046 | |
michael@0 | 3047 | nsDisplayWrapList::~nsDisplayWrapList() { |
michael@0 | 3048 | mList.DeleteAll(); |
michael@0 | 3049 | } |
michael@0 | 3050 | |
michael@0 | 3051 | void |
michael@0 | 3052 | nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
michael@0 | 3053 | HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) { |
michael@0 | 3054 | mList.HitTest(aBuilder, aRect, aState, aOutFrames); |
michael@0 | 3055 | } |
michael@0 | 3056 | |
michael@0 | 3057 | nsRect |
michael@0 | 3058 | nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
michael@0 | 3059 | *aSnap = false; |
michael@0 | 3060 | return mBounds; |
michael@0 | 3061 | } |
michael@0 | 3062 | |
michael@0 | 3063 | bool |
michael@0 | 3064 | nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 3065 | nsRegion* aVisibleRegion, |
michael@0 | 3066 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 3067 | // Convert the passed in visible region to our appunits. |
michael@0 | 3068 | nsRegion visibleRegion; |
michael@0 | 3069 | // mVisibleRect has been clipped to GetClippedBounds |
michael@0 | 3070 | visibleRegion.And(*aVisibleRegion, mVisibleRect); |
michael@0 | 3071 | nsRegion originalVisibleRegion = visibleRegion; |
michael@0 | 3072 | |
michael@0 | 3073 | bool retval = |
michael@0 | 3074 | mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion, |
michael@0 | 3075 | mVisibleRect, |
michael@0 | 3076 | aAllowVisibleRegionExpansion); |
michael@0 | 3077 | |
michael@0 | 3078 | nsRegion removed; |
michael@0 | 3079 | // removed = originalVisibleRegion - visibleRegion |
michael@0 | 3080 | removed.Sub(originalVisibleRegion, visibleRegion); |
michael@0 | 3081 | // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications |
michael@0 | 3082 | // SubtractFromVisibleRegion does) |
michael@0 | 3083 | aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed); |
michael@0 | 3084 | |
michael@0 | 3085 | return retval; |
michael@0 | 3086 | } |
michael@0 | 3087 | |
michael@0 | 3088 | nsRegion |
michael@0 | 3089 | nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 3090 | bool* aSnap) { |
michael@0 | 3091 | *aSnap = false; |
michael@0 | 3092 | nsRegion result; |
michael@0 | 3093 | if (mList.IsOpaque()) { |
michael@0 | 3094 | // Everything within GetBounds that's visible is opaque. |
michael@0 | 3095 | result = GetBounds(aBuilder, aSnap); |
michael@0 | 3096 | } |
michael@0 | 3097 | return result; |
michael@0 | 3098 | } |
michael@0 | 3099 | |
michael@0 | 3100 | bool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { |
michael@0 | 3101 | // We could try to do something but let's conservatively just return false. |
michael@0 | 3102 | return false; |
michael@0 | 3103 | } |
michael@0 | 3104 | |
michael@0 | 3105 | bool nsDisplayWrapList::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, |
michael@0 | 3106 | nsIFrame* aFrame) { |
michael@0 | 3107 | NS_WARNING("nsDisplayWrapList::IsVaryingRelativeToMovingFrame called unexpectedly"); |
michael@0 | 3108 | // We could try to do something but let's conservatively just return true. |
michael@0 | 3109 | return true; |
michael@0 | 3110 | } |
michael@0 | 3111 | |
michael@0 | 3112 | void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 3113 | nsRenderingContext* aCtx) { |
michael@0 | 3114 | NS_ERROR("nsDisplayWrapList should have been flattened away for painting"); |
michael@0 | 3115 | } |
michael@0 | 3116 | |
michael@0 | 3117 | /** |
michael@0 | 3118 | * Returns true if all descendant display items can be placed in the same |
michael@0 | 3119 | * ThebesLayer --- GetLayerState returns LAYER_INACTIVE or LAYER_NONE, |
michael@0 | 3120 | * and they all have the expected animated geometry root. |
michael@0 | 3121 | */ |
michael@0 | 3122 | static LayerState |
michael@0 | 3123 | RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder, |
michael@0 | 3124 | LayerManager* aManager, |
michael@0 | 3125 | const ContainerLayerParameters& aParameters, |
michael@0 | 3126 | const nsDisplayList& aList, |
michael@0 | 3127 | nsIFrame* aExpectedAnimatedGeometryRootForChildren) |
michael@0 | 3128 | { |
michael@0 | 3129 | LayerState result = LAYER_INACTIVE; |
michael@0 | 3130 | for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { |
michael@0 | 3131 | if (result == LAYER_INACTIVE && |
michael@0 | 3132 | nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) != |
michael@0 | 3133 | aExpectedAnimatedGeometryRootForChildren) { |
michael@0 | 3134 | result = LAYER_ACTIVE; |
michael@0 | 3135 | } |
michael@0 | 3136 | |
michael@0 | 3137 | LayerState state = i->GetLayerState(aBuilder, aManager, aParameters); |
michael@0 | 3138 | if ((state == LAYER_ACTIVE || state == LAYER_ACTIVE_FORCE) && |
michael@0 | 3139 | state > result) { |
michael@0 | 3140 | result = state; |
michael@0 | 3141 | } |
michael@0 | 3142 | if (state == LAYER_ACTIVE_EMPTY && state > result) { |
michael@0 | 3143 | result = LAYER_ACTIVE_FORCE; |
michael@0 | 3144 | } |
michael@0 | 3145 | if (state == LAYER_NONE) { |
michael@0 | 3146 | nsDisplayList* list = i->GetSameCoordinateSystemChildren(); |
michael@0 | 3147 | if (list) { |
michael@0 | 3148 | LayerState childState = |
michael@0 | 3149 | RequiredLayerStateForChildren(aBuilder, aManager, aParameters, *list, |
michael@0 | 3150 | aExpectedAnimatedGeometryRootForChildren); |
michael@0 | 3151 | if (childState > result) { |
michael@0 | 3152 | result = childState; |
michael@0 | 3153 | } |
michael@0 | 3154 | } |
michael@0 | 3155 | } |
michael@0 | 3156 | } |
michael@0 | 3157 | return result; |
michael@0 | 3158 | } |
michael@0 | 3159 | |
michael@0 | 3160 | nsRect nsDisplayWrapList::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) |
michael@0 | 3161 | { |
michael@0 | 3162 | nsRect bounds; |
michael@0 | 3163 | for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) { |
michael@0 | 3164 | bounds.UnionRect(bounds, i->GetComponentAlphaBounds(aBuilder)); |
michael@0 | 3165 | } |
michael@0 | 3166 | return bounds; |
michael@0 | 3167 | } |
michael@0 | 3168 | |
michael@0 | 3169 | static nsresult |
michael@0 | 3170 | WrapDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, |
michael@0 | 3171 | nsDisplayList* aList, nsDisplayWrapper* aWrapper) { |
michael@0 | 3172 | if (!aList->GetTop()) |
michael@0 | 3173 | return NS_OK; |
michael@0 | 3174 | nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList); |
michael@0 | 3175 | if (!item) |
michael@0 | 3176 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 3177 | // aList was emptied |
michael@0 | 3178 | aList->AppendToTop(item); |
michael@0 | 3179 | return NS_OK; |
michael@0 | 3180 | } |
michael@0 | 3181 | |
michael@0 | 3182 | static nsresult |
michael@0 | 3183 | WrapEachDisplayItem(nsDisplayListBuilder* aBuilder, |
michael@0 | 3184 | nsDisplayList* aList, nsDisplayWrapper* aWrapper) { |
michael@0 | 3185 | nsDisplayList newList; |
michael@0 | 3186 | nsDisplayItem* item; |
michael@0 | 3187 | while ((item = aList->RemoveBottom())) { |
michael@0 | 3188 | item = aWrapper->WrapItem(aBuilder, item); |
michael@0 | 3189 | if (!item) |
michael@0 | 3190 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 3191 | newList.AppendToTop(item); |
michael@0 | 3192 | } |
michael@0 | 3193 | // aList was emptied |
michael@0 | 3194 | aList->AppendToTop(&newList); |
michael@0 | 3195 | return NS_OK; |
michael@0 | 3196 | } |
michael@0 | 3197 | |
michael@0 | 3198 | nsresult nsDisplayWrapper::WrapLists(nsDisplayListBuilder* aBuilder, |
michael@0 | 3199 | nsIFrame* aFrame, const nsDisplayListSet& aIn, const nsDisplayListSet& aOut) |
michael@0 | 3200 | { |
michael@0 | 3201 | nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn); |
michael@0 | 3202 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3203 | |
michael@0 | 3204 | if (&aOut == &aIn) |
michael@0 | 3205 | return NS_OK; |
michael@0 | 3206 | aOut.BorderBackground()->AppendToTop(aIn.BorderBackground()); |
michael@0 | 3207 | aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds()); |
michael@0 | 3208 | aOut.Floats()->AppendToTop(aIn.Floats()); |
michael@0 | 3209 | aOut.Content()->AppendToTop(aIn.Content()); |
michael@0 | 3210 | aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants()); |
michael@0 | 3211 | aOut.Outlines()->AppendToTop(aIn.Outlines()); |
michael@0 | 3212 | return NS_OK; |
michael@0 | 3213 | } |
michael@0 | 3214 | |
michael@0 | 3215 | nsresult nsDisplayWrapper::WrapListsInPlace(nsDisplayListBuilder* aBuilder, |
michael@0 | 3216 | nsIFrame* aFrame, const nsDisplayListSet& aLists) |
michael@0 | 3217 | { |
michael@0 | 3218 | nsresult rv; |
michael@0 | 3219 | if (WrapBorderBackground()) { |
michael@0 | 3220 | // Our border-backgrounds are in-flow |
michael@0 | 3221 | rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this); |
michael@0 | 3222 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3223 | } |
michael@0 | 3224 | // Our block border-backgrounds are in-flow |
michael@0 | 3225 | rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this); |
michael@0 | 3226 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3227 | // The floats are not in flow |
michael@0 | 3228 | rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this); |
michael@0 | 3229 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3230 | // Our child content is in flow |
michael@0 | 3231 | rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this); |
michael@0 | 3232 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3233 | // The positioned descendants may not be in-flow |
michael@0 | 3234 | rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this); |
michael@0 | 3235 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3236 | // The outlines may not be in-flow |
michael@0 | 3237 | return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this); |
michael@0 | 3238 | } |
michael@0 | 3239 | |
michael@0 | 3240 | nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder, |
michael@0 | 3241 | nsIFrame* aFrame, nsDisplayList* aList) |
michael@0 | 3242 | : nsDisplayWrapList(aBuilder, aFrame, aList) { |
michael@0 | 3243 | MOZ_COUNT_CTOR(nsDisplayOpacity); |
michael@0 | 3244 | } |
michael@0 | 3245 | |
michael@0 | 3246 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3247 | nsDisplayOpacity::~nsDisplayOpacity() { |
michael@0 | 3248 | MOZ_COUNT_DTOR(nsDisplayOpacity); |
michael@0 | 3249 | } |
michael@0 | 3250 | #endif |
michael@0 | 3251 | |
michael@0 | 3252 | nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 3253 | bool* aSnap) { |
michael@0 | 3254 | *aSnap = false; |
michael@0 | 3255 | // We are never opaque, if our opacity was < 1 then we wouldn't have |
michael@0 | 3256 | // been created. |
michael@0 | 3257 | return nsRegion(); |
michael@0 | 3258 | } |
michael@0 | 3259 | |
michael@0 | 3260 | // nsDisplayOpacity uses layers for rendering |
michael@0 | 3261 | already_AddRefed<Layer> |
michael@0 | 3262 | nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3263 | LayerManager* aManager, |
michael@0 | 3264 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3265 | if (mFrame->StyleDisplay()->mOpacity == 0 && mFrame->GetContent() && |
michael@0 | 3266 | !nsLayoutUtils::HasAnimations(mFrame->GetContent(), eCSSProperty_opacity)) { |
michael@0 | 3267 | return nullptr; |
michael@0 | 3268 | } |
michael@0 | 3269 | nsRefPtr<Layer> container = aManager->GetLayerBuilder()-> |
michael@0 | 3270 | BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
michael@0 | 3271 | aContainerParameters, nullptr); |
michael@0 | 3272 | if (!container) |
michael@0 | 3273 | return nullptr; |
michael@0 | 3274 | |
michael@0 | 3275 | container->SetOpacity(mFrame->StyleDisplay()->mOpacity); |
michael@0 | 3276 | nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder, |
michael@0 | 3277 | this, mFrame, |
michael@0 | 3278 | eCSSProperty_opacity); |
michael@0 | 3279 | return container.forget(); |
michael@0 | 3280 | } |
michael@0 | 3281 | |
michael@0 | 3282 | /** |
michael@0 | 3283 | * This doesn't take into account layer scaling --- the layer may be |
michael@0 | 3284 | * rendered at a higher (or lower) resolution, affecting the retained layer |
michael@0 | 3285 | * size --- but this should be good enough. |
michael@0 | 3286 | */ |
michael@0 | 3287 | static bool |
michael@0 | 3288 | IsItemTooSmallForActiveLayer(nsDisplayItem* aItem) |
michael@0 | 3289 | { |
michael@0 | 3290 | nsIntRect visibleDevPixels = aItem->GetVisibleRect().ToOutsidePixels( |
michael@0 | 3291 | aItem->Frame()->PresContext()->AppUnitsPerDevPixel()); |
michael@0 | 3292 | static const int MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS = 16; |
michael@0 | 3293 | return visibleDevPixels.Size() < |
michael@0 | 3294 | nsIntSize(MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS, MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS); |
michael@0 | 3295 | } |
michael@0 | 3296 | |
michael@0 | 3297 | bool |
michael@0 | 3298 | nsDisplayOpacity::NeedsActiveLayer() |
michael@0 | 3299 | { |
michael@0 | 3300 | if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity) && |
michael@0 | 3301 | !IsItemTooSmallForActiveLayer(this)) |
michael@0 | 3302 | return true; |
michael@0 | 3303 | if (mFrame->GetContent()) { |
michael@0 | 3304 | if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(), |
michael@0 | 3305 | eCSSProperty_opacity)) { |
michael@0 | 3306 | return true; |
michael@0 | 3307 | } |
michael@0 | 3308 | } |
michael@0 | 3309 | return false; |
michael@0 | 3310 | } |
michael@0 | 3311 | |
michael@0 | 3312 | bool |
michael@0 | 3313 | nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) |
michael@0 | 3314 | { |
michael@0 | 3315 | if (NeedsActiveLayer()) |
michael@0 | 3316 | return false; |
michael@0 | 3317 | |
michael@0 | 3318 | nsDisplayItem* child = mList.GetBottom(); |
michael@0 | 3319 | // Only try folding our opacity down if we have a single |
michael@0 | 3320 | // child. We could potentially do this also if we had multiple |
michael@0 | 3321 | // children as long as they don't overlap. |
michael@0 | 3322 | if (!child || child->GetAbove()) { |
michael@0 | 3323 | return false; |
michael@0 | 3324 | } |
michael@0 | 3325 | |
michael@0 | 3326 | return child->ApplyOpacity(aBuilder, mFrame->StyleDisplay()->mOpacity, mClip); |
michael@0 | 3327 | } |
michael@0 | 3328 | |
michael@0 | 3329 | nsDisplayItem::LayerState |
michael@0 | 3330 | nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, |
michael@0 | 3331 | LayerManager* aManager, |
michael@0 | 3332 | const ContainerLayerParameters& aParameters) { |
michael@0 | 3333 | if (NeedsActiveLayer()) |
michael@0 | 3334 | return LAYER_ACTIVE; |
michael@0 | 3335 | |
michael@0 | 3336 | return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, |
michael@0 | 3337 | nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder)); |
michael@0 | 3338 | } |
michael@0 | 3339 | |
michael@0 | 3340 | bool |
michael@0 | 3341 | nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 3342 | nsRegion* aVisibleRegion, |
michael@0 | 3343 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 3344 | // Our children are translucent so we should not allow them to subtract |
michael@0 | 3345 | // area from aVisibleRegion. We do need to find out what is visible under |
michael@0 | 3346 | // our children in the temporary compositing buffer, because if our children |
michael@0 | 3347 | // paint our entire bounds opaquely then we don't need an alpha channel in |
michael@0 | 3348 | // the temporary compositing buffer. |
michael@0 | 3349 | nsRect bounds = GetClippedBounds(aBuilder); |
michael@0 | 3350 | nsRegion visibleUnderChildren; |
michael@0 | 3351 | visibleUnderChildren.And(*aVisibleRegion, bounds); |
michael@0 | 3352 | nsRect allowExpansion = bounds.Intersect(aAllowVisibleRegionExpansion); |
michael@0 | 3353 | return |
michael@0 | 3354 | nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren, |
michael@0 | 3355 | allowExpansion); |
michael@0 | 3356 | } |
michael@0 | 3357 | |
michael@0 | 3358 | bool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { |
michael@0 | 3359 | if (aItem->GetType() != TYPE_OPACITY) |
michael@0 | 3360 | return false; |
michael@0 | 3361 | // items for the same content element should be merged into a single |
michael@0 | 3362 | // compositing group |
michael@0 | 3363 | // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity |
michael@0 | 3364 | if (aItem->Frame()->GetContent() != mFrame->GetContent()) |
michael@0 | 3365 | return false; |
michael@0 | 3366 | if (aItem->GetClip() != GetClip()) |
michael@0 | 3367 | return false; |
michael@0 | 3368 | MergeFromTrackingMergedFrames(static_cast<nsDisplayOpacity*>(aItem)); |
michael@0 | 3369 | return true; |
michael@0 | 3370 | } |
michael@0 | 3371 | |
michael@0 | 3372 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 3373 | void |
michael@0 | 3374 | nsDisplayOpacity::WriteDebugInfo(nsACString& aTo) |
michael@0 | 3375 | { |
michael@0 | 3376 | aTo += nsPrintfCString(" (opacity %f)", mFrame->StyleDisplay()->mOpacity); |
michael@0 | 3377 | } |
michael@0 | 3378 | #endif |
michael@0 | 3379 | |
michael@0 | 3380 | nsDisplayMixBlendMode::nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder, |
michael@0 | 3381 | nsIFrame* aFrame, nsDisplayList* aList, |
michael@0 | 3382 | uint32_t aFlags) |
michael@0 | 3383 | : nsDisplayWrapList(aBuilder, aFrame, aList) { |
michael@0 | 3384 | MOZ_COUNT_CTOR(nsDisplayMixBlendMode); |
michael@0 | 3385 | } |
michael@0 | 3386 | |
michael@0 | 3387 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3388 | nsDisplayMixBlendMode::~nsDisplayMixBlendMode() { |
michael@0 | 3389 | MOZ_COUNT_DTOR(nsDisplayMixBlendMode); |
michael@0 | 3390 | } |
michael@0 | 3391 | #endif |
michael@0 | 3392 | |
michael@0 | 3393 | nsRegion nsDisplayMixBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 3394 | bool* aSnap) { |
michael@0 | 3395 | *aSnap = false; |
michael@0 | 3396 | // We are never considered opaque |
michael@0 | 3397 | return nsRegion(); |
michael@0 | 3398 | } |
michael@0 | 3399 | |
michael@0 | 3400 | // nsDisplayMixBlendMode uses layers for rendering |
michael@0 | 3401 | already_AddRefed<Layer> |
michael@0 | 3402 | nsDisplayMixBlendMode::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3403 | LayerManager* aManager, |
michael@0 | 3404 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3405 | ContainerLayerParameters newContainerParameters = aContainerParameters; |
michael@0 | 3406 | newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; |
michael@0 | 3407 | |
michael@0 | 3408 | nsRefPtr<Layer> container = aManager->GetLayerBuilder()-> |
michael@0 | 3409 | BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
michael@0 | 3410 | newContainerParameters, nullptr); |
michael@0 | 3411 | if (!container) { |
michael@0 | 3412 | return nullptr; |
michael@0 | 3413 | } |
michael@0 | 3414 | |
michael@0 | 3415 | container->DeprecatedSetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode)); |
michael@0 | 3416 | |
michael@0 | 3417 | return container.forget(); |
michael@0 | 3418 | } |
michael@0 | 3419 | |
michael@0 | 3420 | bool nsDisplayMixBlendMode::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 3421 | nsRegion* aVisibleRegion, |
michael@0 | 3422 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 3423 | // Our children are need their backdrop so we should not allow them to subtract |
michael@0 | 3424 | // area from aVisibleRegion. We do need to find out what is visible under |
michael@0 | 3425 | // our children in the temporary compositing buffer, because if our children |
michael@0 | 3426 | // paint our entire bounds opaquely then we don't need an alpha channel in |
michael@0 | 3427 | // the temporary compositing buffer. |
michael@0 | 3428 | nsRect bounds = GetClippedBounds(aBuilder); |
michael@0 | 3429 | nsRegion visibleUnderChildren; |
michael@0 | 3430 | visibleUnderChildren.And(*aVisibleRegion, bounds); |
michael@0 | 3431 | nsRect allowExpansion = bounds.Intersect(aAllowVisibleRegionExpansion); |
michael@0 | 3432 | return |
michael@0 | 3433 | nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren, |
michael@0 | 3434 | allowExpansion); |
michael@0 | 3435 | } |
michael@0 | 3436 | |
michael@0 | 3437 | bool nsDisplayMixBlendMode::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { |
michael@0 | 3438 | if (aItem->GetType() != TYPE_MIX_BLEND_MODE) |
michael@0 | 3439 | return false; |
michael@0 | 3440 | // items for the same content element should be merged into a single |
michael@0 | 3441 | // compositing group |
michael@0 | 3442 | // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity |
michael@0 | 3443 | if (aItem->Frame()->GetContent() != mFrame->GetContent()) |
michael@0 | 3444 | return false; |
michael@0 | 3445 | if (aItem->GetClip() != GetClip()) |
michael@0 | 3446 | return false; |
michael@0 | 3447 | MergeFromTrackingMergedFrames(static_cast<nsDisplayMixBlendMode*>(aItem)); |
michael@0 | 3448 | return true; |
michael@0 | 3449 | } |
michael@0 | 3450 | |
michael@0 | 3451 | nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3452 | nsIFrame* aFrame, nsDisplayList* aList, |
michael@0 | 3453 | uint32_t aFlags) |
michael@0 | 3454 | : nsDisplayWrapList(aBuilder, aFrame, aList) { |
michael@0 | 3455 | MOZ_COUNT_CTOR(nsDisplayBlendContainer); |
michael@0 | 3456 | } |
michael@0 | 3457 | |
michael@0 | 3458 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3459 | nsDisplayBlendContainer::~nsDisplayBlendContainer() { |
michael@0 | 3460 | MOZ_COUNT_DTOR(nsDisplayBlendContainer); |
michael@0 | 3461 | } |
michael@0 | 3462 | #endif |
michael@0 | 3463 | |
michael@0 | 3464 | // nsDisplayBlendContainer uses layers for rendering |
michael@0 | 3465 | already_AddRefed<Layer> |
michael@0 | 3466 | nsDisplayBlendContainer::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3467 | LayerManager* aManager, |
michael@0 | 3468 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3469 | // turn off anti-aliasing in the parent stacking context because it changes |
michael@0 | 3470 | // how the group is initialized. |
michael@0 | 3471 | ContainerLayerParameters newContainerParameters = aContainerParameters; |
michael@0 | 3472 | newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; |
michael@0 | 3473 | |
michael@0 | 3474 | nsRefPtr<Layer> container = aManager->GetLayerBuilder()-> |
michael@0 | 3475 | BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
michael@0 | 3476 | newContainerParameters, nullptr); |
michael@0 | 3477 | if (!container) { |
michael@0 | 3478 | return nullptr; |
michael@0 | 3479 | } |
michael@0 | 3480 | |
michael@0 | 3481 | container->SetForceIsolatedGroup(true); |
michael@0 | 3482 | return container.forget(); |
michael@0 | 3483 | } |
michael@0 | 3484 | |
michael@0 | 3485 | bool nsDisplayBlendContainer::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { |
michael@0 | 3486 | if (aItem->GetType() != TYPE_BLEND_CONTAINER) |
michael@0 | 3487 | return false; |
michael@0 | 3488 | // items for the same content element should be merged into a single |
michael@0 | 3489 | // compositing group |
michael@0 | 3490 | // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity |
michael@0 | 3491 | if (aItem->Frame()->GetContent() != mFrame->GetContent()) |
michael@0 | 3492 | return false; |
michael@0 | 3493 | if (aItem->GetClip() != GetClip()) |
michael@0 | 3494 | return false; |
michael@0 | 3495 | MergeFromTrackingMergedFrames(static_cast<nsDisplayBlendContainer*>(aItem)); |
michael@0 | 3496 | return true; |
michael@0 | 3497 | } |
michael@0 | 3498 | |
michael@0 | 3499 | nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3500 | nsIFrame* aFrame, nsDisplayList* aList, |
michael@0 | 3501 | uint32_t aFlags, ViewID aScrollTarget) |
michael@0 | 3502 | : nsDisplayWrapList(aBuilder, aFrame, aList) |
michael@0 | 3503 | , mFlags(aFlags) |
michael@0 | 3504 | , mScrollTarget(aScrollTarget) { |
michael@0 | 3505 | MOZ_COUNT_CTOR(nsDisplayOwnLayer); |
michael@0 | 3506 | } |
michael@0 | 3507 | |
michael@0 | 3508 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3509 | nsDisplayOwnLayer::~nsDisplayOwnLayer() { |
michael@0 | 3510 | MOZ_COUNT_DTOR(nsDisplayOwnLayer); |
michael@0 | 3511 | } |
michael@0 | 3512 | #endif |
michael@0 | 3513 | |
michael@0 | 3514 | // nsDisplayOpacity uses layers for rendering |
michael@0 | 3515 | already_AddRefed<Layer> |
michael@0 | 3516 | nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3517 | LayerManager* aManager, |
michael@0 | 3518 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3519 | nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()-> |
michael@0 | 3520 | BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
michael@0 | 3521 | aContainerParameters, nullptr); |
michael@0 | 3522 | if (mFlags & VERTICAL_SCROLLBAR) { |
michael@0 | 3523 | layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::VERTICAL); |
michael@0 | 3524 | } |
michael@0 | 3525 | if (mFlags & HORIZONTAL_SCROLLBAR) { |
michael@0 | 3526 | layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::HORIZONTAL); |
michael@0 | 3527 | } |
michael@0 | 3528 | |
michael@0 | 3529 | if (mFlags & GENERATE_SUBDOC_INVALIDATIONS) { |
michael@0 | 3530 | mFrame->PresContext()->SetNotifySubDocInvalidationData(layer); |
michael@0 | 3531 | } |
michael@0 | 3532 | return layer.forget(); |
michael@0 | 3533 | } |
michael@0 | 3534 | |
michael@0 | 3535 | nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder, |
michael@0 | 3536 | nsIFrame* aFrame, nsDisplayList* aList, |
michael@0 | 3537 | uint32_t aFlags) |
michael@0 | 3538 | : nsDisplayOwnLayer(aBuilder, aFrame, aList, aFlags) |
michael@0 | 3539 | , mScrollParentId(aBuilder->GetCurrentScrollParentId()) |
michael@0 | 3540 | { |
michael@0 | 3541 | MOZ_COUNT_CTOR(nsDisplaySubDocument); |
michael@0 | 3542 | } |
michael@0 | 3543 | |
michael@0 | 3544 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3545 | nsDisplaySubDocument::~nsDisplaySubDocument() { |
michael@0 | 3546 | MOZ_COUNT_DTOR(nsDisplaySubDocument); |
michael@0 | 3547 | } |
michael@0 | 3548 | #endif |
michael@0 | 3549 | |
michael@0 | 3550 | already_AddRefed<Layer> |
michael@0 | 3551 | nsDisplaySubDocument::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3552 | LayerManager* aManager, |
michael@0 | 3553 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3554 | nsRefPtr<Layer> layer = nsDisplayOwnLayer::BuildLayer( |
michael@0 | 3555 | aBuilder, aManager, aContainerParameters); |
michael@0 | 3556 | |
michael@0 | 3557 | if (!(mFlags & GENERATE_SCROLLABLE_LAYER)) { |
michael@0 | 3558 | return layer.forget(); |
michael@0 | 3559 | } |
michael@0 | 3560 | |
michael@0 | 3561 | NS_ASSERTION(layer->AsContainerLayer(), "nsDisplayOwnLayer should have made a ContainerLayer"); |
michael@0 | 3562 | if (ContainerLayer* container = layer->AsContainerLayer()) { |
michael@0 | 3563 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 3564 | nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame(); |
michael@0 | 3565 | bool isRootContentDocument = presContext->IsRootContentDocument(); |
michael@0 | 3566 | |
michael@0 | 3567 | bool usingDisplayport = false; |
michael@0 | 3568 | bool usingCriticalDisplayport = false; |
michael@0 | 3569 | nsRect displayport, criticalDisplayport; |
michael@0 | 3570 | ViewID scrollId = FrameMetrics::NULL_SCROLL_ID; |
michael@0 | 3571 | if (rootScrollFrame) { |
michael@0 | 3572 | nsIContent* content = rootScrollFrame->GetContent(); |
michael@0 | 3573 | if (content) { |
michael@0 | 3574 | usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport); |
michael@0 | 3575 | usingCriticalDisplayport = |
michael@0 | 3576 | nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport); |
michael@0 | 3577 | |
michael@0 | 3578 | if (isRootContentDocument) { |
michael@0 | 3579 | scrollId = nsLayoutUtils::FindOrCreateIDFor(content); |
michael@0 | 3580 | } else { |
michael@0 | 3581 | nsLayoutUtils::FindIDFor(content, &scrollId); |
michael@0 | 3582 | } |
michael@0 | 3583 | } |
michael@0 | 3584 | } |
michael@0 | 3585 | |
michael@0 | 3586 | nsRect viewport = mFrame->GetRect() - |
michael@0 | 3587 | mFrame->GetPosition() + |
michael@0 | 3588 | mFrame->GetOffsetToCrossDoc(ReferenceFrame()); |
michael@0 | 3589 | |
michael@0 | 3590 | container->SetScrollHandoffParentId(mScrollParentId); |
michael@0 | 3591 | RecordFrameMetrics(mFrame, rootScrollFrame, ReferenceFrame(), |
michael@0 | 3592 | container, mList.GetVisibleRect(), viewport, |
michael@0 | 3593 | (usingDisplayport ? &displayport : nullptr), |
michael@0 | 3594 | (usingCriticalDisplayport ? &criticalDisplayport : nullptr), |
michael@0 | 3595 | scrollId, isRootContentDocument, aContainerParameters); |
michael@0 | 3596 | } |
michael@0 | 3597 | |
michael@0 | 3598 | return layer.forget(); |
michael@0 | 3599 | } |
michael@0 | 3600 | |
michael@0 | 3601 | nsRect |
michael@0 | 3602 | nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
michael@0 | 3603 | { |
michael@0 | 3604 | bool usingDisplayPort = |
michael@0 | 3605 | nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); |
michael@0 | 3606 | |
michael@0 | 3607 | if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { |
michael@0 | 3608 | *aSnap = false; |
michael@0 | 3609 | return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame); |
michael@0 | 3610 | } |
michael@0 | 3611 | |
michael@0 | 3612 | return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap); |
michael@0 | 3613 | } |
michael@0 | 3614 | |
michael@0 | 3615 | bool |
michael@0 | 3616 | nsDisplaySubDocument::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 3617 | nsRegion* aVisibleRegion, |
michael@0 | 3618 | const nsRect& aAllowVisibleRegionExpansion) |
michael@0 | 3619 | { |
michael@0 | 3620 | nsRect displayport; |
michael@0 | 3621 | bool usingDisplayPort = |
michael@0 | 3622 | nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext(), &displayport); |
michael@0 | 3623 | |
michael@0 | 3624 | if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) { |
michael@0 | 3625 | return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion, |
michael@0 | 3626 | aAllowVisibleRegionExpansion); |
michael@0 | 3627 | } |
michael@0 | 3628 | |
michael@0 | 3629 | nsRegion childVisibleRegion; |
michael@0 | 3630 | // The visible region for the children may be much bigger than the hole we |
michael@0 | 3631 | // are viewing the children from, so that the compositor process has enough |
michael@0 | 3632 | // content to asynchronously pan while content is being refreshed. |
michael@0 | 3633 | childVisibleRegion = displayport + mFrame->GetOffsetToCrossDoc(ReferenceFrame()); |
michael@0 | 3634 | |
michael@0 | 3635 | nsRect boundedRect = |
michael@0 | 3636 | childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder)); |
michael@0 | 3637 | nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion); |
michael@0 | 3638 | bool visible = mList.ComputeVisibilityForSublist( |
michael@0 | 3639 | aBuilder, &childVisibleRegion, boundedRect, allowExpansion, |
michael@0 | 3640 | usingDisplayPort ? mFrame : nullptr); |
michael@0 | 3641 | // We don't allow this computation to influence aVisibleRegion, on the |
michael@0 | 3642 | // assumption that the layer can be asynchronously scrolled so we'll |
michael@0 | 3643 | // definitely need all the content under it. |
michael@0 | 3644 | |
michael@0 | 3645 | return visible; |
michael@0 | 3646 | } |
michael@0 | 3647 | |
michael@0 | 3648 | bool |
michael@0 | 3649 | nsDisplaySubDocument::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) |
michael@0 | 3650 | { |
michael@0 | 3651 | bool usingDisplayPort = |
michael@0 | 3652 | nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); |
michael@0 | 3653 | |
michael@0 | 3654 | if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { |
michael@0 | 3655 | return true; |
michael@0 | 3656 | } |
michael@0 | 3657 | |
michael@0 | 3658 | return nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(aBuilder); |
michael@0 | 3659 | } |
michael@0 | 3660 | |
michael@0 | 3661 | nsRegion |
michael@0 | 3662 | nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) |
michael@0 | 3663 | { |
michael@0 | 3664 | bool usingDisplayPort = |
michael@0 | 3665 | nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); |
michael@0 | 3666 | |
michael@0 | 3667 | if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { |
michael@0 | 3668 | *aSnap = false; |
michael@0 | 3669 | return nsRegion(); |
michael@0 | 3670 | } |
michael@0 | 3671 | |
michael@0 | 3672 | return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap); |
michael@0 | 3673 | } |
michael@0 | 3674 | |
michael@0 | 3675 | nsDisplayResolution::nsDisplayResolution(nsDisplayListBuilder* aBuilder, |
michael@0 | 3676 | nsIFrame* aFrame, nsDisplayList* aList, |
michael@0 | 3677 | uint32_t aFlags) |
michael@0 | 3678 | : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags) { |
michael@0 | 3679 | MOZ_COUNT_CTOR(nsDisplayResolution); |
michael@0 | 3680 | } |
michael@0 | 3681 | |
michael@0 | 3682 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3683 | nsDisplayResolution::~nsDisplayResolution() { |
michael@0 | 3684 | MOZ_COUNT_DTOR(nsDisplayResolution); |
michael@0 | 3685 | } |
michael@0 | 3686 | #endif |
michael@0 | 3687 | |
michael@0 | 3688 | already_AddRefed<Layer> |
michael@0 | 3689 | nsDisplayResolution::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3690 | LayerManager* aManager, |
michael@0 | 3691 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3692 | nsIPresShell* presShell = mFrame->PresContext()->PresShell(); |
michael@0 | 3693 | ContainerLayerParameters containerParameters( |
michael@0 | 3694 | presShell->GetXResolution(), presShell->GetYResolution(), nsIntPoint(), |
michael@0 | 3695 | aContainerParameters); |
michael@0 | 3696 | |
michael@0 | 3697 | nsRefPtr<Layer> layer = nsDisplaySubDocument::BuildLayer( |
michael@0 | 3698 | aBuilder, aManager, containerParameters); |
michael@0 | 3699 | layer->SetPostScale(1.0f / presShell->GetXResolution(), |
michael@0 | 3700 | 1.0f / presShell->GetYResolution()); |
michael@0 | 3701 | return layer.forget(); |
michael@0 | 3702 | } |
michael@0 | 3703 | |
michael@0 | 3704 | nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, |
michael@0 | 3705 | nsIFrame* aFrame, |
michael@0 | 3706 | nsDisplayList* aList) |
michael@0 | 3707 | : nsDisplayOwnLayer(aBuilder, aFrame, aList) |
michael@0 | 3708 | { |
michael@0 | 3709 | MOZ_COUNT_CTOR(nsDisplayStickyPosition); |
michael@0 | 3710 | } |
michael@0 | 3711 | |
michael@0 | 3712 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3713 | nsDisplayStickyPosition::~nsDisplayStickyPosition() { |
michael@0 | 3714 | MOZ_COUNT_DTOR(nsDisplayStickyPosition); |
michael@0 | 3715 | } |
michael@0 | 3716 | #endif |
michael@0 | 3717 | |
michael@0 | 3718 | already_AddRefed<Layer> |
michael@0 | 3719 | nsDisplayStickyPosition::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3720 | LayerManager* aManager, |
michael@0 | 3721 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3722 | nsRefPtr<Layer> layer = |
michael@0 | 3723 | nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters); |
michael@0 | 3724 | |
michael@0 | 3725 | StickyScrollContainer* stickyScrollContainer = StickyScrollContainer:: |
michael@0 | 3726 | GetStickyScrollContainerForFrame(mFrame); |
michael@0 | 3727 | if (!stickyScrollContainer) { |
michael@0 | 3728 | return layer.forget(); |
michael@0 | 3729 | } |
michael@0 | 3730 | |
michael@0 | 3731 | nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame()); |
michael@0 | 3732 | nsPresContext* presContext = scrollFrame->PresContext(); |
michael@0 | 3733 | |
michael@0 | 3734 | // Sticky position frames whose scroll frame is the root scroll frame are |
michael@0 | 3735 | // reflowed into the scroll-port size if one has been set. |
michael@0 | 3736 | nsSize scrollFrameSize = scrollFrame->GetSize(); |
michael@0 | 3737 | if (scrollFrame == presContext->PresShell()->GetRootScrollFrame() && |
michael@0 | 3738 | presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) { |
michael@0 | 3739 | scrollFrameSize = presContext->PresShell()-> |
michael@0 | 3740 | GetScrollPositionClampingScrollPortSize(); |
michael@0 | 3741 | } |
michael@0 | 3742 | |
michael@0 | 3743 | nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame, |
michael@0 | 3744 | nsRect(scrollFrame->GetOffsetToCrossDoc(ReferenceFrame()), scrollFrameSize), |
michael@0 | 3745 | mFrame, presContext, aContainerParameters); |
michael@0 | 3746 | |
michael@0 | 3747 | ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor( |
michael@0 | 3748 | stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent()); |
michael@0 | 3749 | |
michael@0 | 3750 | float factor = presContext->AppUnitsPerDevPixel(); |
michael@0 | 3751 | nsRect outer; |
michael@0 | 3752 | nsRect inner; |
michael@0 | 3753 | stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner); |
michael@0 | 3754 | LayerRect stickyOuter(NSAppUnitsToFloatPixels(outer.x, factor) * |
michael@0 | 3755 | aContainerParameters.mXScale, |
michael@0 | 3756 | NSAppUnitsToFloatPixels(outer.y, factor) * |
michael@0 | 3757 | aContainerParameters.mYScale, |
michael@0 | 3758 | NSAppUnitsToFloatPixels(outer.width, factor) * |
michael@0 | 3759 | aContainerParameters.mXScale, |
michael@0 | 3760 | NSAppUnitsToFloatPixels(outer.height, factor) * |
michael@0 | 3761 | aContainerParameters.mYScale); |
michael@0 | 3762 | LayerRect stickyInner(NSAppUnitsToFloatPixels(inner.x, factor) * |
michael@0 | 3763 | aContainerParameters.mXScale, |
michael@0 | 3764 | NSAppUnitsToFloatPixels(inner.y, factor) * |
michael@0 | 3765 | aContainerParameters.mYScale, |
michael@0 | 3766 | NSAppUnitsToFloatPixels(inner.width, factor) * |
michael@0 | 3767 | aContainerParameters.mXScale, |
michael@0 | 3768 | NSAppUnitsToFloatPixels(inner.height, factor) * |
michael@0 | 3769 | aContainerParameters.mYScale); |
michael@0 | 3770 | layer->SetStickyPositionData(scrollId, stickyOuter, stickyInner); |
michael@0 | 3771 | |
michael@0 | 3772 | return layer.forget(); |
michael@0 | 3773 | } |
michael@0 | 3774 | |
michael@0 | 3775 | bool nsDisplayStickyPosition::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { |
michael@0 | 3776 | if (aItem->GetType() != TYPE_STICKY_POSITION) |
michael@0 | 3777 | return false; |
michael@0 | 3778 | // Items with the same fixed position frame can be merged. |
michael@0 | 3779 | nsDisplayStickyPosition* other = static_cast<nsDisplayStickyPosition*>(aItem); |
michael@0 | 3780 | if (other->mFrame != mFrame) |
michael@0 | 3781 | return false; |
michael@0 | 3782 | if (aItem->GetClip() != GetClip()) |
michael@0 | 3783 | return false; |
michael@0 | 3784 | MergeFromTrackingMergedFrames(other); |
michael@0 | 3785 | return true; |
michael@0 | 3786 | } |
michael@0 | 3787 | |
michael@0 | 3788 | nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3789 | nsDisplayList* aList, |
michael@0 | 3790 | nsIFrame* aForFrame, |
michael@0 | 3791 | nsIFrame* aScrolledFrame, |
michael@0 | 3792 | nsIFrame* aScrollFrame) |
michael@0 | 3793 | : nsDisplayWrapList(aBuilder, aForFrame, aList) |
michael@0 | 3794 | , mScrollFrame(aScrollFrame) |
michael@0 | 3795 | , mScrolledFrame(aScrolledFrame) |
michael@0 | 3796 | , mScrollParentId(aBuilder->GetCurrentScrollParentId()) |
michael@0 | 3797 | { |
michael@0 | 3798 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3799 | MOZ_COUNT_CTOR(nsDisplayScrollLayer); |
michael@0 | 3800 | #endif |
michael@0 | 3801 | |
michael@0 | 3802 | NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), |
michael@0 | 3803 | "Need a child frame with content"); |
michael@0 | 3804 | } |
michael@0 | 3805 | |
michael@0 | 3806 | nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3807 | nsDisplayItem* aItem, |
michael@0 | 3808 | nsIFrame* aForFrame, |
michael@0 | 3809 | nsIFrame* aScrolledFrame, |
michael@0 | 3810 | nsIFrame* aScrollFrame) |
michael@0 | 3811 | : nsDisplayWrapList(aBuilder, aForFrame, aItem) |
michael@0 | 3812 | , mScrollFrame(aScrollFrame) |
michael@0 | 3813 | , mScrolledFrame(aScrolledFrame) |
michael@0 | 3814 | , mScrollParentId(aBuilder->GetCurrentScrollParentId()) |
michael@0 | 3815 | { |
michael@0 | 3816 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3817 | MOZ_COUNT_CTOR(nsDisplayScrollLayer); |
michael@0 | 3818 | #endif |
michael@0 | 3819 | |
michael@0 | 3820 | NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), |
michael@0 | 3821 | "Need a child frame with content"); |
michael@0 | 3822 | } |
michael@0 | 3823 | |
michael@0 | 3824 | nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3825 | nsIFrame* aForFrame, |
michael@0 | 3826 | nsIFrame* aScrolledFrame, |
michael@0 | 3827 | nsIFrame* aScrollFrame) |
michael@0 | 3828 | : nsDisplayWrapList(aBuilder, aForFrame) |
michael@0 | 3829 | , mScrollFrame(aScrollFrame) |
michael@0 | 3830 | , mScrolledFrame(aScrolledFrame) |
michael@0 | 3831 | , mScrollParentId(aBuilder->GetCurrentScrollParentId()) |
michael@0 | 3832 | { |
michael@0 | 3833 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3834 | MOZ_COUNT_CTOR(nsDisplayScrollLayer); |
michael@0 | 3835 | #endif |
michael@0 | 3836 | |
michael@0 | 3837 | NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), |
michael@0 | 3838 | "Need a child frame with content"); |
michael@0 | 3839 | } |
michael@0 | 3840 | |
michael@0 | 3841 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 3842 | nsDisplayScrollLayer::~nsDisplayScrollLayer() |
michael@0 | 3843 | { |
michael@0 | 3844 | MOZ_COUNT_DTOR(nsDisplayScrollLayer); |
michael@0 | 3845 | } |
michael@0 | 3846 | #endif |
michael@0 | 3847 | |
michael@0 | 3848 | nsRect |
michael@0 | 3849 | nsDisplayScrollLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
michael@0 | 3850 | { |
michael@0 | 3851 | nsIScrollableFrame* sf = do_QueryFrame(mScrollFrame); |
michael@0 | 3852 | if (sf) { |
michael@0 | 3853 | *aSnap = false; |
michael@0 | 3854 | return sf->GetScrollPortRect() + aBuilder->ToReferenceFrame(mScrollFrame); |
michael@0 | 3855 | } |
michael@0 | 3856 | return nsDisplayWrapList::GetBounds(aBuilder, aSnap); |
michael@0 | 3857 | } |
michael@0 | 3858 | |
michael@0 | 3859 | already_AddRefed<Layer> |
michael@0 | 3860 | nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 3861 | LayerManager* aManager, |
michael@0 | 3862 | const ContainerLayerParameters& aContainerParameters) { |
michael@0 | 3863 | nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()-> |
michael@0 | 3864 | BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
michael@0 | 3865 | aContainerParameters, nullptr); |
michael@0 | 3866 | |
michael@0 | 3867 | // Get the already set unique ID for scrolling this content remotely. |
michael@0 | 3868 | // Or, if not set, generate a new ID. |
michael@0 | 3869 | nsIContent* content = mScrolledFrame->GetContent(); |
michael@0 | 3870 | ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(content); |
michael@0 | 3871 | |
michael@0 | 3872 | nsRect viewport = mScrollFrame->GetRect() - |
michael@0 | 3873 | mScrollFrame->GetPosition() + |
michael@0 | 3874 | mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame()); |
michael@0 | 3875 | |
michael@0 | 3876 | bool usingDisplayport = false; |
michael@0 | 3877 | bool usingCriticalDisplayport = false; |
michael@0 | 3878 | nsRect displayport, criticalDisplayport; |
michael@0 | 3879 | if (content) { |
michael@0 | 3880 | usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport); |
michael@0 | 3881 | usingCriticalDisplayport = |
michael@0 | 3882 | nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport); |
michael@0 | 3883 | } |
michael@0 | 3884 | layer->SetScrollHandoffParentId(mScrollParentId); |
michael@0 | 3885 | RecordFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), layer, |
michael@0 | 3886 | mList.GetVisibleRect(), viewport, |
michael@0 | 3887 | (usingDisplayport ? &displayport : nullptr), |
michael@0 | 3888 | (usingCriticalDisplayport ? &criticalDisplayport : nullptr), |
michael@0 | 3889 | scrollId, false, aContainerParameters); |
michael@0 | 3890 | |
michael@0 | 3891 | return layer.forget(); |
michael@0 | 3892 | } |
michael@0 | 3893 | |
michael@0 | 3894 | bool |
michael@0 | 3895 | nsDisplayScrollLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) |
michael@0 | 3896 | { |
michael@0 | 3897 | if (nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), nullptr)) { |
michael@0 | 3898 | return true; |
michael@0 | 3899 | } |
michael@0 | 3900 | |
michael@0 | 3901 | return nsDisplayWrapList::ShouldBuildLayerEvenIfInvisible(aBuilder); |
michael@0 | 3902 | } |
michael@0 | 3903 | |
michael@0 | 3904 | bool |
michael@0 | 3905 | nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 3906 | nsRegion* aVisibleRegion, |
michael@0 | 3907 | const nsRect& aAllowVisibleRegionExpansion) |
michael@0 | 3908 | { |
michael@0 | 3909 | nsRect displayport; |
michael@0 | 3910 | bool usingDisplayPort = |
michael@0 | 3911 | nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), &displayport); |
michael@0 | 3912 | nsRegion childVisibleRegion; |
michael@0 | 3913 | if (usingDisplayPort) { |
michael@0 | 3914 | // The visible region for the children may be much bigger than the hole we |
michael@0 | 3915 | // are viewing the children from, so that the compositor process has enough |
michael@0 | 3916 | // content to asynchronously pan while content is being refreshed. |
michael@0 | 3917 | childVisibleRegion = displayport + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame()); |
michael@0 | 3918 | } else { |
michael@0 | 3919 | bool snap; |
michael@0 | 3920 | childVisibleRegion = GetBounds(aBuilder, &snap); |
michael@0 | 3921 | } |
michael@0 | 3922 | |
michael@0 | 3923 | nsRect boundedRect = |
michael@0 | 3924 | childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder)); |
michael@0 | 3925 | nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion); |
michael@0 | 3926 | bool visible = mList.ComputeVisibilityForSublist( |
michael@0 | 3927 | aBuilder, &childVisibleRegion, boundedRect, allowExpansion, |
michael@0 | 3928 | usingDisplayPort ? mScrollFrame : nullptr); |
michael@0 | 3929 | // We don't allow this computation to influence aVisibleRegion, on the |
michael@0 | 3930 | // assumption that the layer can be asynchronously scrolled so we'll |
michael@0 | 3931 | // definitely need all the content under it. |
michael@0 | 3932 | |
michael@0 | 3933 | return visible; |
michael@0 | 3934 | } |
michael@0 | 3935 | |
michael@0 | 3936 | LayerState |
michael@0 | 3937 | nsDisplayScrollLayer::GetLayerState(nsDisplayListBuilder* aBuilder, |
michael@0 | 3938 | LayerManager* aManager, |
michael@0 | 3939 | const ContainerLayerParameters& aParameters) |
michael@0 | 3940 | { |
michael@0 | 3941 | // Force this as a layer so we can scroll asynchronously. |
michael@0 | 3942 | // This causes incorrect rendering for rounded clips! |
michael@0 | 3943 | return LAYER_ACTIVE_FORCE; |
michael@0 | 3944 | } |
michael@0 | 3945 | |
michael@0 | 3946 | // Check if we are going to clip an abs pos item that we don't contain. |
michael@0 | 3947 | // Root scroll frames clip all their descendants, so we don't need to worry |
michael@0 | 3948 | // about them. |
michael@0 | 3949 | bool |
michael@0 | 3950 | WouldCauseIncorrectClippingOnAbsPosItem(nsDisplayListBuilder* aBuilder, |
michael@0 | 3951 | nsDisplayScrollLayer* aItem) |
michael@0 | 3952 | { |
michael@0 | 3953 | nsIFrame* scrollFrame = aItem->GetScrollFrame(); |
michael@0 | 3954 | nsIPresShell* presShell = scrollFrame->PresContext()->PresShell(); |
michael@0 | 3955 | if (scrollFrame == presShell->GetRootScrollFrame()) { |
michael@0 | 3956 | return false; |
michael@0 | 3957 | } |
michael@0 | 3958 | nsIFrame* scrolledFrame = aItem->GetScrolledFrame(); |
michael@0 | 3959 | nsIFrame* frame = aItem->Frame(); |
michael@0 | 3960 | if (frame == scrolledFrame || !frame->IsAbsolutelyPositioned() || |
michael@0 | 3961 | nsLayoutUtils::IsAncestorFrameCrossDoc(scrollFrame, frame, presShell->GetRootFrame())) { |
michael@0 | 3962 | return false; |
michael@0 | 3963 | } |
michael@0 | 3964 | if (!aItem->GetClip().IsRectAffectedByClip(aItem->GetChildren()->GetBounds(aBuilder))) { |
michael@0 | 3965 | return false; |
michael@0 | 3966 | } |
michael@0 | 3967 | return true; |
michael@0 | 3968 | } |
michael@0 | 3969 | |
michael@0 | 3970 | bool |
michael@0 | 3971 | nsDisplayScrollLayer::TryMerge(nsDisplayListBuilder* aBuilder, |
michael@0 | 3972 | nsDisplayItem* aItem) |
michael@0 | 3973 | { |
michael@0 | 3974 | if (aItem->GetType() != TYPE_SCROLL_LAYER) { |
michael@0 | 3975 | return false; |
michael@0 | 3976 | } |
michael@0 | 3977 | nsDisplayScrollLayer* other = static_cast<nsDisplayScrollLayer*>(aItem); |
michael@0 | 3978 | if (other->mScrolledFrame != this->mScrolledFrame) { |
michael@0 | 3979 | return false; |
michael@0 | 3980 | } |
michael@0 | 3981 | if (aItem->GetClip() != GetClip()) { |
michael@0 | 3982 | return false; |
michael@0 | 3983 | } |
michael@0 | 3984 | |
michael@0 | 3985 | if (WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this) || |
michael@0 | 3986 | WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, other)) { |
michael@0 | 3987 | return false; |
michael@0 | 3988 | } |
michael@0 | 3989 | |
michael@0 | 3990 | NS_ASSERTION(other->mReferenceFrame == mReferenceFrame, |
michael@0 | 3991 | "Must have the same reference frame!"); |
michael@0 | 3992 | |
michael@0 | 3993 | FrameProperties props = mScrolledFrame->Properties(); |
michael@0 | 3994 | props.Set(nsIFrame::ScrollLayerCount(), |
michael@0 | 3995 | reinterpret_cast<void*>(GetScrollLayerCount() - 1)); |
michael@0 | 3996 | |
michael@0 | 3997 | // Swap frames with the other item before doing MergeFrom. |
michael@0 | 3998 | // XXX - This ensures that the frame associated with a scroll layer after |
michael@0 | 3999 | // merging is the first, rather than the last. This tends to change less, |
michael@0 | 4000 | // ensuring we're more likely to retain the associated gfx layer. |
michael@0 | 4001 | // See Bug 729534 and Bug 731641. |
michael@0 | 4002 | nsIFrame* tmp = mFrame; |
michael@0 | 4003 | mFrame = other->mFrame; |
michael@0 | 4004 | other->mFrame = tmp; |
michael@0 | 4005 | MergeFromTrackingMergedFrames(other); |
michael@0 | 4006 | return true; |
michael@0 | 4007 | } |
michael@0 | 4008 | |
michael@0 | 4009 | void |
michael@0 | 4010 | PropagateClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip, |
michael@0 | 4011 | nsDisplayList* aList) |
michael@0 | 4012 | { |
michael@0 | 4013 | for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) { |
michael@0 | 4014 | DisplayItemClip clip(i->GetClip()); |
michael@0 | 4015 | clip.IntersectWith(aClip); |
michael@0 | 4016 | i->SetClip(aBuilder, clip); |
michael@0 | 4017 | nsDisplayList* list = i->GetSameCoordinateSystemChildren(); |
michael@0 | 4018 | if (list) { |
michael@0 | 4019 | PropagateClip(aBuilder, aClip, list); |
michael@0 | 4020 | } |
michael@0 | 4021 | } |
michael@0 | 4022 | } |
michael@0 | 4023 | |
michael@0 | 4024 | bool |
michael@0 | 4025 | nsDisplayScrollLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) |
michael@0 | 4026 | { |
michael@0 | 4027 | bool badAbsPosClip = WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this); |
michael@0 | 4028 | if (GetScrollLayerCount() > 1 || badAbsPosClip) { |
michael@0 | 4029 | // Propagate our clip to our children. The clip for the scroll frame is |
michael@0 | 4030 | // on this item, but not our child items so that they can draw non-visible |
michael@0 | 4031 | // parts of the display port. But if we are flattening we failed and can't |
michael@0 | 4032 | // draw the extra content, so it needs to be clipped. |
michael@0 | 4033 | // But don't induce our clip on abs pos frames that we shouldn't be clipping. |
michael@0 | 4034 | if (!badAbsPosClip) { |
michael@0 | 4035 | PropagateClip(aBuilder, GetClip(), &mList); |
michael@0 | 4036 | } |
michael@0 | 4037 | return true; |
michael@0 | 4038 | } |
michael@0 | 4039 | if (mFrame != mScrolledFrame) { |
michael@0 | 4040 | mMergedFrames.AppendElement(mFrame); |
michael@0 | 4041 | mFrame = mScrolledFrame; |
michael@0 | 4042 | } |
michael@0 | 4043 | return false; |
michael@0 | 4044 | } |
michael@0 | 4045 | |
michael@0 | 4046 | intptr_t |
michael@0 | 4047 | nsDisplayScrollLayer::GetScrollLayerCount() |
michael@0 | 4048 | { |
michael@0 | 4049 | FrameProperties props = mScrolledFrame->Properties(); |
michael@0 | 4050 | #ifdef DEBUG |
michael@0 | 4051 | bool hasCount = false; |
michael@0 | 4052 | intptr_t result = reinterpret_cast<intptr_t>( |
michael@0 | 4053 | props.Get(nsIFrame::ScrollLayerCount(), &hasCount)); |
michael@0 | 4054 | // If this aborts, then the property was either not added before scroll |
michael@0 | 4055 | // layers were created or the property was deleted to early. If the latter, |
michael@0 | 4056 | // make sure that nsDisplayScrollInfoLayer is on the bottom of the list so |
michael@0 | 4057 | // that it is processed last. |
michael@0 | 4058 | NS_ABORT_IF_FALSE(hasCount, "nsDisplayScrollLayer should always be defined"); |
michael@0 | 4059 | return result; |
michael@0 | 4060 | #else |
michael@0 | 4061 | return reinterpret_cast<intptr_t>(props.Get(nsIFrame::ScrollLayerCount())); |
michael@0 | 4062 | #endif |
michael@0 | 4063 | } |
michael@0 | 4064 | |
michael@0 | 4065 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 4066 | void |
michael@0 | 4067 | nsDisplayScrollLayer::WriteDebugInfo(nsACString& aTo) |
michael@0 | 4068 | { |
michael@0 | 4069 | aTo += nsPrintfCString(" (scrollframe %p scrolledframe %p)", |
michael@0 | 4070 | mScrollFrame, mScrolledFrame); |
michael@0 | 4071 | } |
michael@0 | 4072 | #endif |
michael@0 | 4073 | |
michael@0 | 4074 | nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer( |
michael@0 | 4075 | nsDisplayListBuilder* aBuilder, |
michael@0 | 4076 | nsIFrame* aScrolledFrame, |
michael@0 | 4077 | nsIFrame* aScrollFrame) |
michael@0 | 4078 | : nsDisplayScrollLayer(aBuilder, aScrollFrame, aScrolledFrame, aScrollFrame) |
michael@0 | 4079 | { |
michael@0 | 4080 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 4081 | MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer); |
michael@0 | 4082 | #endif |
michael@0 | 4083 | } |
michael@0 | 4084 | |
michael@0 | 4085 | nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer() |
michael@0 | 4086 | { |
michael@0 | 4087 | FrameProperties props = mScrolledFrame->Properties(); |
michael@0 | 4088 | props.Remove(nsIFrame::ScrollLayerCount()); |
michael@0 | 4089 | MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer); |
michael@0 | 4090 | } |
michael@0 | 4091 | |
michael@0 | 4092 | nsRect |
michael@0 | 4093 | nsDisplayScrollInfoLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
michael@0 | 4094 | { |
michael@0 | 4095 | return nsDisplayWrapList::GetBounds(aBuilder, aSnap); |
michael@0 | 4096 | } |
michael@0 | 4097 | |
michael@0 | 4098 | LayerState |
michael@0 | 4099 | nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder, |
michael@0 | 4100 | LayerManager* aManager, |
michael@0 | 4101 | const ContainerLayerParameters& aParameters) |
michael@0 | 4102 | { |
michael@0 | 4103 | return LAYER_ACTIVE_EMPTY; |
michael@0 | 4104 | } |
michael@0 | 4105 | |
michael@0 | 4106 | bool |
michael@0 | 4107 | nsDisplayScrollInfoLayer::TryMerge(nsDisplayListBuilder* aBuilder, |
michael@0 | 4108 | nsDisplayItem* aItem) |
michael@0 | 4109 | { |
michael@0 | 4110 | return false; |
michael@0 | 4111 | } |
michael@0 | 4112 | |
michael@0 | 4113 | bool |
michael@0 | 4114 | nsDisplayScrollInfoLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) |
michael@0 | 4115 | { |
michael@0 | 4116 | // Layer metadata for a particular scroll frame needs to be unique. Only |
michael@0 | 4117 | // one nsDisplayScrollLayer (with rendered content) or one |
michael@0 | 4118 | // nsDisplayScrollInfoLayer (with only the metadata) should survive the |
michael@0 | 4119 | // visibility computation. |
michael@0 | 4120 | return GetScrollLayerCount() == 1; |
michael@0 | 4121 | } |
michael@0 | 4122 | |
michael@0 | 4123 | nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, |
michael@0 | 4124 | nsIFrame* aFrame, nsDisplayList* aList, |
michael@0 | 4125 | int32_t aAPD, int32_t aParentAPD, |
michael@0 | 4126 | uint32_t aFlags) |
michael@0 | 4127 | : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags) |
michael@0 | 4128 | , mAPD(aAPD), mParentAPD(aParentAPD) { |
michael@0 | 4129 | MOZ_COUNT_CTOR(nsDisplayZoom); |
michael@0 | 4130 | } |
michael@0 | 4131 | |
michael@0 | 4132 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 4133 | nsDisplayZoom::~nsDisplayZoom() { |
michael@0 | 4134 | MOZ_COUNT_DTOR(nsDisplayZoom); |
michael@0 | 4135 | } |
michael@0 | 4136 | #endif |
michael@0 | 4137 | |
michael@0 | 4138 | nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) |
michael@0 | 4139 | { |
michael@0 | 4140 | nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap); |
michael@0 | 4141 | *aSnap = false; |
michael@0 | 4142 | return bounds.ConvertAppUnitsRoundOut(mAPD, mParentAPD); |
michael@0 | 4143 | } |
michael@0 | 4144 | |
michael@0 | 4145 | void nsDisplayZoom::HitTest(nsDisplayListBuilder *aBuilder, |
michael@0 | 4146 | const nsRect& aRect, |
michael@0 | 4147 | HitTestState *aState, |
michael@0 | 4148 | nsTArray<nsIFrame*> *aOutFrames) |
michael@0 | 4149 | { |
michael@0 | 4150 | nsRect rect; |
michael@0 | 4151 | // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1 |
michael@0 | 4152 | // rect as well instead of possibly rounding the width or height to zero. |
michael@0 | 4153 | if (aRect.width == 1 && aRect.height == 1) { |
michael@0 | 4154 | rect.MoveTo(aRect.TopLeft().ConvertAppUnits(mParentAPD, mAPD)); |
michael@0 | 4155 | rect.width = rect.height = 1; |
michael@0 | 4156 | } else { |
michael@0 | 4157 | rect = aRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD); |
michael@0 | 4158 | } |
michael@0 | 4159 | mList.HitTest(aBuilder, rect, aState, aOutFrames); |
michael@0 | 4160 | } |
michael@0 | 4161 | |
michael@0 | 4162 | void nsDisplayZoom::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 4163 | nsRenderingContext* aCtx) |
michael@0 | 4164 | { |
michael@0 | 4165 | mList.PaintForFrame(aBuilder, aCtx, mFrame, nsDisplayList::PAINT_DEFAULT); |
michael@0 | 4166 | } |
michael@0 | 4167 | |
michael@0 | 4168 | bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder, |
michael@0 | 4169 | nsRegion *aVisibleRegion, |
michael@0 | 4170 | const nsRect& aAllowVisibleRegionExpansion) |
michael@0 | 4171 | { |
michael@0 | 4172 | // Convert the passed in visible region to our appunits. |
michael@0 | 4173 | nsRegion visibleRegion; |
michael@0 | 4174 | // mVisibleRect has been clipped to GetClippedBounds |
michael@0 | 4175 | visibleRegion.And(*aVisibleRegion, mVisibleRect); |
michael@0 | 4176 | visibleRegion = visibleRegion.ConvertAppUnitsRoundOut(mParentAPD, mAPD); |
michael@0 | 4177 | nsRegion originalVisibleRegion = visibleRegion; |
michael@0 | 4178 | |
michael@0 | 4179 | nsRect transformedVisibleRect = |
michael@0 | 4180 | mVisibleRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD); |
michael@0 | 4181 | nsRect allowExpansion = |
michael@0 | 4182 | aAllowVisibleRegionExpansion.ConvertAppUnitsRoundIn(mParentAPD, mAPD); |
michael@0 | 4183 | bool retval; |
michael@0 | 4184 | // If we are to generate a scrollable layer we call |
michael@0 | 4185 | // nsDisplaySubDocument::ComputeVisibility to make the necessary adjustments |
michael@0 | 4186 | // for ComputeVisibility, it does all it's calculations in the child APD. |
michael@0 | 4187 | bool usingDisplayPort = |
michael@0 | 4188 | nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); |
michael@0 | 4189 | if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) { |
michael@0 | 4190 | retval = |
michael@0 | 4191 | mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion, |
michael@0 | 4192 | transformedVisibleRect, |
michael@0 | 4193 | allowExpansion); |
michael@0 | 4194 | } else { |
michael@0 | 4195 | retval = |
michael@0 | 4196 | nsDisplaySubDocument::ComputeVisibility(aBuilder, &visibleRegion, |
michael@0 | 4197 | allowExpansion); |
michael@0 | 4198 | } |
michael@0 | 4199 | |
michael@0 | 4200 | nsRegion removed; |
michael@0 | 4201 | // removed = originalVisibleRegion - visibleRegion |
michael@0 | 4202 | removed.Sub(originalVisibleRegion, visibleRegion); |
michael@0 | 4203 | // Convert removed region to parent appunits. |
michael@0 | 4204 | removed = removed.ConvertAppUnitsRoundIn(mAPD, mParentAPD); |
michael@0 | 4205 | // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications |
michael@0 | 4206 | // SubtractFromVisibleRegion does) |
michael@0 | 4207 | aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed); |
michael@0 | 4208 | |
michael@0 | 4209 | return retval; |
michael@0 | 4210 | } |
michael@0 | 4211 | |
michael@0 | 4212 | /////////////////////////////////////////////////// |
michael@0 | 4213 | // nsDisplayTransform Implementation |
michael@0 | 4214 | // |
michael@0 | 4215 | |
michael@0 | 4216 | // Write #define UNIFIED_CONTINUATIONS here to have the transform property try |
michael@0 | 4217 | // to transform content with continuations as one unified block instead of |
michael@0 | 4218 | // several smaller ones. This is currently disabled because it doesn't work |
michael@0 | 4219 | // correctly, since when the frames are initially being reflowed, their |
michael@0 | 4220 | // continuations all compute their bounding rects independently of each other |
michael@0 | 4221 | // and consequently get the wrong value. Write #define DEBUG_HIT here to have |
michael@0 | 4222 | // the nsDisplayTransform class dump out a bunch of information about hit |
michael@0 | 4223 | // detection. |
michael@0 | 4224 | #undef UNIFIED_CONTINUATIONS |
michael@0 | 4225 | #undef DEBUG_HIT |
michael@0 | 4226 | |
michael@0 | 4227 | /* Returns the bounds of a frame as defined for transforms. If |
michael@0 | 4228 | * UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding |
michael@0 | 4229 | * rectangle, translated to the origin. Otherwise, returns the smallest |
michael@0 | 4230 | * rectangle containing a frame and all of its continuations. For example, if |
michael@0 | 4231 | * there is a <span> element with several continuations split over several |
michael@0 | 4232 | * lines, this function will return the rectangle containing all of those |
michael@0 | 4233 | * continuations. This rectangle is relative to the origin of the frame's local |
michael@0 | 4234 | * coordinate space. |
michael@0 | 4235 | */ |
michael@0 | 4236 | #ifndef UNIFIED_CONTINUATIONS |
michael@0 | 4237 | |
michael@0 | 4238 | nsRect |
michael@0 | 4239 | nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) |
michael@0 | 4240 | { |
michael@0 | 4241 | NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!"); |
michael@0 | 4242 | |
michael@0 | 4243 | if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
michael@0 | 4244 | // TODO: SVG needs to define what percentage translations resolve against. |
michael@0 | 4245 | return nsRect(); |
michael@0 | 4246 | } |
michael@0 | 4247 | |
michael@0 | 4248 | return nsRect(nsPoint(0, 0), aFrame->GetSize()); |
michael@0 | 4249 | } |
michael@0 | 4250 | |
michael@0 | 4251 | #else |
michael@0 | 4252 | |
michael@0 | 4253 | nsRect |
michael@0 | 4254 | nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) |
michael@0 | 4255 | { |
michael@0 | 4256 | NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!"); |
michael@0 | 4257 | |
michael@0 | 4258 | nsRect result; |
michael@0 | 4259 | |
michael@0 | 4260 | if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
michael@0 | 4261 | // TODO: SVG needs to define what percentage translations resolve against. |
michael@0 | 4262 | return result; |
michael@0 | 4263 | } |
michael@0 | 4264 | |
michael@0 | 4265 | /* Iterate through the continuation list, unioning together all the |
michael@0 | 4266 | * bounding rects. |
michael@0 | 4267 | */ |
michael@0 | 4268 | for (const nsIFrame *currFrame = aFrame->FirstContinuation(); |
michael@0 | 4269 | currFrame != nullptr; |
michael@0 | 4270 | currFrame = currFrame->GetNextContinuation()) |
michael@0 | 4271 | { |
michael@0 | 4272 | /* Get the frame rect in local coordinates, then translate back to the |
michael@0 | 4273 | * original coordinates. |
michael@0 | 4274 | */ |
michael@0 | 4275 | result.UnionRect(result, nsRect(currFrame->GetOffsetTo(aFrame), |
michael@0 | 4276 | currFrame->GetSize())); |
michael@0 | 4277 | } |
michael@0 | 4278 | |
michael@0 | 4279 | return result; |
michael@0 | 4280 | } |
michael@0 | 4281 | |
michael@0 | 4282 | #endif |
michael@0 | 4283 | |
michael@0 | 4284 | nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, |
michael@0 | 4285 | nsDisplayList *aList, ComputeTransformFunction aTransformGetter, |
michael@0 | 4286 | uint32_t aIndex) |
michael@0 | 4287 | : nsDisplayItem(aBuilder, aFrame) |
michael@0 | 4288 | , mStoredList(aBuilder, aFrame, aList) |
michael@0 | 4289 | , mTransformGetter(aTransformGetter) |
michael@0 | 4290 | , mIndex(aIndex) |
michael@0 | 4291 | { |
michael@0 | 4292 | MOZ_COUNT_CTOR(nsDisplayTransform); |
michael@0 | 4293 | NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); |
michael@0 | 4294 | NS_ABORT_IF_FALSE(!aFrame->IsTransformed(), "Can't specify a transform getter for a transformed frame!"); |
michael@0 | 4295 | mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip()); |
michael@0 | 4296 | } |
michael@0 | 4297 | |
michael@0 | 4298 | nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, |
michael@0 | 4299 | nsDisplayList *aList, uint32_t aIndex) |
michael@0 | 4300 | : nsDisplayItem(aBuilder, aFrame) |
michael@0 | 4301 | , mStoredList(aBuilder, aFrame, aList) |
michael@0 | 4302 | , mTransformGetter(nullptr) |
michael@0 | 4303 | , mIndex(aIndex) |
michael@0 | 4304 | { |
michael@0 | 4305 | MOZ_COUNT_CTOR(nsDisplayTransform); |
michael@0 | 4306 | NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); |
michael@0 | 4307 | mReferenceFrame = |
michael@0 | 4308 | aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); |
michael@0 | 4309 | mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); |
michael@0 | 4310 | mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip()); |
michael@0 | 4311 | } |
michael@0 | 4312 | |
michael@0 | 4313 | nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, |
michael@0 | 4314 | nsDisplayItem *aItem, uint32_t aIndex) |
michael@0 | 4315 | : nsDisplayItem(aBuilder, aFrame) |
michael@0 | 4316 | , mStoredList(aBuilder, aFrame, aItem) |
michael@0 | 4317 | , mTransformGetter(nullptr) |
michael@0 | 4318 | , mIndex(aIndex) |
michael@0 | 4319 | { |
michael@0 | 4320 | MOZ_COUNT_CTOR(nsDisplayTransform); |
michael@0 | 4321 | NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); |
michael@0 | 4322 | mReferenceFrame = |
michael@0 | 4323 | aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); |
michael@0 | 4324 | mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); |
michael@0 | 4325 | mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip()); |
michael@0 | 4326 | } |
michael@0 | 4327 | |
michael@0 | 4328 | /* Returns the delta specified by the -moz-transform-origin property. |
michael@0 | 4329 | * This is a positive delta, meaning that it indicates the direction to move |
michael@0 | 4330 | * to get from (0, 0) of the frame to the transform origin. This function is |
michael@0 | 4331 | * called off the main thread. |
michael@0 | 4332 | */ |
michael@0 | 4333 | /* static */ gfxPoint3D |
michael@0 | 4334 | nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame, |
michael@0 | 4335 | float aAppUnitsPerPixel, |
michael@0 | 4336 | const nsRect* aBoundsOverride) |
michael@0 | 4337 | { |
michael@0 | 4338 | NS_PRECONDITION(aFrame, "Can't get delta for a null frame!"); |
michael@0 | 4339 | NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(), |
michael@0 | 4340 | "Shouldn't get a delta for an untransformed frame!"); |
michael@0 | 4341 | |
michael@0 | 4342 | if (!aFrame->IsTransformed()) { |
michael@0 | 4343 | return gfxPoint3D(); |
michael@0 | 4344 | } |
michael@0 | 4345 | |
michael@0 | 4346 | /* For both of the coordinates, if the value of -moz-transform is a |
michael@0 | 4347 | * percentage, it's relative to the size of the frame. Otherwise, if it's |
michael@0 | 4348 | * a distance, it's already computed for us! |
michael@0 | 4349 | */ |
michael@0 | 4350 | const nsStyleDisplay* display = aFrame->StyleDisplay(); |
michael@0 | 4351 | nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride : |
michael@0 | 4352 | nsDisplayTransform::GetFrameBoundsForTransform(aFrame)); |
michael@0 | 4353 | |
michael@0 | 4354 | /* Allows us to access named variables by index. */ |
michael@0 | 4355 | float coords[3]; |
michael@0 | 4356 | const nscoord* dimensions[2] = |
michael@0 | 4357 | {&boundingRect.width, &boundingRect.height}; |
michael@0 | 4358 | |
michael@0 | 4359 | for (uint8_t index = 0; index < 2; ++index) { |
michael@0 | 4360 | /* If the -moz-transform-origin specifies a percentage, take the percentage |
michael@0 | 4361 | * of the size of the box. |
michael@0 | 4362 | */ |
michael@0 | 4363 | const nsStyleCoord &coord = display->mTransformOrigin[index]; |
michael@0 | 4364 | if (coord.GetUnit() == eStyleUnit_Calc) { |
michael@0 | 4365 | const nsStyleCoord::Calc *calc = coord.GetCalcValue(); |
michael@0 | 4366 | coords[index] = |
michael@0 | 4367 | NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * |
michael@0 | 4368 | calc->mPercent + |
michael@0 | 4369 | NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); |
michael@0 | 4370 | } else if (coord.GetUnit() == eStyleUnit_Percent) { |
michael@0 | 4371 | coords[index] = |
michael@0 | 4372 | NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * |
michael@0 | 4373 | coord.GetPercentValue(); |
michael@0 | 4374 | } else { |
michael@0 | 4375 | NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); |
michael@0 | 4376 | coords[index] = |
michael@0 | 4377 | NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); |
michael@0 | 4378 | } |
michael@0 | 4379 | if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) && |
michael@0 | 4380 | coord.GetUnit() != eStyleUnit_Percent) { |
michael@0 | 4381 | // <length> values represent offsets from the origin of the SVG element's |
michael@0 | 4382 | // user space, not the top left of its bounds, so we must adjust for that: |
michael@0 | 4383 | nscoord offset = |
michael@0 | 4384 | (index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y; |
michael@0 | 4385 | coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel); |
michael@0 | 4386 | } |
michael@0 | 4387 | } |
michael@0 | 4388 | |
michael@0 | 4389 | coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), |
michael@0 | 4390 | aAppUnitsPerPixel); |
michael@0 | 4391 | /* Adjust based on the origin of the rectangle. */ |
michael@0 | 4392 | coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel); |
michael@0 | 4393 | coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel); |
michael@0 | 4394 | |
michael@0 | 4395 | return gfxPoint3D(coords[0], coords[1], coords[2]); |
michael@0 | 4396 | } |
michael@0 | 4397 | |
michael@0 | 4398 | /* Returns the delta specified by the -moz-perspective-origin property. |
michael@0 | 4399 | * This is a positive delta, meaning that it indicates the direction to move |
michael@0 | 4400 | * to get from (0, 0) of the frame to the perspective origin. This function is |
michael@0 | 4401 | * called off the main thread. |
michael@0 | 4402 | */ |
michael@0 | 4403 | /* static */ gfxPoint3D |
michael@0 | 4404 | nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame, |
michael@0 | 4405 | float aAppUnitsPerPixel) |
michael@0 | 4406 | { |
michael@0 | 4407 | NS_PRECONDITION(aFrame, "Can't get delta for a null frame!"); |
michael@0 | 4408 | NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(), |
michael@0 | 4409 | "Shouldn't get a delta for an untransformed frame!"); |
michael@0 | 4410 | |
michael@0 | 4411 | if (!aFrame->IsTransformed()) { |
michael@0 | 4412 | return gfxPoint3D(); |
michael@0 | 4413 | } |
michael@0 | 4414 | |
michael@0 | 4415 | /* For both of the coordinates, if the value of -moz-perspective-origin is a |
michael@0 | 4416 | * percentage, it's relative to the size of the frame. Otherwise, if it's |
michael@0 | 4417 | * a distance, it's already computed for us! |
michael@0 | 4418 | */ |
michael@0 | 4419 | |
michael@0 | 4420 | //TODO: Should this be using our bounds or the parent's bounds? |
michael@0 | 4421 | // How do we handle aBoundsOverride in the latter case? |
michael@0 | 4422 | nsIFrame* parent = aFrame->GetParentStyleContextFrame(); |
michael@0 | 4423 | if (!parent) { |
michael@0 | 4424 | return gfxPoint3D(); |
michael@0 | 4425 | } |
michael@0 | 4426 | const nsStyleDisplay* display = parent->StyleDisplay(); |
michael@0 | 4427 | nsRect boundingRect = nsDisplayTransform::GetFrameBoundsForTransform(parent); |
michael@0 | 4428 | |
michael@0 | 4429 | /* Allows us to access named variables by index. */ |
michael@0 | 4430 | gfxPoint3D result; |
michael@0 | 4431 | result.z = 0.0f; |
michael@0 | 4432 | gfxFloat* coords[2] = {&result.x, &result.y}; |
michael@0 | 4433 | const nscoord* dimensions[2] = |
michael@0 | 4434 | {&boundingRect.width, &boundingRect.height}; |
michael@0 | 4435 | |
michael@0 | 4436 | for (uint8_t index = 0; index < 2; ++index) { |
michael@0 | 4437 | /* If the -moz-transform-origin specifies a percentage, take the percentage |
michael@0 | 4438 | * of the size of the box. |
michael@0 | 4439 | */ |
michael@0 | 4440 | const nsStyleCoord &coord = display->mPerspectiveOrigin[index]; |
michael@0 | 4441 | if (coord.GetUnit() == eStyleUnit_Calc) { |
michael@0 | 4442 | const nsStyleCoord::Calc *calc = coord.GetCalcValue(); |
michael@0 | 4443 | *coords[index] = |
michael@0 | 4444 | NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * |
michael@0 | 4445 | calc->mPercent + |
michael@0 | 4446 | NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); |
michael@0 | 4447 | } else if (coord.GetUnit() == eStyleUnit_Percent) { |
michael@0 | 4448 | *coords[index] = |
michael@0 | 4449 | NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * |
michael@0 | 4450 | coord.GetPercentValue(); |
michael@0 | 4451 | } else { |
michael@0 | 4452 | NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); |
michael@0 | 4453 | *coords[index] = |
michael@0 | 4454 | NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); |
michael@0 | 4455 | } |
michael@0 | 4456 | } |
michael@0 | 4457 | |
michael@0 | 4458 | nsPoint parentOffset = aFrame->GetOffsetTo(parent); |
michael@0 | 4459 | gfxPoint3D gfxOffset( |
michael@0 | 4460 | NSAppUnitsToFloatPixels(parentOffset.x, aAppUnitsPerPixel), |
michael@0 | 4461 | NSAppUnitsToFloatPixels(parentOffset.y, aAppUnitsPerPixel), |
michael@0 | 4462 | 0.0f); |
michael@0 | 4463 | |
michael@0 | 4464 | return result - gfxOffset; |
michael@0 | 4465 | } |
michael@0 | 4466 | |
michael@0 | 4467 | nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame, |
michael@0 | 4468 | float aAppUnitsPerPixel, |
michael@0 | 4469 | const nsRect* aBoundsOverride) |
michael@0 | 4470 | : mFrame(aFrame) |
michael@0 | 4471 | , mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform) |
michael@0 | 4472 | , mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride)) |
michael@0 | 4473 | , mToPerspectiveOrigin(GetDeltaToPerspectiveOrigin(aFrame, aAppUnitsPerPixel)) |
michael@0 | 4474 | , mChildPerspective(0) |
michael@0 | 4475 | { |
michael@0 | 4476 | const nsStyleDisplay* parentDisp = nullptr; |
michael@0 | 4477 | nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent(); |
michael@0 | 4478 | if (parentStyleContext) { |
michael@0 | 4479 | parentDisp = parentStyleContext->StyleDisplay(); |
michael@0 | 4480 | } |
michael@0 | 4481 | if (parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord) { |
michael@0 | 4482 | mChildPerspective = parentDisp->mChildPerspective.GetCoordValue(); |
michael@0 | 4483 | } |
michael@0 | 4484 | } |
michael@0 | 4485 | |
michael@0 | 4486 | /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that |
michael@0 | 4487 | * translates from local coordinate space to transform coordinate space, then |
michael@0 | 4488 | * hands it back. |
michael@0 | 4489 | */ |
michael@0 | 4490 | gfx3DMatrix |
michael@0 | 4491 | nsDisplayTransform::GetResultingTransformMatrix(const FrameTransformProperties& aProperties, |
michael@0 | 4492 | const nsPoint& aOrigin, |
michael@0 | 4493 | float aAppUnitsPerPixel, |
michael@0 | 4494 | const nsRect* aBoundsOverride, |
michael@0 | 4495 | nsIFrame** aOutAncestor) |
michael@0 | 4496 | { |
michael@0 | 4497 | return GetResultingTransformMatrixInternal(aProperties, aOrigin, aAppUnitsPerPixel, |
michael@0 | 4498 | aBoundsOverride, aOutAncestor); |
michael@0 | 4499 | } |
michael@0 | 4500 | |
michael@0 | 4501 | gfx3DMatrix |
michael@0 | 4502 | nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, |
michael@0 | 4503 | const nsPoint& aOrigin, |
michael@0 | 4504 | float aAppUnitsPerPixel, |
michael@0 | 4505 | const nsRect* aBoundsOverride, |
michael@0 | 4506 | nsIFrame** aOutAncestor) |
michael@0 | 4507 | { |
michael@0 | 4508 | FrameTransformProperties props(aFrame, |
michael@0 | 4509 | aAppUnitsPerPixel, |
michael@0 | 4510 | aBoundsOverride); |
michael@0 | 4511 | |
michael@0 | 4512 | return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel, |
michael@0 | 4513 | aBoundsOverride, aOutAncestor); |
michael@0 | 4514 | } |
michael@0 | 4515 | |
michael@0 | 4516 | gfx3DMatrix |
michael@0 | 4517 | nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties, |
michael@0 | 4518 | const nsPoint& aOrigin, |
michael@0 | 4519 | float aAppUnitsPerPixel, |
michael@0 | 4520 | const nsRect* aBoundsOverride, |
michael@0 | 4521 | nsIFrame** aOutAncestor) |
michael@0 | 4522 | { |
michael@0 | 4523 | const nsIFrame *frame = aProperties.mFrame; |
michael@0 | 4524 | |
michael@0 | 4525 | if (aOutAncestor) { |
michael@0 | 4526 | *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame); |
michael@0 | 4527 | } |
michael@0 | 4528 | |
michael@0 | 4529 | /* Account for the -moz-transform-origin property by translating the |
michael@0 | 4530 | * coordinate space to the new origin. |
michael@0 | 4531 | */ |
michael@0 | 4532 | gfxPoint3D newOrigin = |
michael@0 | 4533 | gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel), |
michael@0 | 4534 | NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel), |
michael@0 | 4535 | 0.0f); |
michael@0 | 4536 | |
michael@0 | 4537 | /* Get the underlying transform matrix. This requires us to get the |
michael@0 | 4538 | * bounds of the frame. |
michael@0 | 4539 | */ |
michael@0 | 4540 | nsRect bounds = (aBoundsOverride ? *aBoundsOverride : |
michael@0 | 4541 | nsDisplayTransform::GetFrameBoundsForTransform(frame)); |
michael@0 | 4542 | |
michael@0 | 4543 | /* Get the matrix, then change its basis to factor in the origin. */ |
michael@0 | 4544 | bool dummy; |
michael@0 | 4545 | gfx3DMatrix result; |
michael@0 | 4546 | // Call IsSVGTransformed() regardless of the value of |
michael@0 | 4547 | // disp->mSpecifiedTransform, since we still need any transformFromSVGParent. |
michael@0 | 4548 | mozilla::gfx::Matrix svgTransform, transformFromSVGParent; |
michael@0 | 4549 | bool hasSVGTransforms = |
michael@0 | 4550 | frame && frame->IsSVGTransformed(&svgTransform, &transformFromSVGParent); |
michael@0 | 4551 | /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */ |
michael@0 | 4552 | if (aProperties.mTransformList) { |
michael@0 | 4553 | result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead, |
michael@0 | 4554 | frame ? frame->StyleContext() : nullptr, |
michael@0 | 4555 | frame ? frame->PresContext() : nullptr, |
michael@0 | 4556 | dummy, bounds, aAppUnitsPerPixel); |
michael@0 | 4557 | } else if (hasSVGTransforms) { |
michael@0 | 4558 | // Correct the translation components for zoom: |
michael@0 | 4559 | float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() / |
michael@0 | 4560 | aAppUnitsPerPixel; |
michael@0 | 4561 | svgTransform._31 *= pixelsPerCSSPx; |
michael@0 | 4562 | svgTransform._32 *= pixelsPerCSSPx; |
michael@0 | 4563 | result = gfx3DMatrix::From2D(ThebesMatrix(svgTransform)); |
michael@0 | 4564 | } |
michael@0 | 4565 | |
michael@0 | 4566 | if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) { |
michael@0 | 4567 | // Correct the translation components for zoom: |
michael@0 | 4568 | float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() / |
michael@0 | 4569 | aAppUnitsPerPixel; |
michael@0 | 4570 | transformFromSVGParent._31 *= pixelsPerCSSPx; |
michael@0 | 4571 | transformFromSVGParent._32 *= pixelsPerCSSPx; |
michael@0 | 4572 | result = result * gfx3DMatrix::From2D(ThebesMatrix(transformFromSVGParent)); |
michael@0 | 4573 | } |
michael@0 | 4574 | |
michael@0 | 4575 | if (aProperties.mChildPerspective > 0.0) { |
michael@0 | 4576 | gfx3DMatrix perspective; |
michael@0 | 4577 | perspective._34 = |
michael@0 | 4578 | -1.0 / NSAppUnitsToFloatPixels(aProperties.mChildPerspective, aAppUnitsPerPixel); |
michael@0 | 4579 | /* At the point when perspective is applied, we have been translated to the transform origin. |
michael@0 | 4580 | * The translation to the perspective origin is the difference between these values. |
michael@0 | 4581 | */ |
michael@0 | 4582 | result = result * nsLayoutUtils::ChangeMatrixBasis(aProperties.mToPerspectiveOrigin - aProperties.mToTransformOrigin, perspective); |
michael@0 | 4583 | } |
michael@0 | 4584 | |
michael@0 | 4585 | gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x), |
michael@0 | 4586 | hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y), |
michael@0 | 4587 | 0); |
michael@0 | 4588 | |
michael@0 | 4589 | if (frame && frame->Preserves3D()) { |
michael@0 | 4590 | // Include the transform set on our parent |
michael@0 | 4591 | NS_ASSERTION(frame->GetParent() && |
michael@0 | 4592 | frame->GetParent()->IsTransformed() && |
michael@0 | 4593 | frame->GetParent()->Preserves3DChildren(), |
michael@0 | 4594 | "Preserve3D mismatch!"); |
michael@0 | 4595 | FrameTransformProperties props(frame->GetParent(), |
michael@0 | 4596 | aAppUnitsPerPixel, |
michael@0 | 4597 | nullptr); |
michael@0 | 4598 | gfx3DMatrix parent = |
michael@0 | 4599 | GetResultingTransformMatrixInternal(props, |
michael@0 | 4600 | aOrigin - frame->GetPosition(), |
michael@0 | 4601 | aAppUnitsPerPixel, nullptr, aOutAncestor); |
michael@0 | 4602 | return nsLayoutUtils::ChangeMatrixBasis(rounded + aProperties.mToTransformOrigin, result) * parent; |
michael@0 | 4603 | } |
michael@0 | 4604 | |
michael@0 | 4605 | return nsLayoutUtils::ChangeMatrixBasis |
michael@0 | 4606 | (rounded + aProperties.mToTransformOrigin, result); |
michael@0 | 4607 | } |
michael@0 | 4608 | |
michael@0 | 4609 | bool |
michael@0 | 4610 | nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) |
michael@0 | 4611 | { |
michael@0 | 4612 | if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity)) { |
michael@0 | 4613 | return true; |
michael@0 | 4614 | } |
michael@0 | 4615 | |
michael@0 | 4616 | if (nsLayoutUtils::IsAnimationLoggingEnabled()) { |
michael@0 | 4617 | nsCString message; |
michael@0 | 4618 | message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for opacity animation"); |
michael@0 | 4619 | CommonElementAnimationData::LogAsyncAnimationFailure(message, |
michael@0 | 4620 | Frame()->GetContent()); |
michael@0 | 4621 | } |
michael@0 | 4622 | return false; |
michael@0 | 4623 | } |
michael@0 | 4624 | |
michael@0 | 4625 | bool |
michael@0 | 4626 | nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) |
michael@0 | 4627 | { |
michael@0 | 4628 | return ShouldPrerenderTransformedContent(aBuilder, |
michael@0 | 4629 | Frame(), |
michael@0 | 4630 | nsLayoutUtils::IsAnimationLoggingEnabled()); |
michael@0 | 4631 | } |
michael@0 | 4632 | |
michael@0 | 4633 | /* static */ bool |
michael@0 | 4634 | nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder, |
michael@0 | 4635 | nsIFrame* aFrame, |
michael@0 | 4636 | bool aLogAnimations) |
michael@0 | 4637 | { |
michael@0 | 4638 | // Elements whose transform has been modified recently, or which |
michael@0 | 4639 | // have a compositor-animated transform, can be prerendered. An element |
michael@0 | 4640 | // might have only just had its transform animated in which case |
michael@0 | 4641 | // the ActiveLayerManager may not have been notified yet. |
michael@0 | 4642 | if (!ActiveLayerTracker::IsStyleAnimated(aFrame, eCSSProperty_transform) && |
michael@0 | 4643 | (!aFrame->GetContent() || |
michael@0 | 4644 | !nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(), |
michael@0 | 4645 | eCSSProperty_transform))) { |
michael@0 | 4646 | if (aLogAnimations) { |
michael@0 | 4647 | nsCString message; |
michael@0 | 4648 | message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation"); |
michael@0 | 4649 | CommonElementAnimationData::LogAsyncAnimationFailure(message, |
michael@0 | 4650 | aFrame->GetContent()); |
michael@0 | 4651 | } |
michael@0 | 4652 | return false; |
michael@0 | 4653 | } |
michael@0 | 4654 | |
michael@0 | 4655 | nsSize refSize = aBuilder->RootReferenceFrame()->GetSize(); |
michael@0 | 4656 | // Only prerender if the transformed frame's size (in the reference |
michael@0 | 4657 | // frames coordinate space) is <= the reference frame size (~viewport), |
michael@0 | 4658 | // allowing a 1/8th fuzz factor for shadows, borders, etc. |
michael@0 | 4659 | refSize += nsSize(refSize.width / 8, refSize.height / 8); |
michael@0 | 4660 | nsRect frameRect = aFrame->GetVisualOverflowRectRelativeToSelf(); |
michael@0 | 4661 | |
michael@0 | 4662 | frameRect = |
michael@0 | 4663 | nsLayoutUtils::TransformFrameRectToAncestor(aFrame, frameRect, |
michael@0 | 4664 | aBuilder->RootReferenceFrame()); |
michael@0 | 4665 | |
michael@0 | 4666 | if (frameRect.Size() <= refSize) { |
michael@0 | 4667 | return true; |
michael@0 | 4668 | } |
michael@0 | 4669 | |
michael@0 | 4670 | if (aLogAnimations) { |
michael@0 | 4671 | nsCString message; |
michael@0 | 4672 | message.AppendLiteral("Performance warning: Async animation disabled because frame size ("); |
michael@0 | 4673 | message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.width)); |
michael@0 | 4674 | message.AppendLiteral(", "); |
michael@0 | 4675 | message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.height)); |
michael@0 | 4676 | message.AppendLiteral(") is bigger than the viewport ("); |
michael@0 | 4677 | message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.width)); |
michael@0 | 4678 | message.AppendLiteral(", "); |
michael@0 | 4679 | message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.height)); |
michael@0 | 4680 | message.AppendLiteral(")"); |
michael@0 | 4681 | CommonElementAnimationData::LogAsyncAnimationFailure(message, |
michael@0 | 4682 | aFrame->GetContent()); |
michael@0 | 4683 | } |
michael@0 | 4684 | return false; |
michael@0 | 4685 | } |
michael@0 | 4686 | |
michael@0 | 4687 | /* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */ |
michael@0 | 4688 | static bool IsFrameVisible(nsIFrame* aFrame, const gfx3DMatrix& aMatrix) |
michael@0 | 4689 | { |
michael@0 | 4690 | if (aMatrix.IsSingular()) { |
michael@0 | 4691 | return false; |
michael@0 | 4692 | } |
michael@0 | 4693 | if (aFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN && |
michael@0 | 4694 | aMatrix.IsBackfaceVisible()) { |
michael@0 | 4695 | return false; |
michael@0 | 4696 | } |
michael@0 | 4697 | return true; |
michael@0 | 4698 | } |
michael@0 | 4699 | |
michael@0 | 4700 | const gfx3DMatrix& |
michael@0 | 4701 | nsDisplayTransform::GetTransform() |
michael@0 | 4702 | { |
michael@0 | 4703 | if (mTransform.IsIdentity()) { |
michael@0 | 4704 | float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 4705 | gfxPoint3D newOrigin = |
michael@0 | 4706 | gfxPoint3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale), |
michael@0 | 4707 | NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), |
michael@0 | 4708 | 0.0f); |
michael@0 | 4709 | if (mTransformGetter) { |
michael@0 | 4710 | mTransform = mTransformGetter(mFrame, scale); |
michael@0 | 4711 | mTransform = nsLayoutUtils::ChangeMatrixBasis(newOrigin, mTransform); |
michael@0 | 4712 | } else { |
michael@0 | 4713 | mTransform = |
michael@0 | 4714 | GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale); |
michael@0 | 4715 | |
michael@0 | 4716 | /** |
michael@0 | 4717 | * Shift the coorindates to be relative to our reference frame instead of relative to this frame. |
michael@0 | 4718 | * When we have preserve-3d, our reference frame is already guaranteed to be an ancestor of the |
michael@0 | 4719 | * preserve-3d chain, so we only need to do this once. |
michael@0 | 4720 | */ |
michael@0 | 4721 | bool hasSVGTransforms = mFrame->IsSVGTransformed(); |
michael@0 | 4722 | gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x), |
michael@0 | 4723 | hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y), |
michael@0 | 4724 | 0); |
michael@0 | 4725 | mTransform.Translate(rounded); |
michael@0 | 4726 | } |
michael@0 | 4727 | } |
michael@0 | 4728 | return mTransform; |
michael@0 | 4729 | } |
michael@0 | 4730 | |
michael@0 | 4731 | bool |
michael@0 | 4732 | nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) |
michael@0 | 4733 | { |
michael@0 | 4734 | return ShouldPrerenderTransformedContent(aBuilder, mFrame, false); |
michael@0 | 4735 | } |
michael@0 | 4736 | |
michael@0 | 4737 | already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder, |
michael@0 | 4738 | LayerManager *aManager, |
michael@0 | 4739 | const ContainerLayerParameters& aContainerParameters) |
michael@0 | 4740 | { |
michael@0 | 4741 | const gfx3DMatrix& newTransformMatrix = GetTransform(); |
michael@0 | 4742 | |
michael@0 | 4743 | if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN && |
michael@0 | 4744 | newTransformMatrix.IsBackfaceVisible()) { |
michael@0 | 4745 | return nullptr; |
michael@0 | 4746 | } |
michael@0 | 4747 | |
michael@0 | 4748 | uint32_t flags = ShouldPrerenderTransformedContent(aBuilder, mFrame, false) ? |
michael@0 | 4749 | FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0; |
michael@0 | 4750 | nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()-> |
michael@0 | 4751 | BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetChildren(), |
michael@0 | 4752 | aContainerParameters, &newTransformMatrix, flags); |
michael@0 | 4753 | |
michael@0 | 4754 | if (!container) { |
michael@0 | 4755 | return nullptr; |
michael@0 | 4756 | } |
michael@0 | 4757 | |
michael@0 | 4758 | // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags, |
michael@0 | 4759 | // so we never need to explicitely unset this flag. |
michael@0 | 4760 | if (mFrame->Preserves3D() || mFrame->Preserves3DChildren()) { |
michael@0 | 4761 | container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D); |
michael@0 | 4762 | } else { |
michael@0 | 4763 | container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_PRESERVE_3D); |
michael@0 | 4764 | } |
michael@0 | 4765 | |
michael@0 | 4766 | nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder, |
michael@0 | 4767 | this, mFrame, |
michael@0 | 4768 | eCSSProperty_transform); |
michael@0 | 4769 | if (ShouldPrerenderTransformedContent(aBuilder, mFrame, false)) { |
michael@0 | 4770 | container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(), |
michael@0 | 4771 | /*the value is irrelevant*/nullptr); |
michael@0 | 4772 | container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_MAY_CHANGE_TRANSFORM); |
michael@0 | 4773 | } else { |
michael@0 | 4774 | container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey()); |
michael@0 | 4775 | container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_MAY_CHANGE_TRANSFORM); |
michael@0 | 4776 | } |
michael@0 | 4777 | return container.forget(); |
michael@0 | 4778 | } |
michael@0 | 4779 | |
michael@0 | 4780 | nsDisplayItem::LayerState |
michael@0 | 4781 | nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, |
michael@0 | 4782 | LayerManager* aManager, |
michael@0 | 4783 | const ContainerLayerParameters& aParameters) { |
michael@0 | 4784 | // If the transform is 3d, or the layer takes part in preserve-3d sorting |
michael@0 | 4785 | // then we *always* want this to be an active layer. |
michael@0 | 4786 | if (!GetTransform().Is2D() || mFrame->Preserves3D()) { |
michael@0 | 4787 | return LAYER_ACTIVE_FORCE; |
michael@0 | 4788 | } |
michael@0 | 4789 | // Here we check if the *post-transform* bounds of this item are big enough |
michael@0 | 4790 | // to justify an active layer. |
michael@0 | 4791 | if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_transform) && |
michael@0 | 4792 | !IsItemTooSmallForActiveLayer(this)) |
michael@0 | 4793 | return LAYER_ACTIVE; |
michael@0 | 4794 | if (mFrame->GetContent()) { |
michael@0 | 4795 | if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(), |
michael@0 | 4796 | eCSSProperty_transform)) { |
michael@0 | 4797 | return LAYER_ACTIVE; |
michael@0 | 4798 | } |
michael@0 | 4799 | } |
michael@0 | 4800 | |
michael@0 | 4801 | const nsStyleDisplay* disp = mFrame->StyleDisplay(); |
michael@0 | 4802 | if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM)) { |
michael@0 | 4803 | return LAYER_ACTIVE; |
michael@0 | 4804 | } |
michael@0 | 4805 | |
michael@0 | 4806 | // Expect the child display items to have this frame as their animated |
michael@0 | 4807 | // geometry root (since it will be their reference frame). If they have a |
michael@0 | 4808 | // different animated geometry root, we'll make this an active layer so the |
michael@0 | 4809 | // animation can be accelerated. |
michael@0 | 4810 | return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, |
michael@0 | 4811 | *mStoredList.GetChildren(), Frame()); |
michael@0 | 4812 | } |
michael@0 | 4813 | |
michael@0 | 4814 | bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder, |
michael@0 | 4815 | nsRegion *aVisibleRegion, |
michael@0 | 4816 | const nsRect& aAllowVisibleRegionExpansion) |
michael@0 | 4817 | { |
michael@0 | 4818 | /* As we do this, we need to be sure to |
michael@0 | 4819 | * untransform the visible rect, since we want everything that's painting to |
michael@0 | 4820 | * think that it's painting in its original rectangular coordinate space. |
michael@0 | 4821 | * If we can't untransform, take the entire overflow rect */ |
michael@0 | 4822 | nsRect untransformedVisibleRect; |
michael@0 | 4823 | if (ShouldPrerenderTransformedContent(aBuilder, mFrame) || |
michael@0 | 4824 | !UntransformVisibleRect(aBuilder, &untransformedVisibleRect)) |
michael@0 | 4825 | { |
michael@0 | 4826 | untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf(); |
michael@0 | 4827 | } |
michael@0 | 4828 | nsRegion untransformedVisible = untransformedVisibleRect; |
michael@0 | 4829 | // Call RecomputeVisiblity instead of ComputeVisibility since |
michael@0 | 4830 | // nsDisplayItem::ComputeVisibility should only be called from |
michael@0 | 4831 | // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item) |
michael@0 | 4832 | mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible); |
michael@0 | 4833 | return true; |
michael@0 | 4834 | } |
michael@0 | 4835 | |
michael@0 | 4836 | #ifdef DEBUG_HIT |
michael@0 | 4837 | #include <time.h> |
michael@0 | 4838 | #endif |
michael@0 | 4839 | |
michael@0 | 4840 | /* HitTest does some fun stuff with matrix transforms to obtain the answer. */ |
michael@0 | 4841 | void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder, |
michael@0 | 4842 | const nsRect& aRect, |
michael@0 | 4843 | HitTestState *aState, |
michael@0 | 4844 | nsTArray<nsIFrame*> *aOutFrames) |
michael@0 | 4845 | { |
michael@0 | 4846 | /* Here's how this works: |
michael@0 | 4847 | * 1. Get the matrix. If it's singular, abort (clearly we didn't hit |
michael@0 | 4848 | * anything). |
michael@0 | 4849 | * 2. Invert the matrix. |
michael@0 | 4850 | * 3. Use it to transform the rect into the correct space. |
michael@0 | 4851 | * 4. Pass that rect down through to the list's version of HitTest. |
michael@0 | 4852 | */ |
michael@0 | 4853 | // GetTransform always operates in dev pixels. |
michael@0 | 4854 | float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 4855 | gfx3DMatrix matrix = GetTransform(); |
michael@0 | 4856 | |
michael@0 | 4857 | if (!IsFrameVisible(mFrame, matrix)) { |
michael@0 | 4858 | return; |
michael@0 | 4859 | } |
michael@0 | 4860 | |
michael@0 | 4861 | /* We want to go from transformed-space to regular space. |
michael@0 | 4862 | * Thus we have to invert the matrix, which normally does |
michael@0 | 4863 | * the reverse operation (e.g. regular->transformed) |
michael@0 | 4864 | */ |
michael@0 | 4865 | bool snap; |
michael@0 | 4866 | nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); |
michael@0 | 4867 | gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), |
michael@0 | 4868 | NSAppUnitsToFloatPixels(childBounds.y, factor), |
michael@0 | 4869 | NSAppUnitsToFloatPixels(childBounds.width, factor), |
michael@0 | 4870 | NSAppUnitsToFloatPixels(childBounds.height, factor)); |
michael@0 | 4871 | |
michael@0 | 4872 | /* Now, apply the transform and pass it down the channel. */ |
michael@0 | 4873 | nsRect resultingRect; |
michael@0 | 4874 | if (aRect.width == 1 && aRect.height == 1) { |
michael@0 | 4875 | // Magic width/height indicating we're hit testing a point, not a rect |
michael@0 | 4876 | gfxPoint point; |
michael@0 | 4877 | if (!matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor), |
michael@0 | 4878 | NSAppUnitsToFloatPixels(aRect.y, factor)), |
michael@0 | 4879 | childGfxBounds, |
michael@0 | 4880 | &point)) { |
michael@0 | 4881 | return; |
michael@0 | 4882 | } |
michael@0 | 4883 | |
michael@0 | 4884 | resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor), |
michael@0 | 4885 | NSFloatPixelsToAppUnits(float(point.y), factor), |
michael@0 | 4886 | 1, 1); |
michael@0 | 4887 | |
michael@0 | 4888 | } else { |
michael@0 | 4889 | gfxRect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor), |
michael@0 | 4890 | NSAppUnitsToFloatPixels(aRect.y, factor), |
michael@0 | 4891 | NSAppUnitsToFloatPixels(aRect.width, factor), |
michael@0 | 4892 | NSAppUnitsToFloatPixels(aRect.height, factor)); |
michael@0 | 4893 | |
michael@0 | 4894 | gfxRect rect = matrix.UntransformBounds(originalRect, childGfxBounds); |
michael@0 | 4895 | |
michael@0 | 4896 | resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor), |
michael@0 | 4897 | NSFloatPixelsToAppUnits(float(rect.Y()), factor), |
michael@0 | 4898 | NSFloatPixelsToAppUnits(float(rect.Width()), factor), |
michael@0 | 4899 | NSFloatPixelsToAppUnits(float(rect.Height()), factor)); |
michael@0 | 4900 | } |
michael@0 | 4901 | |
michael@0 | 4902 | if (resultingRect.IsEmpty()) { |
michael@0 | 4903 | return; |
michael@0 | 4904 | } |
michael@0 | 4905 | |
michael@0 | 4906 | |
michael@0 | 4907 | #ifdef DEBUG_HIT |
michael@0 | 4908 | printf("Frame: %p\n", dynamic_cast<void *>(mFrame)); |
michael@0 | 4909 | printf(" Untransformed point: (%f, %f)\n", resultingRect.X(), resultingRect.Y()); |
michael@0 | 4910 | uint32_t originalFrameCount = aOutFrames.Length(); |
michael@0 | 4911 | #endif |
michael@0 | 4912 | |
michael@0 | 4913 | mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames); |
michael@0 | 4914 | |
michael@0 | 4915 | #ifdef DEBUG_HIT |
michael@0 | 4916 | if (originalFrameCount != aOutFrames.Length()) |
michael@0 | 4917 | printf(" Hit! Time: %f, first frame: %p\n", static_cast<double>(clock()), |
michael@0 | 4918 | dynamic_cast<void *>(aOutFrames.ElementAt(0))); |
michael@0 | 4919 | printf("=== end of hit test ===\n"); |
michael@0 | 4920 | #endif |
michael@0 | 4921 | |
michael@0 | 4922 | } |
michael@0 | 4923 | |
michael@0 | 4924 | float |
michael@0 | 4925 | nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint) |
michael@0 | 4926 | { |
michael@0 | 4927 | // GetTransform always operates in dev pixels. |
michael@0 | 4928 | float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 4929 | gfx3DMatrix matrix = GetTransform(); |
michael@0 | 4930 | |
michael@0 | 4931 | NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!"); |
michael@0 | 4932 | |
michael@0 | 4933 | bool snap; |
michael@0 | 4934 | nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); |
michael@0 | 4935 | gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), |
michael@0 | 4936 | NSAppUnitsToFloatPixels(childBounds.y, factor), |
michael@0 | 4937 | NSAppUnitsToFloatPixels(childBounds.width, factor), |
michael@0 | 4938 | NSAppUnitsToFloatPixels(childBounds.height, factor)); |
michael@0 | 4939 | |
michael@0 | 4940 | gfxPoint point; |
michael@0 | 4941 | DebugOnly<bool> result = matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor), |
michael@0 | 4942 | NSAppUnitsToFloatPixels(aPoint.y, factor)), |
michael@0 | 4943 | childGfxBounds, |
michael@0 | 4944 | &point); |
michael@0 | 4945 | NS_ASSERTION(result, "Why are we trying to get the depth for a point we didn't hit?"); |
michael@0 | 4946 | |
michael@0 | 4947 | gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0)); |
michael@0 | 4948 | return transformed.z; |
michael@0 | 4949 | } |
michael@0 | 4950 | |
michael@0 | 4951 | /* The bounding rectangle for the object is the overflow rectangle translated |
michael@0 | 4952 | * by the reference point. |
michael@0 | 4953 | */ |
michael@0 | 4954 | nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap) |
michael@0 | 4955 | { |
michael@0 | 4956 | nsRect untransformedBounds = |
michael@0 | 4957 | ShouldPrerenderTransformedContent(aBuilder, mFrame) ? |
michael@0 | 4958 | mFrame->GetVisualOverflowRectRelativeToSelf() : |
michael@0 | 4959 | mStoredList.GetBounds(aBuilder, aSnap); |
michael@0 | 4960 | *aSnap = false; |
michael@0 | 4961 | // GetTransform always operates in dev pixels. |
michael@0 | 4962 | float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 4963 | return nsLayoutUtils::MatrixTransformRect(untransformedBounds, |
michael@0 | 4964 | GetTransform(), |
michael@0 | 4965 | factor); |
michael@0 | 4966 | } |
michael@0 | 4967 | |
michael@0 | 4968 | /* The transform is opaque iff the transform consists solely of scales and |
michael@0 | 4969 | * translations and if the underlying content is opaque. Thus if the transform |
michael@0 | 4970 | * is of the form |
michael@0 | 4971 | * |
michael@0 | 4972 | * |a c e| |
michael@0 | 4973 | * |b d f| |
michael@0 | 4974 | * |0 0 1| |
michael@0 | 4975 | * |
michael@0 | 4976 | * We need b and c to be zero. |
michael@0 | 4977 | * |
michael@0 | 4978 | * We also need to check whether the underlying opaque content completely fills |
michael@0 | 4979 | * our visible rect. We use UntransformRect which expands to the axis-aligned |
michael@0 | 4980 | * bounding rect, but that's OK since if |
michael@0 | 4981 | * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it |
michael@0 | 4982 | * certainly contains the actual (non-axis-aligned) untransformed rect. |
michael@0 | 4983 | */ |
michael@0 | 4984 | nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder, |
michael@0 | 4985 | bool* aSnap) |
michael@0 | 4986 | { |
michael@0 | 4987 | *aSnap = false; |
michael@0 | 4988 | nsRect untransformedVisible; |
michael@0 | 4989 | // If we're going to prerender all our content, pretend like we |
michael@0 | 4990 | // don't have opqaue content so that everything under us is rendered |
michael@0 | 4991 | // as well. That will increase graphics memory usage if our frame |
michael@0 | 4992 | // covers the entire window, but it allows our transform to be |
michael@0 | 4993 | // updated extremely cheaply, without invalidating any other |
michael@0 | 4994 | // content. |
michael@0 | 4995 | if (ShouldPrerenderTransformedContent(aBuilder, mFrame) || |
michael@0 | 4996 | !UntransformVisibleRect(aBuilder, &untransformedVisible)) { |
michael@0 | 4997 | return nsRegion(); |
michael@0 | 4998 | } |
michael@0 | 4999 | |
michael@0 | 5000 | const gfx3DMatrix& matrix = GetTransform(); |
michael@0 | 5001 | |
michael@0 | 5002 | nsRegion result; |
michael@0 | 5003 | gfxMatrix matrix2d; |
michael@0 | 5004 | bool tmpSnap; |
michael@0 | 5005 | if (matrix.Is2D(&matrix2d) && |
michael@0 | 5006 | matrix2d.PreservesAxisAlignedRectangles() && |
michael@0 | 5007 | mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap).Contains(untransformedVisible)) { |
michael@0 | 5008 | result = mVisibleRect; |
michael@0 | 5009 | } |
michael@0 | 5010 | return result; |
michael@0 | 5011 | } |
michael@0 | 5012 | |
michael@0 | 5013 | /* The transform is uniform if it fills the entire bounding rect and the |
michael@0 | 5014 | * wrapped list is uniform. See GetOpaqueRegion for discussion of why this |
michael@0 | 5015 | * works. |
michael@0 | 5016 | */ |
michael@0 | 5017 | bool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor) |
michael@0 | 5018 | { |
michael@0 | 5019 | nsRect untransformedVisible; |
michael@0 | 5020 | if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) { |
michael@0 | 5021 | return false; |
michael@0 | 5022 | } |
michael@0 | 5023 | const gfx3DMatrix& matrix = GetTransform(); |
michael@0 | 5024 | |
michael@0 | 5025 | gfxMatrix matrix2d; |
michael@0 | 5026 | return matrix.Is2D(&matrix2d) && |
michael@0 | 5027 | matrix2d.PreservesAxisAlignedRectangles() && |
michael@0 | 5028 | mStoredList.GetVisibleRect().Contains(untransformedVisible) && |
michael@0 | 5029 | mStoredList.IsUniform(aBuilder, aColor); |
michael@0 | 5030 | } |
michael@0 | 5031 | |
michael@0 | 5032 | /* If UNIFIED_CONTINUATIONS is defined, we can merge two display lists that |
michael@0 | 5033 | * share the same underlying content. Otherwise, doing so results in graphical |
michael@0 | 5034 | * glitches. |
michael@0 | 5035 | */ |
michael@0 | 5036 | #ifndef UNIFIED_CONTINUATIONS |
michael@0 | 5037 | |
michael@0 | 5038 | bool |
michael@0 | 5039 | nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder, |
michael@0 | 5040 | nsDisplayItem *aItem) |
michael@0 | 5041 | { |
michael@0 | 5042 | return false; |
michael@0 | 5043 | } |
michael@0 | 5044 | |
michael@0 | 5045 | #else |
michael@0 | 5046 | |
michael@0 | 5047 | bool |
michael@0 | 5048 | nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder, |
michael@0 | 5049 | nsDisplayItem *aItem) |
michael@0 | 5050 | { |
michael@0 | 5051 | NS_PRECONDITION(aItem, "Why did you try merging with a null item?"); |
michael@0 | 5052 | NS_PRECONDITION(aBuilder, "Why did you try merging with a null builder?"); |
michael@0 | 5053 | |
michael@0 | 5054 | /* Make sure that we're dealing with two transforms. */ |
michael@0 | 5055 | if (aItem->GetType() != TYPE_TRANSFORM) |
michael@0 | 5056 | return false; |
michael@0 | 5057 | |
michael@0 | 5058 | /* Check to see that both frames are part of the same content. */ |
michael@0 | 5059 | if (aItem->Frame()->GetContent() != mFrame->GetContent()) |
michael@0 | 5060 | return false; |
michael@0 | 5061 | |
michael@0 | 5062 | if (aItem->GetClip() != GetClip()) |
michael@0 | 5063 | return false; |
michael@0 | 5064 | |
michael@0 | 5065 | /* Now, move everything over to this frame and signal that |
michael@0 | 5066 | * we merged things! |
michael@0 | 5067 | */ |
michael@0 | 5068 | mStoredList.MergeFrom(&static_cast<nsDisplayTransform*>(aItem)->mStoredList); |
michael@0 | 5069 | return true; |
michael@0 | 5070 | } |
michael@0 | 5071 | |
michael@0 | 5072 | #endif |
michael@0 | 5073 | |
michael@0 | 5074 | /* TransformRect takes in as parameters a rectangle (in app space) and returns |
michael@0 | 5075 | * the smallest rectangle (in app space) containing the transformed image of |
michael@0 | 5076 | * that rectangle. That is, it takes the four corners of the rectangle, |
michael@0 | 5077 | * transforms them according to the matrix associated with the specified frame, |
michael@0 | 5078 | * then returns the smallest rectangle containing the four transformed points. |
michael@0 | 5079 | * |
michael@0 | 5080 | * @param aUntransformedBounds The rectangle (in app units) to transform. |
michael@0 | 5081 | * @param aFrame The frame whose transformation should be applied. |
michael@0 | 5082 | * @param aOrigin The delta from the frame origin to the coordinate space origin |
michael@0 | 5083 | * @param aBoundsOverride (optional) Force the frame bounds to be the |
michael@0 | 5084 | * specified bounds. |
michael@0 | 5085 | * @return The smallest rectangle containing the image of the transformed |
michael@0 | 5086 | * rectangle. |
michael@0 | 5087 | */ |
michael@0 | 5088 | nsRect nsDisplayTransform::TransformRect(const nsRect &aUntransformedBounds, |
michael@0 | 5089 | const nsIFrame* aFrame, |
michael@0 | 5090 | const nsPoint &aOrigin, |
michael@0 | 5091 | const nsRect* aBoundsOverride) |
michael@0 | 5092 | { |
michael@0 | 5093 | NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); |
michael@0 | 5094 | |
michael@0 | 5095 | float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 5096 | return nsLayoutUtils::MatrixTransformRect |
michael@0 | 5097 | (aUntransformedBounds, |
michael@0 | 5098 | GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), |
michael@0 | 5099 | factor); |
michael@0 | 5100 | } |
michael@0 | 5101 | |
michael@0 | 5102 | nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds, |
michael@0 | 5103 | const nsIFrame* aFrame, |
michael@0 | 5104 | const nsPoint &aOrigin, |
michael@0 | 5105 | const nsRect* aBoundsOverride) |
michael@0 | 5106 | { |
michael@0 | 5107 | NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); |
michael@0 | 5108 | |
michael@0 | 5109 | float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 5110 | return nsLayoutUtils::MatrixTransformRectOut |
michael@0 | 5111 | (aUntransformedBounds, |
michael@0 | 5112 | GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), |
michael@0 | 5113 | factor); |
michael@0 | 5114 | } |
michael@0 | 5115 | |
michael@0 | 5116 | bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds, |
michael@0 | 5117 | const nsRect &aChildBounds, |
michael@0 | 5118 | const nsIFrame* aFrame, |
michael@0 | 5119 | const nsPoint &aOrigin, |
michael@0 | 5120 | nsRect *aOutRect) |
michael@0 | 5121 | { |
michael@0 | 5122 | NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); |
michael@0 | 5123 | |
michael@0 | 5124 | float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 5125 | |
michael@0 | 5126 | gfx3DMatrix transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr); |
michael@0 | 5127 | if (transform.IsSingular()) { |
michael@0 | 5128 | return false; |
michael@0 | 5129 | } |
michael@0 | 5130 | |
michael@0 | 5131 | gfxRect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor), |
michael@0 | 5132 | NSAppUnitsToFloatPixels(aTransformedBounds.y, factor), |
michael@0 | 5133 | NSAppUnitsToFloatPixels(aTransformedBounds.width, factor), |
michael@0 | 5134 | NSAppUnitsToFloatPixels(aTransformedBounds.height, factor)); |
michael@0 | 5135 | |
michael@0 | 5136 | gfxRect childGfxBounds(NSAppUnitsToFloatPixels(aChildBounds.x, factor), |
michael@0 | 5137 | NSAppUnitsToFloatPixels(aChildBounds.y, factor), |
michael@0 | 5138 | NSAppUnitsToFloatPixels(aChildBounds.width, factor), |
michael@0 | 5139 | NSAppUnitsToFloatPixels(aChildBounds.height, factor)); |
michael@0 | 5140 | |
michael@0 | 5141 | result = transform.UntransformBounds(result, childGfxBounds); |
michael@0 | 5142 | *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); |
michael@0 | 5143 | return true; |
michael@0 | 5144 | } |
michael@0 | 5145 | |
michael@0 | 5146 | bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder, |
michael@0 | 5147 | nsRect *aOutRect) |
michael@0 | 5148 | { |
michael@0 | 5149 | const gfx3DMatrix& matrix = GetTransform(); |
michael@0 | 5150 | if (matrix.IsSingular()) |
michael@0 | 5151 | return false; |
michael@0 | 5152 | |
michael@0 | 5153 | // GetTransform always operates in dev pixels. |
michael@0 | 5154 | float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); |
michael@0 | 5155 | gfxRect result(NSAppUnitsToFloatPixels(mVisibleRect.x, factor), |
michael@0 | 5156 | NSAppUnitsToFloatPixels(mVisibleRect.y, factor), |
michael@0 | 5157 | NSAppUnitsToFloatPixels(mVisibleRect.width, factor), |
michael@0 | 5158 | NSAppUnitsToFloatPixels(mVisibleRect.height, factor)); |
michael@0 | 5159 | |
michael@0 | 5160 | bool snap; |
michael@0 | 5161 | nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); |
michael@0 | 5162 | gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), |
michael@0 | 5163 | NSAppUnitsToFloatPixels(childBounds.y, factor), |
michael@0 | 5164 | NSAppUnitsToFloatPixels(childBounds.width, factor), |
michael@0 | 5165 | NSAppUnitsToFloatPixels(childBounds.height, factor)); |
michael@0 | 5166 | |
michael@0 | 5167 | /* We want to untransform the matrix, so invert the transformation first! */ |
michael@0 | 5168 | result = matrix.UntransformBounds(result, childGfxBounds); |
michael@0 | 5169 | |
michael@0 | 5170 | *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); |
michael@0 | 5171 | |
michael@0 | 5172 | return true; |
michael@0 | 5173 | } |
michael@0 | 5174 | |
michael@0 | 5175 | nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, |
michael@0 | 5176 | nsIFrame* aFrame, nsDisplayList* aList) |
michael@0 | 5177 | : nsDisplayWrapList(aBuilder, aFrame, aList), |
michael@0 | 5178 | mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf()) |
michael@0 | 5179 | { |
michael@0 | 5180 | MOZ_COUNT_CTOR(nsDisplaySVGEffects); |
michael@0 | 5181 | } |
michael@0 | 5182 | |
michael@0 | 5183 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 5184 | nsDisplaySVGEffects::~nsDisplaySVGEffects() |
michael@0 | 5185 | { |
michael@0 | 5186 | MOZ_COUNT_DTOR(nsDisplaySVGEffects); |
michael@0 | 5187 | } |
michael@0 | 5188 | #endif |
michael@0 | 5189 | |
michael@0 | 5190 | nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, |
michael@0 | 5191 | bool* aSnap) |
michael@0 | 5192 | { |
michael@0 | 5193 | *aSnap = false; |
michael@0 | 5194 | return nsRegion(); |
michael@0 | 5195 | } |
michael@0 | 5196 | |
michael@0 | 5197 | void |
michael@0 | 5198 | nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
michael@0 | 5199 | HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) |
michael@0 | 5200 | { |
michael@0 | 5201 | nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2); |
michael@0 | 5202 | if (nsSVGIntegrationUtils::HitTestFrameForEffects(mFrame, |
michael@0 | 5203 | rectCenter - ToReferenceFrame())) { |
michael@0 | 5204 | mList.HitTest(aBuilder, aRect, aState, aOutFrames); |
michael@0 | 5205 | } |
michael@0 | 5206 | } |
michael@0 | 5207 | |
michael@0 | 5208 | void |
michael@0 | 5209 | nsDisplaySVGEffects::PaintAsLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 5210 | nsRenderingContext* aCtx, |
michael@0 | 5211 | LayerManager* aManager) |
michael@0 | 5212 | { |
michael@0 | 5213 | nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx, mFrame, |
michael@0 | 5214 | mVisibleRect, |
michael@0 | 5215 | aBuilder, aManager); |
michael@0 | 5216 | } |
michael@0 | 5217 | |
michael@0 | 5218 | LayerState |
michael@0 | 5219 | nsDisplaySVGEffects::GetLayerState(nsDisplayListBuilder* aBuilder, |
michael@0 | 5220 | LayerManager* aManager, |
michael@0 | 5221 | const ContainerLayerParameters& aParameters) |
michael@0 | 5222 | { |
michael@0 | 5223 | return LAYER_SVG_EFFECTS; |
michael@0 | 5224 | } |
michael@0 | 5225 | |
michael@0 | 5226 | already_AddRefed<Layer> |
michael@0 | 5227 | nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder, |
michael@0 | 5228 | LayerManager* aManager, |
michael@0 | 5229 | const ContainerLayerParameters& aContainerParameters) |
michael@0 | 5230 | { |
michael@0 | 5231 | const nsIContent* content = mFrame->GetContent(); |
michael@0 | 5232 | bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); |
michael@0 | 5233 | if (hasSVGLayout) { |
michael@0 | 5234 | nsISVGChildFrame *svgChildFrame = do_QueryFrame(mFrame); |
michael@0 | 5235 | if (!svgChildFrame || !mFrame->GetContent()->IsSVG()) { |
michael@0 | 5236 | NS_ASSERTION(false, "why?"); |
michael@0 | 5237 | return nullptr; |
michael@0 | 5238 | } |
michael@0 | 5239 | if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { |
michael@0 | 5240 | return nullptr; // The SVG spec says not to draw filters for this |
michael@0 | 5241 | } |
michael@0 | 5242 | } |
michael@0 | 5243 | |
michael@0 | 5244 | float opacity = mFrame->StyleDisplay()->mOpacity; |
michael@0 | 5245 | if (opacity == 0.0f) |
michael@0 | 5246 | return nullptr; |
michael@0 | 5247 | |
michael@0 | 5248 | nsIFrame* firstFrame = |
michael@0 | 5249 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); |
michael@0 | 5250 | nsSVGEffects::EffectProperties effectProperties = |
michael@0 | 5251 | nsSVGEffects::GetEffectProperties(firstFrame); |
michael@0 | 5252 | |
michael@0 | 5253 | bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); |
michael@0 | 5254 | effectProperties.GetClipPathFrame(&isOK); |
michael@0 | 5255 | effectProperties.GetMaskFrame(&isOK); |
michael@0 | 5256 | |
michael@0 | 5257 | if (!isOK) { |
michael@0 | 5258 | return nullptr; |
michael@0 | 5259 | } |
michael@0 | 5260 | |
michael@0 | 5261 | ContainerLayerParameters newContainerParameters = aContainerParameters; |
michael@0 | 5262 | if (effectProperties.HasValidFilter()) { |
michael@0 | 5263 | newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; |
michael@0 | 5264 | } |
michael@0 | 5265 | |
michael@0 | 5266 | nsRefPtr<ContainerLayer> container = aManager->GetLayerBuilder()-> |
michael@0 | 5267 | BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, |
michael@0 | 5268 | newContainerParameters, nullptr); |
michael@0 | 5269 | |
michael@0 | 5270 | return container.forget(); |
michael@0 | 5271 | } |
michael@0 | 5272 | |
michael@0 | 5273 | bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder, |
michael@0 | 5274 | nsRegion* aVisibleRegion, |
michael@0 | 5275 | const nsRect& aAllowVisibleRegionExpansion) { |
michael@0 | 5276 | nsPoint offset = ToReferenceFrame(); |
michael@0 | 5277 | nsRect dirtyRect = |
michael@0 | 5278 | nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mFrame, |
michael@0 | 5279 | mVisibleRect - offset) + |
michael@0 | 5280 | offset; |
michael@0 | 5281 | |
michael@0 | 5282 | // Our children may be made translucent or arbitrarily deformed so we should |
michael@0 | 5283 | // not allow them to subtract area from aVisibleRegion. |
michael@0 | 5284 | nsRegion childrenVisible(dirtyRect); |
michael@0 | 5285 | nsRect r = dirtyRect.Intersect(mList.GetBounds(aBuilder)); |
michael@0 | 5286 | mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r, nsRect()); |
michael@0 | 5287 | return true; |
michael@0 | 5288 | } |
michael@0 | 5289 | |
michael@0 | 5290 | bool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) |
michael@0 | 5291 | { |
michael@0 | 5292 | if (aItem->GetType() != TYPE_SVG_EFFECTS) |
michael@0 | 5293 | return false; |
michael@0 | 5294 | // items for the same content element should be merged into a single |
michael@0 | 5295 | // compositing group |
michael@0 | 5296 | // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects |
michael@0 | 5297 | if (aItem->Frame()->GetContent() != mFrame->GetContent()) |
michael@0 | 5298 | return false; |
michael@0 | 5299 | if (aItem->GetClip() != GetClip()) |
michael@0 | 5300 | return false; |
michael@0 | 5301 | nsDisplaySVGEffects* other = static_cast<nsDisplaySVGEffects*>(aItem); |
michael@0 | 5302 | MergeFromTrackingMergedFrames(other); |
michael@0 | 5303 | mEffectsBounds.UnionRect(mEffectsBounds, |
michael@0 | 5304 | other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame)); |
michael@0 | 5305 | return true; |
michael@0 | 5306 | } |
michael@0 | 5307 | |
michael@0 | 5308 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 5309 | void |
michael@0 | 5310 | nsDisplaySVGEffects::PrintEffects(nsACString& aTo) |
michael@0 | 5311 | { |
michael@0 | 5312 | nsIFrame* firstFrame = |
michael@0 | 5313 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); |
michael@0 | 5314 | nsSVGEffects::EffectProperties effectProperties = |
michael@0 | 5315 | nsSVGEffects::GetEffectProperties(firstFrame); |
michael@0 | 5316 | bool isOK = true; |
michael@0 | 5317 | nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); |
michael@0 | 5318 | bool first = true; |
michael@0 | 5319 | aTo += " effects=("; |
michael@0 | 5320 | if (mFrame->StyleDisplay()->mOpacity != 1.0f) { |
michael@0 | 5321 | first = false; |
michael@0 | 5322 | aTo += nsPrintfCString("opacity(%f)", mFrame->StyleDisplay()->mOpacity); |
michael@0 | 5323 | } |
michael@0 | 5324 | if (clipPathFrame) { |
michael@0 | 5325 | if (!first) { |
michael@0 | 5326 | aTo += ", "; |
michael@0 | 5327 | } |
michael@0 | 5328 | aTo += nsPrintfCString("clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial"); |
michael@0 | 5329 | first = false; |
michael@0 | 5330 | } |
michael@0 | 5331 | if (effectProperties.HasValidFilter()) { |
michael@0 | 5332 | if (!first) { |
michael@0 | 5333 | aTo += ", "; |
michael@0 | 5334 | } |
michael@0 | 5335 | aTo += "filter"; |
michael@0 | 5336 | first = false; |
michael@0 | 5337 | } |
michael@0 | 5338 | if (effectProperties.GetMaskFrame(&isOK)) { |
michael@0 | 5339 | if (!first) { |
michael@0 | 5340 | aTo += ", "; |
michael@0 | 5341 | } |
michael@0 | 5342 | aTo += "mask"; |
michael@0 | 5343 | } |
michael@0 | 5344 | aTo += ")"; |
michael@0 | 5345 | } |
michael@0 | 5346 | #endif |
michael@0 | 5347 |