layout/base/nsDisplayList.cpp

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

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

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

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

mercurial