michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: set ts=2 sw=2 et tw=78: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: */ michael@0: michael@0: /* michael@0: * structures that represent things to be painted (ordered in z-order), michael@0: * used during painting and hit testing michael@0: */ michael@0: michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/layers/PLayerTransaction.h" michael@0: michael@0: #include "nsDisplayList.h" michael@0: michael@0: #include "nsCSSRendering.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsISelectionController.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsRegion.h" michael@0: #include "nsStyleStructInlines.h" michael@0: #include "nsStyleTransformMatrix.h" michael@0: #include "gfxMatrix.h" michael@0: #include "nsSVGIntegrationUtils.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsIFrameInlines.h" michael@0: #include "nsThemeConstants.h" michael@0: #include "LayerTreeInvalidation.h" michael@0: michael@0: #include "imgIContainer.h" michael@0: #include "BasicLayers.h" michael@0: #include "nsBoxFrame.h" michael@0: #include "nsViewportFrame.h" michael@0: #include "nsSubDocumentFrame.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "nsSVGElement.h" michael@0: #include "nsSVGClipPathFrame.h" michael@0: #include "GeckoProfiler.h" michael@0: #include "nsAnimationManager.h" michael@0: #include "nsTransitionManager.h" michael@0: #include "nsViewManager.h" michael@0: #include "ImageLayers.h" michael@0: #include "ImageContainer.h" michael@0: #include "nsCanvasFrame.h" michael@0: #include "StickyScrollContainer.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "ActiveLayerTracker.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "UnitTransforms.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::css; michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::dom; michael@0: typedef FrameMetrics::ViewID ViewID; michael@0: michael@0: static inline nsIFrame* michael@0: GetTransformRootFrame(nsIFrame* aFrame) michael@0: { michael@0: return nsLayoutUtils::GetTransformRootFrame(aFrame); michael@0: } michael@0: michael@0: static void AddTransformFunctions(nsCSSValueList* aList, michael@0: nsStyleContext* aContext, michael@0: nsPresContext* aPresContext, michael@0: nsRect& aBounds, michael@0: float aAppUnitsPerPixel, michael@0: InfallibleTArray& aFunctions) michael@0: { michael@0: if (aList->mValue.GetUnit() == eCSSUnit_None) { michael@0: return; michael@0: } michael@0: michael@0: for (const nsCSSValueList* curr = aList; curr; curr = curr->mNext) { michael@0: const nsCSSValue& currElem = curr->mValue; michael@0: NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function, michael@0: "Stream should consist solely of functions!"); michael@0: nsCSSValue::Array* array = currElem.GetArrayValue(); michael@0: bool canStoreInRuleTree = true; michael@0: switch (nsStyleTransformMatrix::TransformFunctionOf(array)) { michael@0: case eCSSKeyword_rotatex: michael@0: { michael@0: double theta = array->Item(1).GetAngleValueInRadians(); michael@0: aFunctions.AppendElement(RotationX(theta)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_rotatey: michael@0: { michael@0: double theta = array->Item(1).GetAngleValueInRadians(); michael@0: aFunctions.AppendElement(RotationY(theta)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_rotatez: michael@0: { michael@0: double theta = array->Item(1).GetAngleValueInRadians(); michael@0: aFunctions.AppendElement(RotationZ(theta)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_rotate: michael@0: { michael@0: double theta = array->Item(1).GetAngleValueInRadians(); michael@0: aFunctions.AppendElement(Rotation(theta)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_rotate3d: michael@0: { michael@0: double x = array->Item(1).GetFloatValue(); michael@0: double y = array->Item(2).GetFloatValue(); michael@0: double z = array->Item(3).GetFloatValue(); michael@0: double theta = array->Item(4).GetAngleValueInRadians(); michael@0: aFunctions.AppendElement(Rotation3D(x, y, z, theta)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_scalex: michael@0: { michael@0: double x = array->Item(1).GetFloatValue(); michael@0: aFunctions.AppendElement(Scale(x, 1, 1)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_scaley: michael@0: { michael@0: double y = array->Item(1).GetFloatValue(); michael@0: aFunctions.AppendElement(Scale(1, y, 1)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_scalez: michael@0: { michael@0: double z = array->Item(1).GetFloatValue(); michael@0: aFunctions.AppendElement(Scale(1, 1, z)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_scale: michael@0: { michael@0: double x = array->Item(1).GetFloatValue(); michael@0: // scale(x) is shorthand for scale(x, x); michael@0: double y = array->Count() == 2 ? x : array->Item(2).GetFloatValue(); michael@0: aFunctions.AppendElement(Scale(x, y, 1)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_scale3d: michael@0: { michael@0: double x = array->Item(1).GetFloatValue(); michael@0: double y = array->Item(2).GetFloatValue(); michael@0: double z = array->Item(3).GetFloatValue(); michael@0: aFunctions.AppendElement(Scale(x, y, z)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_translatex: michael@0: { michael@0: double x = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(1), aContext, aPresContext, canStoreInRuleTree, michael@0: aBounds.Width(), aAppUnitsPerPixel); michael@0: aFunctions.AppendElement(Translation(x, 0, 0)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_translatey: michael@0: { michael@0: double y = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(1), aContext, aPresContext, canStoreInRuleTree, michael@0: aBounds.Height(), aAppUnitsPerPixel); michael@0: aFunctions.AppendElement(Translation(0, y, 0)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_translatez: michael@0: { michael@0: double z = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(1), aContext, aPresContext, canStoreInRuleTree, michael@0: 0, aAppUnitsPerPixel); michael@0: aFunctions.AppendElement(Translation(0, 0, z)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_translate: michael@0: { michael@0: double x = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(1), aContext, aPresContext, canStoreInRuleTree, michael@0: aBounds.Width(), aAppUnitsPerPixel); michael@0: // translate(x) is shorthand for translate(x, 0) michael@0: double y = 0; michael@0: if (array->Count() == 3) { michael@0: y = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(2), aContext, aPresContext, canStoreInRuleTree, michael@0: aBounds.Height(), aAppUnitsPerPixel); michael@0: } michael@0: aFunctions.AppendElement(Translation(x, y, 0)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_translate3d: michael@0: { michael@0: double x = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(1), aContext, aPresContext, canStoreInRuleTree, michael@0: aBounds.Width(), aAppUnitsPerPixel); michael@0: double y = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(2), aContext, aPresContext, canStoreInRuleTree, michael@0: aBounds.Height(), aAppUnitsPerPixel); michael@0: double z = nsStyleTransformMatrix::ProcessTranslatePart( michael@0: array->Item(3), aContext, aPresContext, canStoreInRuleTree, michael@0: 0, aAppUnitsPerPixel); michael@0: michael@0: aFunctions.AppendElement(Translation(x, y, z)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_skewx: michael@0: { michael@0: double x = array->Item(1).GetAngleValueInRadians(); michael@0: aFunctions.AppendElement(SkewX(x)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_skewy: michael@0: { michael@0: double y = array->Item(1).GetAngleValueInRadians(); michael@0: aFunctions.AppendElement(SkewY(y)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_skew: michael@0: { michael@0: double x = array->Item(1).GetAngleValueInRadians(); michael@0: // skew(x) is shorthand for skew(x, 0) michael@0: double y = 0; michael@0: if (array->Count() == 3) { michael@0: y = array->Item(2).GetAngleValueInRadians(); michael@0: } michael@0: aFunctions.AppendElement(Skew(x, y)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_matrix: michael@0: { michael@0: gfx::Matrix4x4 matrix; michael@0: matrix._11 = array->Item(1).GetFloatValue(); michael@0: matrix._12 = array->Item(2).GetFloatValue(); michael@0: matrix._13 = 0; michael@0: matrix._14 = 0; michael@0: matrix._21 = array->Item(3).GetFloatValue(); michael@0: matrix._22 = array->Item(4).GetFloatValue(); michael@0: matrix._23 = 0; michael@0: matrix._24 = 0; michael@0: matrix._31 = 0; michael@0: matrix._32 = 0; michael@0: matrix._33 = 1; michael@0: matrix._34 = 0; michael@0: matrix._41 = array->Item(5).GetFloatValue(); michael@0: matrix._42 = array->Item(6).GetFloatValue(); michael@0: matrix._43 = 0; michael@0: matrix._44 = 1; michael@0: aFunctions.AppendElement(TransformMatrix(matrix)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_matrix3d: michael@0: { michael@0: gfx::Matrix4x4 matrix; michael@0: matrix._11 = array->Item(1).GetFloatValue(); michael@0: matrix._12 = array->Item(2).GetFloatValue(); michael@0: matrix._13 = array->Item(3).GetFloatValue(); michael@0: matrix._14 = array->Item(4).GetFloatValue(); michael@0: matrix._21 = array->Item(5).GetFloatValue(); michael@0: matrix._22 = array->Item(6).GetFloatValue(); michael@0: matrix._23 = array->Item(7).GetFloatValue(); michael@0: matrix._24 = array->Item(8).GetFloatValue(); michael@0: matrix._31 = array->Item(9).GetFloatValue(); michael@0: matrix._32 = array->Item(10).GetFloatValue(); michael@0: matrix._33 = array->Item(11).GetFloatValue(); michael@0: matrix._34 = array->Item(12).GetFloatValue(); michael@0: matrix._41 = array->Item(13).GetFloatValue(); michael@0: matrix._42 = array->Item(14).GetFloatValue(); michael@0: matrix._43 = array->Item(15).GetFloatValue(); michael@0: matrix._44 = array->Item(16).GetFloatValue(); michael@0: aFunctions.AppendElement(TransformMatrix(matrix)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_interpolatematrix: michael@0: { michael@0: gfx3DMatrix matrix; michael@0: nsStyleTransformMatrix::ProcessInterpolateMatrix(matrix, array, michael@0: aContext, michael@0: aPresContext, michael@0: canStoreInRuleTree, michael@0: aBounds, michael@0: aAppUnitsPerPixel); michael@0: gfx::Matrix4x4 transform; michael@0: gfx::ToMatrix4x4(matrix, transform); michael@0: aFunctions.AppendElement(TransformMatrix(transform)); michael@0: break; michael@0: } michael@0: case eCSSKeyword_perspective: michael@0: { michael@0: aFunctions.AppendElement(Perspective(array->Item(1).GetFloatValue())); michael@0: break; michael@0: } michael@0: default: michael@0: NS_ERROR("Function not handled yet!"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static TimingFunction michael@0: ToTimingFunction(css::ComputedTimingFunction& aCTF) michael@0: { michael@0: if (aCTF.GetType() == nsTimingFunction::Function) { michael@0: const nsSMILKeySpline* spline = aCTF.GetFunction(); michael@0: return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(), michael@0: spline->X2(), spline->Y2())); michael@0: } michael@0: michael@0: uint32_t type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2; michael@0: return TimingFunction(StepFunction(aCTF.GetSteps(), type)); michael@0: } michael@0: michael@0: static void michael@0: AddAnimationForProperty(nsIFrame* aFrame, nsCSSProperty aProperty, michael@0: mozilla::StyleAnimation* ea, Layer* aLayer, michael@0: AnimationData& aData, bool aPending) michael@0: { michael@0: NS_ASSERTION(aLayer->AsContainerLayer(), "Should only animate ContainerLayer"); michael@0: nsStyleContext* styleContext = aFrame->StyleContext(); michael@0: nsPresContext* presContext = aFrame->PresContext(); michael@0: nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); michael@0: // all data passed directly to the compositor should be in css pixels michael@0: float scale = nsDeviceContext::AppUnitsPerCSSPixel(); michael@0: michael@0: mozilla::layers::Animation* animation = michael@0: aPending ? michael@0: aLayer->AddAnimationForNextTransaction() : michael@0: aLayer->AddAnimation(); michael@0: michael@0: animation->startTime() = ea->mStartTime + ea->mDelay; michael@0: animation->duration() = ea->mIterationDuration; michael@0: animation->numIterations() = michael@0: ea->mIterationCount != NS_IEEEPositiveInfinity() ? ea->mIterationCount : -1; michael@0: animation->direction() = ea->mDirection; michael@0: animation->property() = aProperty; michael@0: animation->data() = aData; michael@0: michael@0: for (uint32_t propIdx = 0; propIdx < ea->mProperties.Length(); propIdx++) { michael@0: AnimationProperty* property = &ea->mProperties[propIdx]; michael@0: michael@0: if (aProperty != property->mProperty) { michael@0: continue; michael@0: } michael@0: michael@0: for (uint32_t segIdx = 0; segIdx < property->mSegments.Length(); segIdx++) { michael@0: AnimationPropertySegment* segment = &property->mSegments[segIdx]; michael@0: michael@0: AnimationSegment* animSegment = animation->segments().AppendElement(); michael@0: if (aProperty == eCSSProperty_transform) { michael@0: animSegment->startState() = InfallibleTArray(); michael@0: animSegment->endState() = InfallibleTArray(); michael@0: michael@0: nsCSSValueSharedList* list = segment->mFromValue.GetCSSValueSharedListValue(); michael@0: AddTransformFunctions(list->mHead, styleContext, presContext, bounds, scale, michael@0: animSegment->startState().get_ArrayOfTransformFunction()); michael@0: michael@0: list = segment->mToValue.GetCSSValueSharedListValue(); michael@0: AddTransformFunctions(list->mHead, styleContext, presContext, bounds, scale, michael@0: animSegment->endState().get_ArrayOfTransformFunction()); michael@0: } else if (aProperty == eCSSProperty_opacity) { michael@0: animSegment->startState() = segment->mFromValue.GetFloatValue(); michael@0: animSegment->endState() = segment->mToValue.GetFloatValue(); michael@0: } michael@0: michael@0: animSegment->startPortion() = segment->mFromKey; michael@0: animSegment->endPortion() = segment->mToKey; michael@0: animSegment->sampleFn() = ToTimingFunction(segment->mTimingFunction); michael@0: } michael@0: } michael@0: } michael@0: michael@0: template michael@0: static void michael@0: AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty, michael@0: nsTArray& aAnimations, michael@0: Layer* aLayer, AnimationData& aData, michael@0: bool aPending) { michael@0: mozilla::TimeStamp currentTime = michael@0: aFrame->PresContext()->RefreshDriver()->MostRecentRefresh(); michael@0: for (uint32_t animIdx = 0; animIdx < aAnimations.Length(); animIdx++) { michael@0: mozilla::StyleAnimation* anim = &aAnimations[animIdx]; michael@0: if (!(anim->HasAnimationOfProperty(aProperty) && michael@0: anim->IsRunningAt(currentTime))) { michael@0: continue; michael@0: } michael@0: AddAnimationForProperty(aFrame, aProperty, anim, aLayer, aData, aPending); michael@0: anim->mIsRunningOnCompositor = true; michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer, michael@0: nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem, michael@0: nsIFrame* aFrame, michael@0: nsCSSProperty aProperty) michael@0: { michael@0: // This function can be called in two ways: from michael@0: // nsDisplay*::BuildLayer while constructing a layer (with all michael@0: // pointers non-null), or from RestyleManager's handling of michael@0: // UpdateOpacityLayer/UpdateTransformLayer hints. michael@0: MOZ_ASSERT(!aBuilder == !aItem, michael@0: "should only be called in two configurations, with both " michael@0: "aBuilder and aItem, or with neither"); michael@0: MOZ_ASSERT(!aItem || aFrame == aItem->Frame(), "frame mismatch"); michael@0: michael@0: bool pending = !aBuilder; michael@0: michael@0: if (pending) { michael@0: aLayer->ClearAnimationsForNextTransaction(); michael@0: } else { michael@0: aLayer->ClearAnimations(); michael@0: } michael@0: michael@0: nsIContent* content = aFrame->GetContent(); michael@0: if (!content) { michael@0: return; michael@0: } michael@0: ElementTransitions* et = michael@0: nsTransitionManager::GetTransitionsForCompositor(content, aProperty); michael@0: michael@0: ElementAnimations* ea = michael@0: nsAnimationManager::GetAnimationsForCompositor(content, aProperty); michael@0: michael@0: if (!ea && !et) { michael@0: return; michael@0: } michael@0: michael@0: // If the frame is not prerendered, bail out. michael@0: // Do this check only during layer construction; during updating the michael@0: // caller is required to check it appropriately. michael@0: if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) { michael@0: // AnimationManager or TransitionManager need to know that we refused to michael@0: // run this animation asynchronously so that they will not throttle the michael@0: // main thread animation. michael@0: aFrame->Properties().Set(nsIFrame::RefusedAsyncAnimation(), michael@0: reinterpret_cast(intptr_t(true))); michael@0: michael@0: // We need to schedule another refresh driver run so that AnimationManager michael@0: // or TransitionManager get a chance to unthrottle the animation. michael@0: aFrame->SchedulePaint(); michael@0: return; michael@0: } michael@0: michael@0: AnimationData data; michael@0: if (aProperty == eCSSProperty_transform) { michael@0: nsRect bounds = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); michael@0: // all data passed directly to the compositor should be in css pixels michael@0: float scale = nsDeviceContext::AppUnitsPerCSSPixel(); michael@0: gfxPoint3D offsetToTransformOrigin = michael@0: nsDisplayTransform::GetDeltaToTransformOrigin(aFrame, scale, &bounds); michael@0: gfxPoint3D offsetToPerspectiveOrigin = michael@0: nsDisplayTransform::GetDeltaToPerspectiveOrigin(aFrame, scale); michael@0: nscoord perspective = 0.0; michael@0: nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent(); michael@0: if (parentStyleContext) { michael@0: const nsStyleDisplay* disp = parentStyleContext->StyleDisplay(); michael@0: if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) { michael@0: perspective = disp->mChildPerspective.GetCoordValue(); michael@0: } michael@0: } michael@0: nsPoint origin; michael@0: if (aItem) { michael@0: origin = aItem->ToReferenceFrame(); michael@0: } else { michael@0: // transform display items used a reference frame computed from michael@0: // their GetTransformRootFrame(). michael@0: nsIFrame* referenceFrame = michael@0: nsLayoutUtils::GetReferenceFrame(GetTransformRootFrame(aFrame)); michael@0: origin = aFrame->GetOffsetToCrossDoc(referenceFrame); michael@0: } michael@0: michael@0: data = TransformData(origin, offsetToTransformOrigin, michael@0: offsetToPerspectiveOrigin, bounds, perspective, michael@0: aFrame->PresContext()->AppUnitsPerDevPixel()); michael@0: } else if (aProperty == eCSSProperty_opacity) { michael@0: data = null_t(); michael@0: } michael@0: michael@0: if (et) { michael@0: AddAnimationsForProperty(aFrame, aProperty, et->mPropertyTransitions, michael@0: aLayer, data, pending); michael@0: aLayer->SetAnimationGeneration(et->mAnimationGeneration); michael@0: } michael@0: michael@0: if (ea) { michael@0: AddAnimationsForProperty(aFrame, aProperty, ea->mAnimations, michael@0: aLayer, data, pending); michael@0: aLayer->SetAnimationGeneration(ea->mAnimationGeneration); michael@0: } michael@0: } michael@0: michael@0: nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, michael@0: Mode aMode, bool aBuildCaret) michael@0: : mReferenceFrame(aReferenceFrame), michael@0: mIgnoreScrollFrame(nullptr), michael@0: mLayerEventRegions(nullptr), michael@0: mCurrentTableItem(nullptr), michael@0: mFinalTransparentRegion(nullptr), michael@0: mCachedOffsetFrame(aReferenceFrame), michael@0: mCachedReferenceFrame(aReferenceFrame), michael@0: mCachedOffset(0, 0), michael@0: mGlassDisplayItem(nullptr), michael@0: mMode(aMode), michael@0: mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID), michael@0: mBuildCaret(aBuildCaret), michael@0: mIgnoreSuppression(false), michael@0: mHadToIgnoreSuppression(false), michael@0: mIsAtRootOfPseudoStackingContext(false), michael@0: mIncludeAllOutOfFlows(false), michael@0: mDescendIntoSubdocuments(true), michael@0: mSelectedFramesOnly(false), michael@0: mAccurateVisibleRegions(false), michael@0: mAllowMergingAndFlattening(true), michael@0: mWillComputePluginGeometry(false), michael@0: mInTransform(false), michael@0: mInFixedPos(false), michael@0: mSyncDecodeImages(false), michael@0: mIsPaintingToWindow(false), michael@0: mIsCompositingCheap(false), michael@0: mContainsPluginItem(false), michael@0: mContainsBlendMode(false), michael@0: mAncestorHasTouchEventHandler(false), michael@0: mHaveScrollableDisplayPort(false) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayListBuilder); michael@0: PL_InitArenaPool(&mPool, "displayListArena", 1024, michael@0: std::max(NS_ALIGNMENT_OF(void*),NS_ALIGNMENT_OF(double))-1); michael@0: michael@0: nsPresContext* pc = aReferenceFrame->PresContext(); michael@0: nsIPresShell *shell = pc->PresShell(); michael@0: if (pc->IsRenderingOnlySelection()) { michael@0: nsCOMPtr selcon(do_QueryInterface(shell)); michael@0: if (selcon) { michael@0: selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, michael@0: getter_AddRefs(mBoundingSelection)); michael@0: } michael@0: } michael@0: michael@0: nsCSSRendering::BeginFrameTreesLocked(); michael@0: PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS)); michael@0: } michael@0: michael@0: static void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) { michael@0: for (nsIFrame* f = aFrame; f; michael@0: f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) { michael@0: if (f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) michael@0: return; michael@0: f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO); michael@0: if (f == aStopAtFrame) { michael@0: // we've reached a frame that we know will be painted, so we can stop. michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, michael@0: nsIFrame* aFrame, michael@0: const nsRect& aDirtyRect) michael@0: { michael@0: nsRect dirtyRectRelativeToDirtyFrame = aDirtyRect; michael@0: if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame)) { michael@0: NS_ASSERTION(aDirtyFrame == aFrame->GetParent(), "Dirty frame should be viewport frame"); michael@0: // position: fixed items are reflowed into and only drawn inside the michael@0: // viewport, or the scroll position clamping scrollport size, if one is michael@0: // set. michael@0: nsIPresShell* ps = aFrame->PresContext()->PresShell(); michael@0: dirtyRectRelativeToDirtyFrame.MoveTo(0, 0); michael@0: if (ps->IsScrollPositionClampingScrollPortSizeSet()) { michael@0: dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetScrollPositionClampingScrollPortSize()); michael@0: } else { michael@0: dirtyRectRelativeToDirtyFrame.SizeTo(aDirtyFrame->GetSize()); michael@0: } michael@0: } michael@0: michael@0: nsRect dirty = dirtyRectRelativeToDirtyFrame - aFrame->GetOffsetTo(aDirtyFrame); michael@0: nsRect overflowRect = aFrame->GetVisualOverflowRect(); michael@0: michael@0: if (aFrame->IsTransformed() && michael@0: nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(), michael@0: eCSSProperty_transform)) { michael@0: /** michael@0: * Add a fuzz factor to the overflow rectangle so that elements only just michael@0: * out of view are pulled into the display list, so they can be michael@0: * prerendered if necessary. michael@0: */ michael@0: overflowRect.Inflate(nsPresContext::CSSPixelsToAppUnits(32)); michael@0: } michael@0: michael@0: if (!dirty.IntersectRect(dirty, overflowRect)) michael@0: return; michael@0: const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants(); michael@0: OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty) michael@0: : new OutOfFlowDisplayData(dirty); michael@0: aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data); michael@0: michael@0: MarkFrameForDisplay(aFrame, aDirtyFrame); michael@0: } michael@0: michael@0: static void UnmarkFrameForDisplay(nsIFrame* aFrame) { michael@0: nsPresContext* presContext = aFrame->PresContext(); michael@0: presContext->PropertyTable()-> michael@0: Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty()); michael@0: michael@0: for (nsIFrame* f = aFrame; f; michael@0: f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) { michael@0: if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) michael@0: return; michael@0: f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO); michael@0: } michael@0: } michael@0: michael@0: static bool gPrintApzcTree = false; michael@0: michael@0: static bool GetApzcTreePrintPref() { michael@0: static bool initialized = false; michael@0: if (!initialized) { michael@0: Preferences::AddBoolVarCache(&gPrintApzcTree, "apz.printtree", gPrintApzcTree); michael@0: initialized = true; michael@0: } michael@0: return gPrintApzcTree; michael@0: } michael@0: michael@0: static void RecordFrameMetrics(nsIFrame* aForFrame, michael@0: nsIFrame* aScrollFrame, michael@0: const nsIFrame* aReferenceFrame, michael@0: ContainerLayer* aRoot, michael@0: const nsRect& aVisibleRect, michael@0: const nsRect& aViewport, michael@0: nsRect* aDisplayPort, michael@0: nsRect* aCriticalDisplayPort, michael@0: ViewID aScrollId, michael@0: bool aIsRoot, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: nsPresContext* presContext = aForFrame->PresContext(); michael@0: int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); michael@0: LayoutDeviceToLayerScale resolution(aContainerParameters.mXScale, aContainerParameters.mYScale); michael@0: michael@0: nsIntRect visible = aVisibleRect.ScaleToNearestPixels( michael@0: resolution.scale, resolution.scale, auPerDevPixel); michael@0: aRoot->SetVisibleRegion(visible); michael@0: michael@0: FrameMetrics metrics; michael@0: metrics.mViewport = CSSRect::FromAppUnits(aViewport); michael@0: if (aDisplayPort) { michael@0: metrics.mDisplayPort = CSSRect::FromAppUnits(*aDisplayPort); michael@0: if (aCriticalDisplayPort) { michael@0: metrics.mCriticalDisplayPort = CSSRect::FromAppUnits(*aCriticalDisplayPort); michael@0: } michael@0: } michael@0: michael@0: nsIScrollableFrame* scrollableFrame = nullptr; michael@0: if (aScrollFrame) michael@0: scrollableFrame = aScrollFrame->GetScrollTargetFrame(); michael@0: michael@0: metrics.mScrollableRect = CSSRect::FromAppUnits( michael@0: nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame)); michael@0: michael@0: if (scrollableFrame) { michael@0: nsPoint scrollPosition = scrollableFrame->GetScrollPosition(); michael@0: metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition)); michael@0: michael@0: // If the frame was scrolled since the last layers update, and by michael@0: // something other than the APZ code, we want to tell the APZ to update michael@0: // its scroll offset. michael@0: nsIAtom* originOfLastScroll = scrollableFrame->OriginOfLastScroll(); michael@0: if (originOfLastScroll && originOfLastScroll != nsGkAtoms::apz) { michael@0: metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration()); michael@0: } michael@0: } michael@0: michael@0: metrics.SetScrollId(aScrollId); michael@0: metrics.mIsRoot = aIsRoot; michael@0: michael@0: // Only the root scrollable frame for a given presShell should pick up michael@0: // the presShell's resolution. All the other frames are 1.0. michael@0: nsIPresShell* presShell = presContext->GetPresShell(); michael@0: if (aScrollFrame == presShell->GetRootScrollFrame()) { michael@0: metrics.mResolution = ParentLayerToLayerScale(presShell->GetXResolution(), michael@0: presShell->GetYResolution()); michael@0: } else { michael@0: metrics.mResolution = ParentLayerToLayerScale(1.0f); michael@0: } michael@0: michael@0: // For the cumulateive resolution, multiply the resolutions of all the michael@0: // presShells back up to the root michael@0: metrics.mCumulativeResolution = LayoutDeviceToLayerScale(1.0f); michael@0: nsIPresShell* curPresShell = presShell; michael@0: while (curPresShell != nullptr) { michael@0: ParentLayerToLayerScale presShellResolution(curPresShell->GetXResolution(), michael@0: curPresShell->GetYResolution()); michael@0: metrics.mCumulativeResolution.scale *= presShellResolution.scale; michael@0: nsPresContext* parentContext = curPresShell->GetPresContext()->GetParentPresContext(); michael@0: curPresShell = parentContext ? parentContext->GetPresShell() : nullptr; michael@0: } michael@0: michael@0: metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale( michael@0: (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel); michael@0: michael@0: // Initially, AsyncPanZoomController should render the content to the screen michael@0: // at the painted resolution. michael@0: const LayerToScreenScale layerToScreenScale(1.0f); michael@0: metrics.SetZoom(metrics.mCumulativeResolution * metrics.mDevPixelsPerCSSPixel michael@0: * layerToScreenScale); michael@0: michael@0: if (presShell) { michael@0: nsIDocument* document = nullptr; michael@0: document = presShell->GetDocument(); michael@0: if (document) { michael@0: nsCOMPtr innerWin(document->GetInnerWindow()); michael@0: if (innerWin) { michael@0: metrics.mMayHaveTouchListeners = innerWin->HasTouchEventListeners(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: LayoutDeviceToParentLayerScale layoutToParentLayerScale = michael@0: // The ScreenToParentLayerScale should be mTransformScale which is not calculated yet, michael@0: // but we don't yet handle CSS transforms, so we assume it's 1 here. michael@0: metrics.mCumulativeResolution * LayerToScreenScale(1.0) * ScreenToParentLayerScale(1.0); michael@0: michael@0: // Calculate the composition bounds as the size of the scroll frame and michael@0: // its origin relative to the reference frame. michael@0: // If aScrollFrame is null, we are in a document without a root scroll frame, michael@0: // so it's a xul document. In this case, use the size of the viewport frame. michael@0: nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame; michael@0: nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame), michael@0: frameForCompositionBoundsCalculation->GetSize()); michael@0: metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel) michael@0: * layoutToParentLayerScale); michael@0: michael@0: michael@0: // For the root scroll frame of the root content document, the above calculation michael@0: // will yield the size of the viewport frame as the composition bounds, which michael@0: // doesn't actually correspond to what is visible when michael@0: // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of michael@0: // the prescontext that the viewport frame is reflowed into. In that case if our michael@0: // document has a widget then the widget's bounds will correspond to what is michael@0: // visible. If we don't have a widget the root view's bounds correspond to what michael@0: // would be visible because they don't get modified by setCSSViewport. michael@0: bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument() michael@0: && aScrollFrame == presShell->GetRootScrollFrame(); michael@0: if (isRootContentDocRootScrollFrame) { michael@0: if (nsIFrame* rootFrame = presShell->GetRootFrame()) { michael@0: if (nsView* view = rootFrame->GetView()) { michael@0: nsRect viewBoundsAppUnits = view->GetBounds() + rootFrame->GetOffsetToCrossDoc(aReferenceFrame); michael@0: ParentLayerIntRect viewBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(viewBoundsAppUnits, auPerDevPixel) michael@0: * layoutToParentLayerScale); michael@0: michael@0: // On Android, we need to do things a bit differently to get things michael@0: // right (see bug 983208, bug 988882). We use the bounds of the nearest michael@0: // widget, but clamp the height to the view bounds height. This clamping michael@0: // is done to get correct results for a page where the page is sized to michael@0: // the screen and thus the dynamic toolbar never disappears. In such a michael@0: // case, we want the composition bounds to exclude the toolbar height, michael@0: // but the widget bounds includes it. We don't currently have a good way michael@0: // of knowing about the toolbar height, but clamping to the view bounds michael@0: // height gives the correct answer in the cases we care about. michael@0: nsIWidget* widget = michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: rootFrame->GetNearestWidget(); michael@0: #else michael@0: view->GetWidget(); michael@0: #endif michael@0: if (widget) { michael@0: nsIntRect widgetBounds; michael@0: widget->GetBounds(widgetBounds); michael@0: metrics.mCompositionBounds = ViewAs(widgetBounds); michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: if (viewBounds.height < metrics.mCompositionBounds.height) { michael@0: metrics.mCompositionBounds.height = viewBounds.height; michael@0: } michael@0: #endif michael@0: } else { michael@0: metrics.mCompositionBounds = viewBounds; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Adjust composition bounds for the size of scroll bars. michael@0: if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { michael@0: nsMargin sizes = scrollableFrame->GetActualScrollbarSizes(); michael@0: // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them. michael@0: ParentLayerIntMargin boundMargins = RoundedToInt(CSSMargin::FromAppUnits(sizes) * CSSToParentLayerScale(1.0f)); michael@0: metrics.mCompositionBounds.Deflate(boundMargins); michael@0: } michael@0: michael@0: metrics.SetRootCompositionSize( michael@0: nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame, michael@0: isRootContentDocRootScrollFrame, metrics)); michael@0: michael@0: if (GetApzcTreePrintPref()) { michael@0: if (nsIContent* content = frameForCompositionBoundsCalculation->GetContent()) { michael@0: nsAutoString contentDescription; michael@0: content->Describe(contentDescription); michael@0: metrics.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription).get()); michael@0: } michael@0: } michael@0: michael@0: metrics.mPresShellId = presShell->GetPresShellId(); michael@0: michael@0: // If the scroll frame's content is marked 'scrollgrab', record this michael@0: // in the FrameMetrics so APZ knows to provide the scroll grabbing michael@0: // behaviour. michael@0: if (aScrollFrame && nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())) { michael@0: metrics.mHasScrollgrab = true; michael@0: } michael@0: michael@0: aRoot->SetFrameMetrics(metrics); michael@0: } michael@0: michael@0: nsDisplayListBuilder::~nsDisplayListBuilder() { michael@0: NS_ASSERTION(mFramesMarkedForDisplay.Length() == 0, michael@0: "All frames should have been unmarked"); michael@0: NS_ASSERTION(mPresShellStates.Length() == 0, michael@0: "All presshells should have been exited"); michael@0: NS_ASSERTION(!mCurrentTableItem, "No table item should be active"); michael@0: michael@0: nsCSSRendering::EndFrameTreesLocked(); michael@0: michael@0: for (uint32_t i = 0; i < mDisplayItemClipsToDestroy.Length(); ++i) { michael@0: mDisplayItemClipsToDestroy[i]->DisplayItemClip::~DisplayItemClip(); michael@0: } michael@0: michael@0: PL_FinishArenaPool(&mPool); michael@0: MOZ_COUNT_DTOR(nsDisplayListBuilder); michael@0: } michael@0: michael@0: uint32_t michael@0: nsDisplayListBuilder::GetBackgroundPaintFlags() { michael@0: uint32_t flags = 0; michael@0: if (mSyncDecodeImages) { michael@0: flags |= nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES; michael@0: } michael@0: if (mIsPaintingToWindow) { michael@0: flags |= nsCSSRendering::PAINTBG_TO_WINDOW; michael@0: } michael@0: return flags; michael@0: } michael@0: michael@0: void michael@0: nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion, michael@0: const nsRegion& aRegion) michael@0: { michael@0: if (aRegion.IsEmpty()) michael@0: return; michael@0: michael@0: nsRegion tmp; michael@0: tmp.Sub(*aVisibleRegion, aRegion); michael@0: // Don't let *aVisibleRegion get too complex, but don't let it fluff out michael@0: // to its bounds either, which can be very bad (see bug 516740). michael@0: // Do let aVisibleRegion get more complex if by doing so we reduce its michael@0: // area by at least half. michael@0: if (GetAccurateVisibleRegions() || tmp.GetNumRects() <= 15 || michael@0: tmp.Area() <= aVisibleRegion->Area()/2) { michael@0: *aVisibleRegion = tmp; michael@0: } michael@0: } michael@0: michael@0: nsCaret * michael@0: nsDisplayListBuilder::GetCaret() { michael@0: nsRefPtr caret = CurrentPresShellState()->mPresShell->GetCaret(); michael@0: return caret; michael@0: } michael@0: michael@0: void michael@0: nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame, michael@0: const nsRect& aDirtyRect) { michael@0: PresShellState* state = mPresShellStates.AppendElement(); michael@0: if (!state) michael@0: return; michael@0: state->mPresShell = aReferenceFrame->PresContext()->PresShell(); michael@0: state->mCaretFrame = nullptr; michael@0: state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length(); michael@0: michael@0: state->mPresShell->UpdateCanvasBackground(); michael@0: michael@0: if (mIsPaintingToWindow) { michael@0: mReferenceFrame->AddPaintedPresShell(state->mPresShell); michael@0: michael@0: state->mPresShell->IncrementPaintCount(); michael@0: } michael@0: michael@0: bool buildCaret = mBuildCaret; michael@0: if (mIgnoreSuppression || !state->mPresShell->IsPaintingSuppressed()) { michael@0: if (state->mPresShell->IsPaintingSuppressed()) { michael@0: mHadToIgnoreSuppression = true; michael@0: } michael@0: state->mIsBackgroundOnly = false; michael@0: } else { michael@0: state->mIsBackgroundOnly = true; michael@0: buildCaret = false; michael@0: } michael@0: michael@0: if (!buildCaret) michael@0: return; michael@0: michael@0: nsRefPtr caret = state->mPresShell->GetCaret(); michael@0: state->mCaretFrame = caret->GetCaretFrame(); michael@0: NS_ASSERTION(state->mCaretFrame == caret->GetCaretFrame(), michael@0: "GetCaretFrame() is unstable"); michael@0: michael@0: if (state->mCaretFrame) { michael@0: // Check if the dirty rect intersects with the caret's dirty rect. michael@0: nsRect caretRect = michael@0: caret->GetCaretRect() + state->mCaretFrame->GetOffsetTo(aReferenceFrame); michael@0: if (caretRect.Intersects(aDirtyRect)) { michael@0: // Okay, our rects intersect, let's mark the frame and all of its ancestors. michael@0: mFramesMarkedForDisplay.AppendElement(state->mCaretFrame); michael@0: MarkFrameForDisplay(state->mCaretFrame, nullptr); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame, michael@0: const nsRect& aDirtyRect) { michael@0: if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) { michael@0: // Must have not allocated a state for this presshell, presumably due michael@0: // to OOM. michael@0: return; michael@0: } michael@0: michael@0: ResetMarkedFramesForDisplayList(); michael@0: mPresShellStates.SetLength(mPresShellStates.Length() - 1); michael@0: } michael@0: michael@0: void michael@0: nsDisplayListBuilder::ResetMarkedFramesForDisplayList() michael@0: { michael@0: // Unmark and pop off the frames marked for display in this pres shell. michael@0: uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay; michael@0: for (uint32_t i = firstFrameForShell; michael@0: i < mFramesMarkedForDisplay.Length(); ++i) { michael@0: UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]); michael@0: } michael@0: mFramesMarkedForDisplay.SetLength(firstFrameForShell); michael@0: } michael@0: michael@0: void michael@0: nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, michael@0: const nsFrameList& aFrames, michael@0: const nsRect& aDirtyRect) { michael@0: mFramesMarkedForDisplay.SetCapacity(mFramesMarkedForDisplay.Length() + aFrames.GetLength()); michael@0: for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) { michael@0: mFramesMarkedForDisplay.AppendElement(e.get()); michael@0: MarkOutOfFlowFrameForDisplay(aDirtyFrame, e.get(), aDirtyRect); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect) michael@0: { michael@0: nsAutoTArray childListArray; michael@0: aDirtyFrame->GetChildLists(&childListArray); michael@0: nsIFrame::ChildListArrayIterator lists(childListArray); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame *child = childFrames.get(); michael@0: if (child->Preserves3D()) { michael@0: mFramesMarkedForDisplay.AppendElement(child); michael@0: nsRect dirty = aDirtyRect - child->GetOffsetTo(aDirtyFrame); michael@0: michael@0: child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(), michael@0: new nsRect(dirty)); michael@0: michael@0: MarkFrameForDisplay(child, aDirtyFrame); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void* michael@0: nsDisplayListBuilder::Allocate(size_t aSize) { michael@0: void *tmp; michael@0: PL_ARENA_ALLOCATE(tmp, &mPool, aSize); michael@0: if (!tmp) { michael@0: NS_RUNTIMEABORT("out of memory"); michael@0: } michael@0: return tmp; michael@0: } michael@0: michael@0: const DisplayItemClip* michael@0: nsDisplayListBuilder::AllocateDisplayItemClip(const DisplayItemClip& aOriginal) michael@0: { michael@0: void* p = Allocate(sizeof(DisplayItemClip)); michael@0: if (!aOriginal.GetRoundedRectCount()) { michael@0: memcpy(p, &aOriginal, sizeof(DisplayItemClip)); michael@0: return static_cast(p); michael@0: } michael@0: michael@0: DisplayItemClip* c = new (p) DisplayItemClip(aOriginal); michael@0: mDisplayItemClipsToDestroy.AppendElement(c); michael@0: return c; michael@0: } michael@0: michael@0: void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const michael@0: { michael@0: aDestination.BorderBackground()->AppendToTop(BorderBackground()); michael@0: aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds()); michael@0: aDestination.Floats()->AppendToTop(Floats()); michael@0: aDestination.Content()->AppendToTop(Content()); michael@0: aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants()); michael@0: aDestination.Outlines()->AppendToTop(Outlines()); michael@0: } michael@0: michael@0: void michael@0: nsDisplayList::FlattenTo(nsTArray* aElements) { michael@0: nsDisplayItem* item; michael@0: while ((item = RemoveBottom()) != nullptr) { michael@0: if (item->GetType() == nsDisplayItem::TYPE_WRAP_LIST) { michael@0: item->GetSameCoordinateSystemChildren()->FlattenTo(aElements); michael@0: item->~nsDisplayItem(); michael@0: } else { michael@0: aElements->AppendElement(item); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const { michael@0: nsRect bounds; michael@0: for (nsDisplayItem* i = GetBottom(); i != nullptr; i = i->GetAbove()) { michael@0: bounds.UnionRect(bounds, i->GetClippedBounds(aBuilder)); michael@0: } michael@0: return bounds; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: nsIFrame* aDisplayPortFrame) { michael@0: PROFILER_LABEL("nsDisplayList", "ComputeVisibilityForRoot"); michael@0: nsRegion r; michael@0: r.And(*aVisibleRegion, GetBounds(aBuilder)); michael@0: return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, michael@0: r.GetBounds(), r.GetBounds(), michael@0: aDisplayPortFrame); michael@0: } michael@0: michael@0: static nsRegion michael@0: TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) michael@0: { michael@0: bool snap; michael@0: nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap); michael@0: if (aBuilder->IsForPluginGeometry()) { michael@0: // Treat all leaf chrome items as opaque, unless their frames are opacity:0. michael@0: // Since opacity:0 frames generate an nsDisplayOpacity, that item will michael@0: // not be treated as opaque here, so opacity:0 chrome content will be michael@0: // effectively ignored, as it should be. michael@0: // We treat leaf chrome items as opaque to ensure that they cover michael@0: // content plugins, for security reasons. michael@0: // Non-leaf chrome items don't render contents of their own so shouldn't michael@0: // be treated as opaque (and their bounds is just the union of their michael@0: // children, which might be a large area their contents don't really cover). michael@0: nsIFrame* f = aItem->Frame(); michael@0: if (f->PresContext()->IsChrome() && !aItem->GetChildren() && michael@0: f->StyleDisplay()->mOpacity != 0.0) { michael@0: opaque = aItem->GetBounds(aBuilder, &snap); michael@0: } michael@0: } michael@0: if (opaque.IsEmpty()) { michael@0: return opaque; michael@0: } michael@0: nsRegion opaqueClipped; michael@0: nsRegionRectIterator iter(opaque); michael@0: for (const nsRect* r = iter.Next(); r; r = iter.Next()) { michael@0: opaqueClipped.Or(opaqueClipped, aItem->GetClip().ApproximateIntersectInward(*r)); michael@0: } michael@0: return opaqueClipped; michael@0: } michael@0: michael@0: /* Checks if aPotentialScrollItem is a scroll layer item and aPotentialScrollbarItem michael@0: * is an overlay scrollbar item for the same scroll frame. michael@0: */ michael@0: static bool michael@0: IsScrollLayerItemAndOverlayScrollbarForScrollFrame( michael@0: nsDisplayItem* aPotentialScrollItem, nsDisplayItem* aPotentialScrollbarItem) michael@0: { michael@0: if (aPotentialScrollItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER && michael@0: aPotentialScrollbarItem && michael@0: aPotentialScrollbarItem->GetType() == nsDisplayItem::TYPE_OWN_LAYER && michael@0: LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { michael@0: nsDisplayScrollLayer* scrollItem = michael@0: static_cast(aPotentialScrollItem); michael@0: nsDisplayOwnLayer* layerItem = michael@0: static_cast(aPotentialScrollbarItem); michael@0: if ((layerItem->GetFlags() & michael@0: (nsDisplayOwnLayer::VERTICAL_SCROLLBAR | michael@0: nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR)) && michael@0: layerItem->Frame()->GetParent() == scrollItem->GetScrollFrame()) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aListVisibleBounds, michael@0: const nsRect& aAllowVisibleRegionExpansion, michael@0: nsIFrame* aDisplayPortFrame) { michael@0: #ifdef DEBUG michael@0: nsRegion r; michael@0: r.And(*aVisibleRegion, GetBounds(aBuilder)); michael@0: NS_ASSERTION(r.GetBounds().IsEqualInterior(aListVisibleBounds), michael@0: "bad aListVisibleBounds"); michael@0: #endif michael@0: michael@0: mVisibleRect = aListVisibleBounds; michael@0: bool anyVisible = false; michael@0: michael@0: nsAutoTArray elements; michael@0: FlattenTo(&elements); michael@0: michael@0: bool forceTransparentSurface = false; michael@0: michael@0: for (int32_t i = elements.Length() - 1; i >= 0; --i) { michael@0: nsDisplayItem* item = elements[i]; michael@0: nsDisplayItem* belowItem = i < 1 ? nullptr : elements[i - 1]; michael@0: michael@0: nsDisplayList* list = item->GetSameCoordinateSystemChildren(); michael@0: if (aBuilder->AllowMergingAndFlattening()) { michael@0: if (belowItem && item->TryMerge(aBuilder, belowItem)) { michael@0: belowItem->~nsDisplayItem(); michael@0: elements.ReplaceElementsAt(i - 1, 1, item); michael@0: continue; michael@0: } michael@0: michael@0: // If an overlay scrollbar item is between a scroll layer item and the michael@0: // other scroll layer items that we need to merge with just move the michael@0: // scrollbar item up, that way it will be on top of the scrolled content michael@0: // and we can try to merge all the scroll layer items. michael@0: if (IsScrollLayerItemAndOverlayScrollbarForScrollFrame(item, belowItem)) { michael@0: elements[i] = belowItem; michael@0: elements[i-1] = item; michael@0: i++; michael@0: continue; michael@0: } michael@0: michael@0: if (list && item->ShouldFlattenAway(aBuilder)) { michael@0: // The elements on the list >= i no longer serve any use. michael@0: elements.SetLength(i); michael@0: list->FlattenTo(&elements); michael@0: i = elements.Length(); michael@0: item->~nsDisplayItem(); michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: nsRect bounds = item->GetClippedBounds(aBuilder); michael@0: michael@0: nsRegion itemVisible; michael@0: itemVisible.And(*aVisibleRegion, bounds); michael@0: item->mVisibleRect = itemVisible.GetBounds(); michael@0: michael@0: if (item->ComputeVisibility(aBuilder, aVisibleRegion, michael@0: aAllowVisibleRegionExpansion.Intersect(bounds))) { michael@0: anyVisible = true; michael@0: michael@0: // If we're in a displayport, we need to make sure that fixed position michael@0: // items do not subtract from the visible region, as async scrolling michael@0: // may expose these occluded areas. michael@0: // If the item is fixed pos in the same document as the displayport michael@0: // then don't let it occlude this list. The only other case possible michael@0: // is that the fixed pos content is in a child document, in which it michael@0: // would scroll with the rest of the content. michael@0: bool occlude = true; michael@0: if (aDisplayPortFrame && item->IsInFixedPos()) { michael@0: if (item->Frame()->PresContext() == aDisplayPortFrame->PresContext()) { michael@0: occlude = false; michael@0: } michael@0: } michael@0: michael@0: if (occlude) { michael@0: nsRegion opaque = TreatAsOpaque(item, aBuilder); michael@0: // Subtract opaque item from the visible region michael@0: aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); michael@0: } michael@0: michael@0: if (aBuilder->NeedToForceTransparentSurfaceForItem(item) || michael@0: (list && list->NeedsTransparentSurface())) { michael@0: forceTransparentSurface = true; michael@0: } michael@0: } michael@0: AppendToBottom(item); michael@0: } michael@0: michael@0: mIsOpaque = !aVisibleRegion->Intersects(mVisibleRect); michael@0: mForceTransparentSurface = forceTransparentSurface; michael@0: #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING) michael@0: mDidComputeVisibility = true; michael@0: #endif michael@0: return anyVisible; michael@0: } michael@0: michael@0: void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx, michael@0: uint32_t aFlags) const { michael@0: PROFILER_LABEL("nsDisplayList", "PaintRoot"); michael@0: PaintForFrame(aBuilder, aCtx, aBuilder->RootReferenceFrame(), aFlags); michael@0: } michael@0: michael@0: /** michael@0: * We paint by executing a layer manager transaction, constructing a michael@0: * single layer representing the display list, and then making it the michael@0: * root of the layer manager, drawing into the ThebesLayers. michael@0: */ michael@0: void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx, michael@0: nsIFrame* aForFrame, michael@0: uint32_t aFlags) const { michael@0: NS_ASSERTION(mDidComputeVisibility, michael@0: "Must call ComputeVisibility before calling Paint"); michael@0: michael@0: nsRefPtr layerManager; michael@0: bool widgetTransaction = false; michael@0: bool allowRetaining = false; michael@0: bool doBeginTransaction = true; michael@0: nsView *view = nullptr; michael@0: if (aFlags & PAINT_USE_WIDGET_LAYERS) { michael@0: nsIFrame* rootReferenceFrame = aBuilder->RootReferenceFrame(); michael@0: view = rootReferenceFrame->GetView(); michael@0: NS_ASSERTION(rootReferenceFrame == nsLayoutUtils::GetDisplayRootFrame(rootReferenceFrame), michael@0: "Reference frame must be a display root for us to use the layer manager"); michael@0: nsIWidget* window = rootReferenceFrame->GetNearestWidget(); michael@0: if (window) { michael@0: layerManager = window->GetLayerManager(&allowRetaining); michael@0: if (layerManager) { michael@0: doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION); michael@0: widgetTransaction = true; michael@0: } michael@0: } michael@0: } michael@0: if (!layerManager) { michael@0: if (!aCtx) { michael@0: NS_WARNING("Nowhere to paint into"); michael@0: return; michael@0: } michael@0: layerManager = new BasicLayerManager(); michael@0: } michael@0: michael@0: // Store the existing layer builder to reinstate it on return. michael@0: FrameLayerBuilder *oldBuilder = layerManager->GetLayerBuilder(); michael@0: michael@0: FrameLayerBuilder *layerBuilder = new FrameLayerBuilder(); michael@0: layerBuilder->Init(aBuilder, layerManager); michael@0: michael@0: if (aFlags & PAINT_COMPRESSED) { michael@0: layerBuilder->SetLayerTreeCompressionMode(); michael@0: } michael@0: michael@0: if (aFlags & PAINT_FLUSH_LAYERS) { michael@0: FrameLayerBuilder::InvalidateAllLayers(layerManager); michael@0: } michael@0: michael@0: if (doBeginTransaction) { michael@0: if (aCtx) { michael@0: layerManager->BeginTransactionWithTarget(aCtx->ThebesContext()); michael@0: } else { michael@0: layerManager->BeginTransaction(); michael@0: } michael@0: } michael@0: if (widgetTransaction) { michael@0: layerBuilder->DidBeginRetainedLayerTransaction(layerManager); michael@0: } michael@0: michael@0: nsPresContext* presContext = aForFrame->PresContext(); michael@0: nsIPresShell* presShell = presContext->GetPresShell(); michael@0: michael@0: NotifySubDocInvalidationFunc computeInvalidFunc = michael@0: presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0; michael@0: bool computeInvalidRect = (computeInvalidFunc || michael@0: (!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) && michael@0: widgetTransaction; michael@0: michael@0: nsAutoPtr props(computeInvalidRect ? michael@0: LayerProperties::CloneFrom(layerManager->GetRoot()) : michael@0: nullptr); michael@0: michael@0: ContainerLayerParameters containerParameters michael@0: (presShell->GetXResolution(), presShell->GetYResolution()); michael@0: nsRefPtr root = layerBuilder-> michael@0: BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, *this, michael@0: containerParameters, nullptr); michael@0: michael@0: nsIDocument* document = nullptr; michael@0: if (presShell) { michael@0: document = presShell->GetDocument(); michael@0: } michael@0: michael@0: if (widgetTransaction || michael@0: // SVG-as-an-image docs don't paint as part of the retained layer tree, michael@0: // but they still need the invalidation state bits cleared in order for michael@0: // invalidation for CSS/SMIL animation to work properly. michael@0: (document && document->IsBeingUsedAsImage())) { michael@0: aForFrame->ClearInvalidationStateBits(); michael@0: } michael@0: michael@0: if (!root) { michael@0: layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder); michael@0: return; michael@0: } michael@0: // Root is being scaled up by the X/Y resolution. Scale it back down. michael@0: root->SetPostScale(1.0f/containerParameters.mXScale, michael@0: 1.0f/containerParameters.mYScale); michael@0: michael@0: ViewID id = FrameMetrics::NULL_SCROLL_ID; michael@0: bool isRoot = presContext->IsRootContentDocument(); michael@0: michael@0: nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); michael@0: nsRect displayport, criticalDisplayport; michael@0: bool usingDisplayport = false; michael@0: bool usingCriticalDisplayport = false; michael@0: if (rootScrollFrame) { michael@0: nsIContent* content = rootScrollFrame->GetContent(); michael@0: if (content) { michael@0: usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport); michael@0: usingCriticalDisplayport = michael@0: nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport); michael@0: michael@0: // If this is the root content document, we want it to have a scroll id. michael@0: if (isRoot) { michael@0: id = nsLayoutUtils::FindOrCreateIDFor(content); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize()); michael@0: michael@0: RecordFrameMetrics(aForFrame, rootScrollFrame, michael@0: aBuilder->FindReferenceFrameFor(aForFrame), michael@0: root, mVisibleRect, viewport, michael@0: (usingDisplayport ? &displayport : nullptr), michael@0: (usingCriticalDisplayport ? &criticalDisplayport : nullptr), michael@0: id, isRoot, containerParameters); michael@0: if (usingDisplayport && michael@0: !(root->GetContentFlags() & Layer::CONTENT_OPAQUE)) { michael@0: // See bug 693938, attachment 567017 michael@0: NS_WARNING("Transparent content with displayports can be expensive."); michael@0: } michael@0: michael@0: layerManager->SetRoot(root); michael@0: layerBuilder->WillEndTransaction(); michael@0: bool temp = aBuilder->SetIsCompositingCheap(layerManager->IsCompositingCheap()); michael@0: LayerManager::EndTransactionFlags flags = LayerManager::END_DEFAULT; michael@0: if (layerManager->NeedsWidgetInvalidation()) { michael@0: if (aFlags & PAINT_NO_COMPOSITE) { michael@0: flags = LayerManager::END_NO_COMPOSITE; michael@0: } michael@0: } else { michael@0: // Client layer managers never composite directly, so michael@0: // we don't need to worry about END_NO_COMPOSITE. michael@0: if (aBuilder->WillComputePluginGeometry()) { michael@0: flags = LayerManager::END_NO_REMOTE_COMPOSITE; michael@0: } michael@0: } michael@0: michael@0: layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, michael@0: aBuilder, flags); michael@0: aBuilder->SetIsCompositingCheap(temp); michael@0: layerBuilder->DidEndTransaction(); michael@0: michael@0: nsIntRegion invalid; michael@0: if (props) { michael@0: invalid = props->ComputeDifferences(root, computeInvalidFunc); michael@0: } else if (widgetTransaction) { michael@0: LayerProperties::ClearInvalidations(root); michael@0: } michael@0: michael@0: bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); michael@0: if (view) { michael@0: if (props) { michael@0: if (!invalid.IsEmpty()) { michael@0: nsIntRect bounds = invalid.GetBounds(); michael@0: nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), michael@0: presContext->DevPixelsToAppUnits(bounds.y), michael@0: presContext->DevPixelsToAppUnits(bounds.width), michael@0: presContext->DevPixelsToAppUnits(bounds.height)); michael@0: if (shouldInvalidate) { michael@0: view->GetViewManager()->InvalidateViewNoSuppression(view, rect); michael@0: } michael@0: presContext->NotifyInvalidation(bounds, 0); michael@0: } michael@0: } else if (shouldInvalidate) { michael@0: view->GetViewManager()->InvalidateView(view); michael@0: } michael@0: } michael@0: michael@0: if (aFlags & PAINT_FLUSH_LAYERS) { michael@0: FrameLayerBuilder::InvalidateAllLayers(layerManager); michael@0: } michael@0: michael@0: layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder); michael@0: } michael@0: michael@0: uint32_t nsDisplayList::Count() const { michael@0: uint32_t count = 0; michael@0: for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) { michael@0: ++count; michael@0: } michael@0: return count; michael@0: } michael@0: michael@0: nsDisplayItem* nsDisplayList::RemoveBottom() { michael@0: nsDisplayItem* item = mSentinel.mAbove; michael@0: if (!item) michael@0: return nullptr; michael@0: mSentinel.mAbove = item->mAbove; michael@0: if (item == mTop) { michael@0: // must have been the only item michael@0: mTop = &mSentinel; michael@0: } michael@0: item->mAbove = nullptr; michael@0: return item; michael@0: } michael@0: michael@0: void nsDisplayList::DeleteAll() { michael@0: nsDisplayItem* item; michael@0: while ((item = RemoveBottom()) != nullptr) { michael@0: item->~nsDisplayItem(); michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: GetMouseThrough(const nsIFrame* aFrame) michael@0: { michael@0: if (!aFrame->IsBoxFrame()) michael@0: return false; michael@0: michael@0: const nsIFrame* frame = aFrame; michael@0: while (frame) { michael@0: if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_ALWAYS) { michael@0: return true; michael@0: } else if (frame->GetStateBits() & NS_FRAME_MOUSE_THROUGH_NEVER) { michael@0: return false; michael@0: } michael@0: frame = frame->GetParentBox(); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: IsFrameReceivingPointerEvents(nsIFrame* aFrame) michael@0: { michael@0: nsSubDocumentFrame* frame = do_QueryFrame(aFrame); michael@0: if (frame && frame->PassPointerEventsToChildren()) { michael@0: return true; michael@0: } michael@0: return NS_STYLE_POINTER_EVENTS_NONE != michael@0: aFrame->StyleVisibility()->GetEffectivePointerEvents(aFrame); michael@0: } michael@0: michael@0: // A list of frames, and their z depth. Used for sorting michael@0: // the results of hit testing. michael@0: struct FramesWithDepth michael@0: { michael@0: FramesWithDepth(float aDepth) : michael@0: mDepth(aDepth) michael@0: {} michael@0: michael@0: bool operator<(const FramesWithDepth& aOther) const { michael@0: if (mDepth != aOther.mDepth) { michael@0: // We want to sort so that the shallowest item (highest depth value) is first michael@0: return mDepth > aOther.mDepth; michael@0: } michael@0: return this < &aOther; michael@0: } michael@0: bool operator==(const FramesWithDepth& aOther) const { michael@0: return this == &aOther; michael@0: } michael@0: michael@0: float mDepth; michael@0: nsTArray mFrames; michael@0: }; michael@0: michael@0: // Sort the frames by depth and then moves all the contained frames to the destination michael@0: void FlushFramesArray(nsTArray& aSource, nsTArray* aDest) michael@0: { michael@0: if (aSource.IsEmpty()) { michael@0: return; michael@0: } michael@0: aSource.Sort(); michael@0: uint32_t length = aSource.Length(); michael@0: for (uint32_t i = 0; i < length; i++) { michael@0: aDest->MoveElementsFrom(aSource[i].mFrames); michael@0: } michael@0: aSource.Clear(); michael@0: } michael@0: michael@0: void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, michael@0: nsDisplayItem::HitTestState* aState, michael@0: nsTArray *aOutFrames) const { michael@0: int32_t itemBufferStart = aState->mItemBuffer.Length(); michael@0: nsDisplayItem* item; michael@0: for (item = GetBottom(); item; item = item->GetAbove()) { michael@0: aState->mItemBuffer.AppendElement(item); michael@0: } michael@0: nsAutoTArray temp; michael@0: for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) { michael@0: // Pop element off the end of the buffer. We want to shorten the buffer michael@0: // so that recursive calls to HitTest have more buffer space. michael@0: item = aState->mItemBuffer[i]; michael@0: aState->mItemBuffer.SetLength(i); michael@0: michael@0: bool snap; michael@0: nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect); michael@0: if (item->GetClip().MayIntersect(r)) { michael@0: nsAutoTArray outFrames; michael@0: item->HitTest(aBuilder, aRect, aState, &outFrames); michael@0: michael@0: // For 3d transforms with preserve-3d we add hit frames into the temp list michael@0: // so we can sort them later, otherwise we add them directly to the output list. michael@0: nsTArray *writeFrames = aOutFrames; michael@0: if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM && michael@0: item->Frame()->Preserves3D()) { michael@0: if (outFrames.Length()) { michael@0: nsDisplayTransform *transform = static_cast(item); michael@0: nsPoint point = aRect.TopLeft(); michael@0: // A 1x1 rect means a point, otherwise use the center of the rect michael@0: if (aRect.width != 1 || aRect.height != 1) { michael@0: point = aRect.Center(); michael@0: } michael@0: temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point))); michael@0: writeFrames = &temp[temp.Length() - 1].mFrames; michael@0: } michael@0: } else { michael@0: // We may have just finished a run of consecutive preserve-3d transforms, michael@0: // so flush these into the destination array before processing our frame list. michael@0: FlushFramesArray(temp, aOutFrames); michael@0: } michael@0: michael@0: for (uint32_t j = 0; j < outFrames.Length(); j++) { michael@0: nsIFrame *f = outFrames.ElementAt(j); michael@0: // Handle the XUL 'mousethrough' feature and 'pointer-events'. michael@0: if (!GetMouseThrough(f) && IsFrameReceivingPointerEvents(f)) { michael@0: writeFrames->AppendElement(f); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: // Clear any remaining preserve-3d transforms. michael@0: FlushFramesArray(temp, aOutFrames); michael@0: NS_ASSERTION(aState->mItemBuffer.Length() == uint32_t(itemBufferStart), michael@0: "How did we forget to pop some elements?"); michael@0: } michael@0: michael@0: static void Sort(nsDisplayList* aList, int32_t aCount, nsDisplayList::SortLEQ aCmp, michael@0: void* aClosure) { michael@0: if (aCount < 2) michael@0: return; michael@0: michael@0: nsDisplayList list1; michael@0: nsDisplayList list2; michael@0: int i; michael@0: int32_t half = aCount/2; michael@0: bool sorted = true; michael@0: nsDisplayItem* prev = nullptr; michael@0: for (i = 0; i < aCount; ++i) { michael@0: nsDisplayItem* item = aList->RemoveBottom(); michael@0: (i < half ? &list1 : &list2)->AppendToTop(item); michael@0: if (sorted && prev && !aCmp(prev, item, aClosure)) { michael@0: sorted = false; michael@0: } michael@0: prev = item; michael@0: } michael@0: if (sorted) { michael@0: aList->AppendToTop(&list1); michael@0: aList->AppendToTop(&list2); michael@0: return; michael@0: } michael@0: michael@0: Sort(&list1, half, aCmp, aClosure); michael@0: Sort(&list2, aCount - half, aCmp, aClosure); michael@0: michael@0: for (i = 0; i < aCount; ++i) { michael@0: if (list1.GetBottom() && michael@0: (!list2.GetBottom() || michael@0: aCmp(list1.GetBottom(), list2.GetBottom(), aClosure))) { michael@0: aList->AppendToTop(list1.RemoveBottom()); michael@0: } else { michael@0: aList->AppendToTop(list2.RemoveBottom()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static nsIContent* FindContentInDocument(nsDisplayItem* aItem, nsIDocument* aDoc) { michael@0: nsIFrame* f = aItem->Frame(); michael@0: while (f) { michael@0: nsPresContext* pc = f->PresContext(); michael@0: if (pc->Document() == aDoc) { michael@0: return f->GetContent(); michael@0: } michael@0: f = nsLayoutUtils::GetCrossDocParentFrame(pc->PresShell()->GetRootFrame()); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: static bool IsContentLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, michael@0: void* aClosure) { michael@0: nsIContent* commonAncestor = static_cast(aClosure); michael@0: // It's possible that the nsIContent for aItem1 or aItem2 is in a subdocument michael@0: // of commonAncestor, because display items for subdocuments have been michael@0: // mixed into the same list. Ensure that we're looking at content michael@0: // in commonAncestor's document. michael@0: nsIDocument* commonAncestorDoc = commonAncestor->OwnerDoc(); michael@0: nsIContent* content1 = FindContentInDocument(aItem1, commonAncestorDoc); michael@0: nsIContent* content2 = FindContentInDocument(aItem2, commonAncestorDoc); michael@0: if (!content1 || !content2) { michael@0: NS_ERROR("Document trees are mixed up!"); michael@0: // Something weird going on michael@0: return true; michael@0: } michael@0: return nsLayoutUtils::CompareTreePosition(content1, content2, commonAncestor) <= 0; michael@0: } michael@0: michael@0: static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, michael@0: void* aClosure) { michael@0: // Note that we can't just take the difference of the two michael@0: // z-indices here, because that might overflow a 32-bit int. michael@0: return aItem1->ZIndex() <= aItem2->ZIndex(); michael@0: } michael@0: michael@0: void nsDisplayList::SortByZOrder(nsDisplayListBuilder* aBuilder, michael@0: nsIContent* aCommonAncestor) { michael@0: Sort(aBuilder, IsZOrderLEQ, aCommonAncestor); michael@0: } michael@0: michael@0: void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder, michael@0: nsIContent* aCommonAncestor) { michael@0: Sort(aBuilder, IsContentLEQ, aCommonAncestor); michael@0: } michael@0: michael@0: void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder, michael@0: SortLEQ aCmp, void* aClosure) { michael@0: ::Sort(this, Count(), aCmp, aClosure); michael@0: } michael@0: michael@0: void michael@0: nsDisplayItem::AddInvalidRegionForSyncDecodeBackgroundImages( michael@0: nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion* aInvalidRegion) michael@0: { michael@0: if (aBuilder->ShouldSyncDecodeImages()) { michael@0: if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) { michael@0: bool snap; michael@0: aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* static */ bool michael@0: nsDisplayItem::ForceActiveLayers() michael@0: { michael@0: static bool sForce = false; michael@0: static bool sForceCached = false; michael@0: michael@0: if (!sForceCached) { michael@0: Preferences::AddBoolVarCache(&sForce, "layers.force-active", false); michael@0: sForceCached = true; michael@0: } michael@0: michael@0: return sForce; michael@0: } michael@0: michael@0: /* static */ int32_t michael@0: nsDisplayItem::MaxActiveLayers() michael@0: { michael@0: static int32_t sMaxLayers = false; michael@0: static bool sMaxLayersCached = false; michael@0: michael@0: if (!sMaxLayersCached) { michael@0: Preferences::AddIntVarCache(&sMaxLayers, "layers.max-active", -1); michael@0: sMaxLayersCached = true; michael@0: } michael@0: michael@0: return sMaxLayers; michael@0: } michael@0: michael@0: int32_t michael@0: nsDisplayItem::ZIndex() const michael@0: { michael@0: if (!mFrame->IsPositioned() && !mFrame->IsFlexItem()) michael@0: return 0; michael@0: michael@0: const nsStylePosition* position = mFrame->StylePosition(); michael@0: if (position->mZIndex.GetUnit() == eStyleUnit_Integer) michael@0: return position->mZIndex.GetIntValue(); michael@0: michael@0: // sort the auto and 0 elements together michael@0: return 0; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion) { michael@0: nsRect bounds = GetClippedBounds(aBuilder); michael@0: michael@0: nsRegion itemVisible; michael@0: itemVisible.And(*aVisibleRegion, bounds); michael@0: mVisibleRect = itemVisible.GetBounds(); michael@0: michael@0: // When we recompute visibility within layers we don't need to michael@0: // expand the visible region for content behind plugins (the plugin michael@0: // is not in the layer). michael@0: if (!ComputeVisibility(aBuilder, aVisibleRegion, nsRect())) michael@0: return false; michael@0: michael@0: nsRegion opaque = TreatAsOpaque(this, aBuilder); michael@0: aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); michael@0: return true; michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayItem::GetClippedBounds(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: bool snap; michael@0: nsRect r = GetBounds(aBuilder, &snap); michael@0: return GetClip().ApplyNonRoundedIntersection(r); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplaySolidColor::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) michael@0: { michael@0: *aSnap = true; michael@0: return mBounds; michael@0: } michael@0: michael@0: void michael@0: nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: aCtx->SetColor(mColor); michael@0: aCtx->FillRect(mVisibleRect); michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: nsDisplaySolidColor::WriteDebugInfo(nsACString& aTo) michael@0: { michael@0: aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)", michael@0: NS_GET_R(mColor), NS_GET_G(mColor), michael@0: NS_GET_B(mColor), NS_GET_A(mColor)); michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame); michael@0: michael@0: for (nsIFrame* f = aFrame; f; f = f->GetParent()) { michael@0: // Bail out if we're in a transformed subtree michael@0: if (f->IsTransformed()) michael@0: return; michael@0: // Bail out if we're not in the displayRoot's document michael@0: if (!f->GetParent() && f != displayRoot) michael@0: return; michael@0: } michael@0: michael@0: nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize()); michael@0: aBuilder->RegisterThemeGeometry(aFrame->StyleDisplay()->mAppearance, michael@0: borderBox.ToNearestPixels(aFrame->PresContext()->AppUnitsPerDevPixel())); michael@0: } michael@0: michael@0: nsDisplayBackgroundImage::nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, michael@0: uint32_t aLayer, michael@0: const nsStyleBackground* aBackgroundStyle) michael@0: : nsDisplayImageContainer(aBuilder, aFrame) michael@0: , mBackgroundStyle(aBackgroundStyle) michael@0: , mLayer(aLayer) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayBackgroundImage); michael@0: michael@0: mBounds = GetBoundsInternal(aBuilder); michael@0: } michael@0: michael@0: nsDisplayBackgroundImage::~nsDisplayBackgroundImage() michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: MOZ_COUNT_DTOR(nsDisplayBackgroundImage); michael@0: #endif michael@0: } michael@0: michael@0: static nsStyleContext* GetBackgroundStyleContext(nsIFrame* aFrame) michael@0: { michael@0: nsStyleContext *sc; michael@0: if (!nsCSSRendering::FindBackground(aFrame, &sc)) { michael@0: // We don't want to bail out if moz-appearance is set on a root michael@0: // node. If it has a parent content node, bail because it's not michael@0: // a root, other wise keep going in order to let the theme stuff michael@0: // draw the background. The canvas really should be drawing the michael@0: // bg, but there's no way to hook that up via css. michael@0: if (!aFrame->StyleDisplay()->mAppearance) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIContent* content = aFrame->GetContent(); michael@0: if (!content || content->GetParent()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: sc = aFrame->StyleContext(); michael@0: } michael@0: return sc; michael@0: } michael@0: michael@0: /*static*/ bool michael@0: nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, michael@0: nsDisplayList* aList) michael@0: { michael@0: nsStyleContext* bgSC = nullptr; michael@0: const nsStyleBackground* bg = nullptr; michael@0: nsPresContext* presContext = aFrame->PresContext(); michael@0: bool isThemed = aFrame->IsThemed(); michael@0: if (!isThemed) { michael@0: bgSC = GetBackgroundStyleContext(aFrame); michael@0: if (bgSC) { michael@0: bg = bgSC->StyleBackground(); michael@0: } michael@0: } michael@0: michael@0: bool drawBackgroundColor = false; michael@0: nscolor color; michael@0: if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) { michael@0: bool drawBackgroundImage; michael@0: color = michael@0: nsCSSRendering::DetermineBackgroundColor(presContext, bgSC, aFrame, michael@0: drawBackgroundImage, drawBackgroundColor); michael@0: } michael@0: michael@0: // An auxiliary list is necessary in case we have background blending; if that michael@0: // is the case, background items need to be wrapped by a blend container to michael@0: // isolate blending to the background michael@0: nsDisplayList bgItemList; michael@0: // Even if we don't actually have a background color to paint, we may still need michael@0: // to create an item for hit testing. michael@0: if ((drawBackgroundColor && color != NS_RGBA(0,0,0,0)) || michael@0: aBuilder->IsForEventDelivery()) { michael@0: bgItemList.AppendNewToTop( michael@0: new (aBuilder) nsDisplayBackgroundColor(aBuilder, aFrame, bg, michael@0: drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0))); michael@0: } michael@0: michael@0: if (isThemed) { michael@0: nsDisplayThemedBackground* bgItem = michael@0: new (aBuilder) nsDisplayThemedBackground(aBuilder, aFrame); michael@0: bgItemList.AppendNewToTop(bgItem); michael@0: aList->AppendToTop(&bgItemList); michael@0: return true; michael@0: } michael@0: michael@0: if (!bg) { michael@0: aList->AppendToTop(&bgItemList); michael@0: return false; michael@0: } michael@0: michael@0: bool needBlendContainer = false; michael@0: michael@0: // Passing bg == nullptr in this macro will result in one iteration with michael@0: // i = 0. michael@0: NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { michael@0: if (bg->mLayers[i].mImage.IsEmpty()) { michael@0: continue; michael@0: } michael@0: michael@0: if (bg->mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) { michael@0: needBlendContainer = true; michael@0: } michael@0: michael@0: nsDisplayBackgroundImage* bgItem = michael@0: new (aBuilder) nsDisplayBackgroundImage(aBuilder, aFrame, i, bg); michael@0: bgItemList.AppendNewToTop(bgItem); michael@0: } michael@0: michael@0: if (needBlendContainer) { michael@0: bgItemList.AppendNewToTop( michael@0: new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, &bgItemList)); michael@0: } michael@0: michael@0: aList->AppendToTop(&bgItemList); michael@0: return false; michael@0: } michael@0: michael@0: // Check that the rounded border of aFrame, added to aToReferenceFrame, michael@0: // intersects aRect. Assumes that the unrounded border has already michael@0: // been checked for intersection. michael@0: static bool michael@0: RoundedBorderIntersectsRect(nsIFrame* aFrame, michael@0: const nsPoint& aFrameToReferenceFrame, michael@0: const nsRect& aTestRect) michael@0: { michael@0: if (!nsRect(aFrameToReferenceFrame, aFrame->GetSize()).Intersects(aTestRect)) michael@0: return false; michael@0: michael@0: nscoord radii[8]; michael@0: return !aFrame->GetBorderRadii(radii) || michael@0: nsLayoutUtils::RoundedRectIntersectsRect(nsRect(aFrameToReferenceFrame, michael@0: aFrame->GetSize()), michael@0: radii, aTestRect); michael@0: } michael@0: michael@0: // Returns TRUE if aContainedRect is guaranteed to be contained in michael@0: // the rounded rect defined by aRoundedRect and aRadii. Complex cases are michael@0: // handled conservatively by returning FALSE in some situations where michael@0: // a more thorough analysis could return TRUE. michael@0: // michael@0: // See also RoundedRectIntersectsRect. michael@0: static bool RoundedRectContainsRect(const nsRect& aRoundedRect, michael@0: const nscoord aRadii[8], michael@0: const nsRect& aContainedRect) { michael@0: nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect); michael@0: return rgn.Contains(aContainedRect); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aClipRect, michael@0: gfxRect* aDestRect) michael@0: { michael@0: if (!mBackgroundStyle) michael@0: return false; michael@0: michael@0: if (mBackgroundStyle->mLayers.Length() != 1) michael@0: return false; michael@0: michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: uint32_t flags = aBuilder->GetBackgroundPaintFlags(); michael@0: nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; michael@0: michael@0: if (layer.mAttachment != NS_STYLE_BG_ATTACHMENT_FIXED) michael@0: return false; michael@0: michael@0: nsBackgroundLayerState state = michael@0: nsCSSRendering::PrepareBackgroundLayer(presContext, michael@0: mFrame, michael@0: flags, michael@0: borderArea, michael@0: aClipRect, michael@0: *mBackgroundStyle, michael@0: layer); michael@0: michael@0: nsImageRenderer* imageRenderer = &state.mImageRenderer; michael@0: // We only care about images here, not gradients. michael@0: if (!imageRenderer->IsRasterImage()) michael@0: return false; michael@0: michael@0: int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); michael@0: *aDestRect = nsLayoutUtils::RectToGfxRect(state.mFillArea, appUnitsPerDevPixel); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBackgroundImage::TryOptimizeToImageLayer(LayerManager* aManager, michael@0: nsDisplayListBuilder* aBuilder) michael@0: { michael@0: if (!mBackgroundStyle) michael@0: return false; michael@0: michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: uint32_t flags = aBuilder->GetBackgroundPaintFlags(); michael@0: nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; michael@0: michael@0: if (layer.mClip != NS_STYLE_BG_CLIP_BORDER) { michael@0: return false; michael@0: } michael@0: nscoord radii[8]; michael@0: if (mFrame->GetBorderRadii(radii)) { michael@0: return false; michael@0: } michael@0: michael@0: nsBackgroundLayerState state = michael@0: nsCSSRendering::PrepareBackgroundLayer(presContext, michael@0: mFrame, michael@0: flags, michael@0: borderArea, michael@0: borderArea, michael@0: *mBackgroundStyle, michael@0: layer); michael@0: michael@0: nsImageRenderer* imageRenderer = &state.mImageRenderer; michael@0: // We only care about images here, not gradients. michael@0: if (!imageRenderer->IsRasterImage()) michael@0: return false; michael@0: michael@0: nsRefPtr imageContainer = imageRenderer->GetContainer(aManager); michael@0: // Image is not ready to be made into a layer yet michael@0: if (!imageContainer) michael@0: return false; michael@0: michael@0: // We currently can't handle tiled or partial backgrounds. michael@0: if (!state.mDestArea.IsEqualEdges(state.mFillArea)) { michael@0: return false; michael@0: } michael@0: michael@0: // XXX Ignoring state.mAnchor. ImageLayer drawing snaps mDestArea edges to michael@0: // layer pixel boundaries. This should be OK for now. michael@0: michael@0: int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); michael@0: mDestRect = nsLayoutUtils::RectToGfxRect(state.mDestArea, appUnitsPerDevPixel); michael@0: mImageContainer = imageContainer; michael@0: michael@0: // Ok, we can turn this into a layer if needed. michael@0: return true; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDisplayBackgroundImage::GetContainer(LayerManager* aManager, michael@0: nsDisplayListBuilder *aBuilder) michael@0: { michael@0: if (!TryOptimizeToImageLayer(aManager, aBuilder)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr container = mImageContainer; michael@0: michael@0: return container.forget(); michael@0: } michael@0: michael@0: LayerState michael@0: nsDisplayBackgroundImage::GetLayerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) michael@0: { michael@0: bool animated = false; michael@0: if (mBackgroundStyle) { michael@0: const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; michael@0: const nsStyleImage* image = &layer.mImage; michael@0: if (image->GetType() == eStyleImageType_Image) { michael@0: imgIRequest* imgreq = image->GetImageData(); michael@0: nsCOMPtr image; michael@0: if (NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) && image) { michael@0: if (NS_FAILED(image->GetAnimated(&animated))) { michael@0: animated = false; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!animated || michael@0: !nsLayoutUtils::AnimatedImageLayersEnabled()) { michael@0: if (!aManager->IsCompositingCheap() || michael@0: !nsLayoutUtils::GPUImageScalingEnabled()) { michael@0: return LAYER_NONE; michael@0: } michael@0: } michael@0: michael@0: if (!TryOptimizeToImageLayer(aManager, aBuilder)) { michael@0: return LAYER_NONE; michael@0: } michael@0: michael@0: if (!animated) { michael@0: mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize(); michael@0: NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!"); michael@0: michael@0: gfxRect destRect = mDestRect; michael@0: michael@0: destRect.width *= aParameters.mXScale; michael@0: destRect.height *= aParameters.mYScale; michael@0: michael@0: // Calculate the scaling factor for the frame. michael@0: gfxSize scale = gfxSize(destRect.width / imageSize.width, destRect.height / imageSize.height); michael@0: michael@0: // If we are not scaling at all, no point in separating this into a layer. michael@0: if (scale.width == 1.0f && scale.height == 1.0f) { michael@0: return LAYER_NONE; michael@0: } michael@0: michael@0: // If the target size is pretty small, no point in using a layer. michael@0: if (destRect.width * destRect.height < 64 * 64) { michael@0: return LAYER_NONE; michael@0: } michael@0: } michael@0: michael@0: return LAYER_ACTIVE; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDisplayBackgroundImage::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) michael@0: { michael@0: nsRefPtr layer = static_cast michael@0: (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this)); michael@0: if (!layer) { michael@0: layer = aManager->CreateImageLayer(); michael@0: if (!layer) michael@0: return nullptr; michael@0: } michael@0: layer->SetContainer(mImageContainer); michael@0: ConfigureLayer(layer, aParameters.mOffset); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: void michael@0: nsDisplayBackgroundImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset) michael@0: { michael@0: aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame)); michael@0: michael@0: mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize(); michael@0: NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!"); michael@0: michael@0: gfxPoint p = mDestRect.TopLeft() + aOffset; michael@0: gfx::Matrix transform; michael@0: transform.Translate(p.x, p.y); michael@0: transform.Scale(mDestRect.width/imageSize.width, michael@0: mDestRect.height/imageSize.height); michael@0: aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); michael@0: aLayer->SetVisibleRegion(nsIntRect(0, 0, imageSize.width, imageSize.height)); michael@0: } michael@0: michael@0: void michael@0: nsDisplayBackgroundImage::HitTest(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) michael@0: { michael@0: if (RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { michael@0: aOutFrames->AppendElement(mFrame); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBackgroundImage::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) michael@0: { michael@0: if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, michael@0: aAllowVisibleRegionExpansion)) { michael@0: return false; michael@0: } michael@0: michael@0: // Return false if the background was propagated away from this michael@0: // frame. We don't want this display item to show up and confuse michael@0: // anything. michael@0: return mBackgroundStyle; michael@0: } michael@0: michael@0: /* static */ nsRegion michael@0: nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem, michael@0: nsPresContext* aPresContext, michael@0: uint8_t aClip, const nsRect& aRect, michael@0: bool* aSnap) michael@0: { michael@0: nsRegion result; michael@0: if (aRect.IsEmpty()) michael@0: return result; michael@0: michael@0: nsIFrame *frame = aItem->Frame(); michael@0: michael@0: nscoord radii[8]; michael@0: nsRect clipRect; michael@0: bool haveRadii; michael@0: switch (aClip) { michael@0: case NS_STYLE_BG_CLIP_BORDER: michael@0: haveRadii = frame->GetBorderRadii(radii); michael@0: clipRect = nsRect(aItem->ToReferenceFrame(), frame->GetSize()); michael@0: break; michael@0: case NS_STYLE_BG_CLIP_PADDING: michael@0: haveRadii = frame->GetPaddingBoxBorderRadii(radii); michael@0: clipRect = frame->GetPaddingRect() - frame->GetPosition() + aItem->ToReferenceFrame(); michael@0: break; michael@0: case NS_STYLE_BG_CLIP_CONTENT: michael@0: haveRadii = frame->GetContentBoxBorderRadii(radii); michael@0: clipRect = frame->GetContentRect() - frame->GetPosition() + aItem->ToReferenceFrame(); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Unknown clip type"); michael@0: return result; michael@0: } michael@0: michael@0: if (haveRadii) { michael@0: *aSnap = false; michael@0: result = nsLayoutUtils::RoundedRectIntersectRect(clipRect, radii, aRect); michael@0: } else { michael@0: result = clipRect.Intersect(aRect); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsRegion michael@0: nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) { michael@0: nsRegion result; michael@0: *aSnap = false; michael@0: michael@0: if (!mBackgroundStyle) michael@0: return result; michael@0: michael@0: michael@0: *aSnap = true; michael@0: michael@0: // For policies other than EACH_BOX, don't try to optimize here, since michael@0: // this could easily lead to O(N^2) behavior inside InlineBackgroundData, michael@0: // which expects frames to be sent to it in content order, not reverse michael@0: // content order which we'll produce here. michael@0: // Of course, if there's only one frame in the flow, it doesn't matter. michael@0: if (mBackgroundStyle->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX || michael@0: (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) { michael@0: const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer]; michael@0: if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL) { michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: result = GetInsideClipRegion(this, presContext, layer.mClip, mBounds, aSnap); michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBackgroundImage::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { michael@0: if (!mBackgroundStyle) { michael@0: *aColor = NS_RGBA(0,0,0,0); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBackgroundImage::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame) michael@0: { michael@0: if (!mBackgroundStyle) michael@0: return false; michael@0: if (!mBackgroundStyle->HasFixedBackground()) michael@0: return false; michael@0: michael@0: // If aFrame is mFrame or an ancestor in this document, and aFrame is michael@0: // not the viewport frame, then moving aFrame will move mFrame michael@0: // relative to the viewport, so our fixed-pos background will change. michael@0: return aFrame->GetParent() && michael@0: (aFrame == mFrame || michael@0: nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame)); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayBackgroundImage::GetPositioningArea() michael@0: { michael@0: if (!mBackgroundStyle) { michael@0: return nsRect(); michael@0: } michael@0: nsIFrame* attachedToFrame; michael@0: return nsCSSRendering::ComputeBackgroundPositioningArea( michael@0: mFrame->PresContext(), mFrame, michael@0: nsRect(ToReferenceFrame(), mFrame->GetSize()), michael@0: *mBackgroundStyle, mBackgroundStyle->mLayers[mLayer], michael@0: &attachedToFrame) + ToReferenceFrame(); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBackgroundImage::RenderingMightDependOnPositioningAreaSizeChange() michael@0: { michael@0: if (!mBackgroundStyle) michael@0: return false; michael@0: michael@0: nscoord radii[8]; michael@0: if (mFrame->GetBorderRadii(radii)) { michael@0: // A change in the size of the positioning area might change the position michael@0: // of the rounded corners. michael@0: return true; michael@0: } michael@0: michael@0: const nsStyleBackground::Layer &layer = mBackgroundStyle->mLayers[mLayer]; michael@0: if (layer.RenderingMightDependOnPositioningAreaSizeChange()) { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static void CheckForBorderItem(nsDisplayItem *aItem, uint32_t& aFlags) michael@0: { michael@0: nsDisplayItem* nextItem = aItem->GetAbove(); michael@0: while (nextItem && nextItem->GetType() == nsDisplayItem::TYPE_BACKGROUND) { michael@0: nextItem = nextItem->GetAbove(); michael@0: } michael@0: if (nextItem && michael@0: nextItem->Frame() == aItem->Frame() && michael@0: nextItem->GetType() == nsDisplayItem::TYPE_BORDER) { michael@0: aFlags |= nsCSSRendering::PAINTBG_WILL_PAINT_BORDER; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr); michael@0: } michael@0: michael@0: void michael@0: nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx, const nsRect& aBounds, michael@0: nsRect* aClipRect) { michael@0: nsPoint offset = ToReferenceFrame(); michael@0: uint32_t flags = aBuilder->GetBackgroundPaintFlags(); michael@0: CheckForBorderItem(this, flags); michael@0: michael@0: nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame, michael@0: aBounds, michael@0: nsRect(offset, mFrame->GetSize()), michael@0: flags, aClipRect, mLayer); michael@0: michael@0: } michael@0: michael@0: void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion* aInvalidRegion) michael@0: { michael@0: if (!mBackgroundStyle) { michael@0: return; michael@0: } michael@0: michael@0: const nsDisplayBackgroundGeometry* geometry = static_cast(aGeometry); michael@0: michael@0: bool snap; michael@0: nsRect bounds = GetBounds(aBuilder, &snap); michael@0: nsRect positioningArea = GetPositioningArea(); michael@0: if (positioningArea.TopLeft() != geometry->mPositioningArea.TopLeft() || michael@0: (positioningArea.Size() != geometry->mPositioningArea.Size() && michael@0: RenderingMightDependOnPositioningAreaSizeChange())) { michael@0: // Positioning area changed in a way that could cause everything to change, michael@0: // so invalidate everything (both old and new painting areas). michael@0: aInvalidRegion->Or(bounds, geometry->mBounds); michael@0: michael@0: if (positioningArea.Size() != geometry->mPositioningArea.Size()) { michael@0: NotifyRenderingChanged(); michael@0: } michael@0: return; michael@0: } michael@0: if (aBuilder->ShouldSyncDecodeImages()) { michael@0: if (mBackgroundStyle && michael@0: !nsCSSRendering::IsBackgroundImageDecodedForStyleContextAndLayer(mBackgroundStyle, mLayer)) { michael@0: aInvalidRegion->Or(*aInvalidRegion, bounds); michael@0: michael@0: NotifyRenderingChanged(); michael@0: } michael@0: } michael@0: if (!bounds.IsEqualInterior(geometry->mBounds)) { michael@0: // Positioning area is unchanged, so invalidate just the change in the michael@0: // painting area. michael@0: aInvalidRegion->Xor(bounds, geometry->mBounds); michael@0: michael@0: NotifyRenderingChanged(); michael@0: } michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayBackgroundImage::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { michael@0: *aSnap = true; michael@0: return mBounds; michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayBackgroundImage::GetBoundsInternal(nsDisplayListBuilder* aBuilder) { michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: michael@0: if (!mBackgroundStyle) { michael@0: return nsRect(); michael@0: } michael@0: michael@0: nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: nsRect clipRect = borderBox; michael@0: if (mFrame->GetType() == nsGkAtoms::canvasFrame) { michael@0: nsCanvasFrame* frame = static_cast(mFrame); michael@0: clipRect = frame->CanvasArea() + ToReferenceFrame(); michael@0: } michael@0: const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer]; michael@0: return nsCSSRendering::GetBackgroundLayerRect(presContext, mFrame, michael@0: borderBox, clipRect, michael@0: *mBackgroundStyle, layer, michael@0: aBuilder->GetBackgroundPaintFlags()); michael@0: } michael@0: michael@0: uint32_t michael@0: nsDisplayBackgroundImage::GetPerFrameKey() michael@0: { michael@0: return (mLayer << nsDisplayItem::TYPE_BITS) | michael@0: nsDisplayItem::GetPerFrameKey(); michael@0: } michael@0: michael@0: nsDisplayThemedBackground::nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame) michael@0: : nsDisplayItem(aBuilder, aFrame) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayThemedBackground); michael@0: michael@0: const nsStyleDisplay* disp = mFrame->StyleDisplay(); michael@0: mAppearance = disp->mAppearance; michael@0: mFrame->IsThemed(disp, &mThemeTransparency); michael@0: michael@0: // Perform necessary RegisterThemeGeometry michael@0: switch (disp->mAppearance) { michael@0: case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR: michael@0: case NS_THEME_TOOLBAR: michael@0: case NS_THEME_WINDOW_TITLEBAR: michael@0: case NS_THEME_WINDOW_BUTTON_BOX: michael@0: case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON: michael@0: case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED: michael@0: RegisterThemeGeometry(aBuilder, aFrame); michael@0: break; michael@0: case NS_THEME_WIN_BORDERLESS_GLASS: michael@0: case NS_THEME_WIN_GLASS: michael@0: aBuilder->SetGlassDisplayItem(this); michael@0: break; michael@0: } michael@0: michael@0: mBounds = GetBoundsInternal(); michael@0: } michael@0: michael@0: nsDisplayThemedBackground::~nsDisplayThemedBackground() michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: MOZ_COUNT_DTOR(nsDisplayThemedBackground); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: nsDisplayThemedBackground::WriteDebugInfo(nsACString& aTo) michael@0: { michael@0: aTo += nsPrintfCString(" (themed, appearance:%d)", mAppearance); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsDisplayThemedBackground::HitTest(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) michael@0: { michael@0: // Assume that any point in our border rect is a hit. michael@0: if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) { michael@0: aOutFrames->AppendElement(mFrame); michael@0: } michael@0: } michael@0: michael@0: nsRegion michael@0: nsDisplayThemedBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) { michael@0: nsRegion result; michael@0: *aSnap = false; michael@0: michael@0: if (mThemeTransparency == nsITheme::eOpaque) { michael@0: result = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayThemedBackground::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { michael@0: if (mAppearance == NS_THEME_WIN_BORDERLESS_GLASS || michael@0: mAppearance == NS_THEME_WIN_GLASS) { michael@0: *aColor = NS_RGBA(0,0,0,0); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayThemedBackground::GetPositioningArea() michael@0: { michael@0: return nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: } michael@0: michael@0: void michael@0: nsDisplayThemedBackground::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: PaintInternal(aBuilder, aCtx, mVisibleRect, nullptr); michael@0: } michael@0: michael@0: michael@0: void michael@0: nsDisplayThemedBackground::PaintInternal(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx, const nsRect& aBounds, michael@0: nsRect* aClipRect) michael@0: { michael@0: // XXXzw this ignores aClipRect. michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: nsITheme *theme = presContext->GetTheme(); michael@0: nsRect borderArea(ToReferenceFrame(), mFrame->GetSize()); michael@0: nsRect drawing(borderArea); michael@0: theme->GetWidgetOverflow(presContext->DeviceContext(), mFrame, mAppearance, michael@0: &drawing); michael@0: drawing.IntersectRect(drawing, aBounds); michael@0: theme->DrawWidgetBackground(aCtx, mFrame, mAppearance, borderArea, drawing); michael@0: } michael@0: michael@0: bool nsDisplayThemedBackground::IsWindowActive() michael@0: { michael@0: EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState(); michael@0: return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE); michael@0: } michael@0: michael@0: void nsDisplayThemedBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion* aInvalidRegion) michael@0: { michael@0: const nsDisplayThemedBackgroundGeometry* geometry = static_cast(aGeometry); michael@0: michael@0: bool snap; michael@0: nsRect bounds = GetBounds(aBuilder, &snap); michael@0: nsRect positioningArea = GetPositioningArea(); michael@0: if (!positioningArea.IsEqualInterior(geometry->mPositioningArea)) { michael@0: // Invalidate everything (both old and new painting areas). michael@0: aInvalidRegion->Or(bounds, geometry->mBounds); michael@0: return; michael@0: } michael@0: if (!bounds.IsEqualInterior(geometry->mBounds)) { michael@0: // Positioning area is unchanged, so invalidate just the change in the michael@0: // painting area. michael@0: aInvalidRegion->Xor(bounds, geometry->mBounds); michael@0: } michael@0: nsITheme* theme = mFrame->PresContext()->GetTheme(); michael@0: if (theme->WidgetAppearanceDependsOnWindowFocus(mAppearance) && michael@0: IsWindowActive() != geometry->mWindowIsActive) { michael@0: aInvalidRegion->Or(*aInvalidRegion, bounds); michael@0: } michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayThemedBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { michael@0: *aSnap = true; michael@0: return mBounds; michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayThemedBackground::GetBoundsInternal() { michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: michael@0: nsRect r(nsPoint(0,0), mFrame->GetSize()); michael@0: presContext->GetTheme()-> michael@0: GetWidgetOverflow(presContext->DeviceContext(), mFrame, michael@0: mFrame->StyleDisplay()->mAppearance, &r); michael@0: #ifdef XP_MACOSX michael@0: // Bug 748219 michael@0: r.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel()); michael@0: #endif michael@0: michael@0: return r + ToReferenceFrame(); michael@0: } michael@0: michael@0: void michael@0: nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: if (mColor == NS_RGBA(0, 0, 0, 0)) { michael@0: return; michael@0: } michael@0: michael@0: nsPoint offset = ToReferenceFrame(); michael@0: uint32_t flags = aBuilder->GetBackgroundPaintFlags(); michael@0: CheckForBorderItem(this, flags); michael@0: nsCSSRendering::PaintBackgroundColor(mFrame->PresContext(), *aCtx, mFrame, michael@0: mVisibleRect, michael@0: nsRect(offset, mFrame->GetSize()), michael@0: flags); michael@0: } michael@0: michael@0: nsRegion michael@0: nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) michael@0: { michael@0: if (NS_GET_A(mColor) != 255) { michael@0: return nsRegion(); michael@0: } michael@0: michael@0: if (!mBackgroundStyle) michael@0: return nsRegion(); michael@0: michael@0: *aSnap = true; michael@0: michael@0: const nsStyleBackground::Layer& bottomLayer = mBackgroundStyle->BottomLayer(); michael@0: nsRect borderBox = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: return nsDisplayBackgroundImage::GetInsideClipRegion(this, presContext, bottomLayer.mClip, borderBox, aSnap); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBackgroundColor::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) michael@0: { michael@0: *aColor = mColor; michael@0: michael@0: if (!mBackgroundStyle) michael@0: return true; michael@0: michael@0: return (!nsLayoutUtils::HasNonZeroCorner(mFrame->StyleBorder()->mBorderRadius) && michael@0: mBackgroundStyle->BottomLayer().mClip == NS_STYLE_BG_CLIP_BORDER); michael@0: } michael@0: michael@0: void michael@0: nsDisplayBackgroundColor::HitTest(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) michael@0: { michael@0: if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { michael@0: // aRect doesn't intersect our border-radius curve. michael@0: return; michael@0: } michael@0: michael@0: aOutFrames->AppendElement(mFrame); michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: nsDisplayBackgroundColor::WriteDebugInfo(nsACString& aTo) michael@0: { michael@0: aTo += nsPrintfCString(" (rgba %d,%d,%d,%d)", michael@0: NS_GET_R(mColor), NS_GET_G(mColor), michael@0: NS_GET_B(mColor), NS_GET_A(mColor)); michael@0: } michael@0: #endif michael@0: michael@0: nsRect michael@0: nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { michael@0: *aSnap = false; michael@0: return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); michael@0: } michael@0: michael@0: void michael@0: nsDisplayOutline::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: // TODO join outlines together michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsCSSRendering::PaintOutline(mFrame->PresContext(), *aCtx, mFrame, michael@0: mVisibleRect, michael@0: nsRect(offset, mFrame->GetSize()), michael@0: mFrame->StyleContext()); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayOutline::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, michael@0: aAllowVisibleRegionExpansion)) { michael@0: return false; michael@0: } michael@0: michael@0: const nsStyleOutline* outline = mFrame->StyleOutline(); michael@0: nsRect borderBox(ToReferenceFrame(), mFrame->GetSize()); michael@0: if (borderBox.Contains(aVisibleRegion->GetBounds()) && michael@0: !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) { michael@0: if (outline->mOutlineOffset >= 0) { michael@0: // the visible region is entirely inside the border-rect, and the outline michael@0: // isn't rendered inside the border-rect, so the outline is not visible michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) michael@0: { michael@0: if (!RoundedBorderIntersectsRect(mFrame, ToReferenceFrame(), aRect)) { michael@0: // aRect doesn't intersect our border-radius curve. michael@0: return; michael@0: } michael@0: michael@0: aOutFrames->AppendElement(mFrame); michael@0: } michael@0: michael@0: void michael@0: nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame) michael@0: { michael@0: NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame), michael@0: "Reference frame mismatch"); michael@0: uint8_t pointerEvents = aFrame->StyleVisibility()->mPointerEvents; michael@0: if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) { michael@0: return; michael@0: } michael@0: // XXX handle other pointerEvents values for SVG michael@0: // XXX Do something clever here for the common case where the border box michael@0: // is obviously entirely inside mHitRegion. michael@0: nsRect borderBox(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize()); michael@0: const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder); michael@0: bool borderBoxHasRoundedCorners = michael@0: nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius); michael@0: if (clip) { michael@0: borderBox = clip->ApplyNonRoundedIntersection(borderBox); michael@0: if (clip->GetRoundedRectCount() > 0) { michael@0: borderBoxHasRoundedCorners = true; michael@0: } michael@0: } michael@0: if (borderBoxHasRoundedCorners || michael@0: (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { michael@0: mMaybeHitRegion.Or(mMaybeHitRegion, borderBox); michael@0: } else { michael@0: mHitRegion.Or(mHitRegion, borderBox); michael@0: } michael@0: if (aBuilder->GetAncestorHasTouchEventHandler()) { michael@0: mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: // Note: Because we exist, we know that the caret is visible, so we don't michael@0: // need to check for the caret's visibility. michael@0: mCaret->PaintCaret(aBuilder, aCtx, mFrame, ToReferenceFrame()); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBorder::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, michael@0: aAllowVisibleRegionExpansion)) { michael@0: return false; michael@0: } michael@0: michael@0: nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() + michael@0: ToReferenceFrame(); michael@0: const nsStyleBorder *styleBorder; michael@0: if (paddingRect.Contains(aVisibleRegion->GetBounds()) && michael@0: !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded() && michael@0: !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) { michael@0: // the visible region is entirely inside the content rect, and no part michael@0: // of the border is rendered inside the content rect, so we are not michael@0: // visible michael@0: // Skip this if there's a border-image (which draws a background michael@0: // too) or if there is a border-radius (which makes the border draw michael@0: // further in). michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsDisplayItemGeometry* michael@0: nsDisplayBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: return new nsDisplayBorderGeometry(this, aBuilder); michael@0: } michael@0: michael@0: void michael@0: nsDisplayBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion* aInvalidRegion) michael@0: { michael@0: const nsDisplayBorderGeometry* geometry = static_cast(aGeometry); michael@0: bool snap; michael@0: if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) || michael@0: !geometry->mContentRect.IsEqualInterior(GetContentRect())) { michael@0: // We can probably get away with only invalidating the difference michael@0: // between the border and padding rects, but the XUL ui at least michael@0: // is apparently painting a background with this? michael@0: aInvalidRegion->Or(GetBounds(aBuilder, &snap), geometry->mBounds); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame, michael@0: mVisibleRect, michael@0: nsRect(offset, mFrame->GetSize()), michael@0: mFrame->StyleContext(), michael@0: mFrame->GetSkipSides()); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) michael@0: { michael@0: *aSnap = true; michael@0: return CalculateBounds(*mFrame->StyleBorder()); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayBorder::CalculateBounds(const nsStyleBorder& aStyleBorder) michael@0: { michael@0: nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize()); michael@0: if (aStyleBorder.IsBorderImageLoaded()) { michael@0: borderBounds.Inflate(aStyleBorder.GetImageOutset()); michael@0: return borderBounds; michael@0: } else { michael@0: nsMargin border = aStyleBorder.GetComputedBorder(); michael@0: nsRect result; michael@0: if (border.top > 0) { michael@0: result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(), border.top); michael@0: } michael@0: if (border.right > 0) { michael@0: result.UnionRect(result, nsRect(borderBounds.XMost() - border.right, borderBounds.Y(), border.right, borderBounds.Height())); michael@0: } michael@0: if (border.bottom > 0) { michael@0: result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.YMost() - border.bottom, borderBounds.Width(), border.bottom)); michael@0: } michael@0: if (border.left > 0) { michael@0: result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.Y(), border.left, borderBounds.Height())); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: } michael@0: michael@0: // Given a region, compute a conservative approximation to it as a list michael@0: // of rectangles that aren't vertically adjacent (i.e., vertically michael@0: // adjacent or overlapping rectangles are combined). michael@0: // Right now this is only approximate, some vertically overlapping rectangles michael@0: // aren't guaranteed to be combined. michael@0: static void michael@0: ComputeDisjointRectangles(const nsRegion& aRegion, michael@0: nsTArray* aRects) { michael@0: nscoord accumulationMargin = nsPresContext::CSSPixelsToAppUnits(25); michael@0: nsRect accumulated; michael@0: nsRegionRectIterator iter(aRegion); michael@0: while (true) { michael@0: const nsRect* r = iter.Next(); michael@0: if (r && !accumulated.IsEmpty() && michael@0: accumulated.YMost() >= r->y - accumulationMargin) { michael@0: accumulated.UnionRect(accumulated, *r); michael@0: continue; michael@0: } michael@0: michael@0: if (!accumulated.IsEmpty()) { michael@0: aRects->AppendElement(accumulated); michael@0: accumulated.SetEmpty(); michael@0: } michael@0: michael@0: if (!r) michael@0: break; michael@0: michael@0: accumulated = *r; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset; michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: nsAutoTArray rects; michael@0: ComputeDisjointRectangles(mVisibleRegion, &rects); michael@0: michael@0: PROFILER_LABEL("nsDisplayBoxShadowOuter", "Paint"); michael@0: for (uint32_t i = 0; i < rects.Length(); ++i) { michael@0: aCtx->PushState(); michael@0: aCtx->IntersectClip(rects[i]); michael@0: nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame, michael@0: borderRect, rects[i], mOpacity); michael@0: aCtx->PopState(); michael@0: } michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { michael@0: *aSnap = false; michael@0: return mBounds; michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayBoxShadowOuter::GetBoundsInternal() { michael@0: return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) + michael@0: ToReferenceFrame(); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, michael@0: aAllowVisibleRegionExpansion)) { michael@0: return false; michael@0: } michael@0: michael@0: // Store the actual visible region michael@0: mVisibleRegion.And(*aVisibleRegion, mVisibleRect); michael@0: michael@0: nsPoint origin = ToReferenceFrame(); michael@0: nsRect visibleBounds = aVisibleRegion->GetBounds(); michael@0: nsRect frameRect(origin, mFrame->GetSize()); michael@0: if (!frameRect.Contains(visibleBounds)) michael@0: return true; michael@0: michael@0: // the visible region is entirely inside the border-rect, and box shadows michael@0: // never render within the border-rect (unless there's a border radius). michael@0: nscoord twipsRadii[8]; michael@0: bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii); michael@0: if (!hasBorderRadii) michael@0: return false; michael@0: michael@0: return !RoundedRectContainsRect(frameRect, twipsRadii, visibleBounds); michael@0: } michael@0: michael@0: void michael@0: nsDisplayBoxShadowOuter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion* aInvalidRegion) michael@0: { michael@0: const nsDisplayItemGenericGeometry* geometry = michael@0: static_cast(aGeometry); michael@0: bool snap; michael@0: if (!geometry->mBounds.IsEqualInterior(GetBounds(aBuilder, &snap)) || michael@0: !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) { michael@0: nsRegion oldShadow, newShadow; michael@0: nscoord dontCare[8]; michael@0: bool hasBorderRadius = mFrame->GetBorderRadii(dontCare); michael@0: if (hasBorderRadius) { michael@0: // If we have rounded corners then we need to invalidate the frame area michael@0: // too since we paint into it. michael@0: oldShadow = geometry->mBounds; michael@0: newShadow = GetBounds(aBuilder, &snap); michael@0: } else { michael@0: oldShadow = oldShadow.Sub(geometry->mBounds, geometry->mBorderRect); michael@0: newShadow = newShadow.Sub(GetBounds(aBuilder, &snap), GetBorderRect()); michael@0: } michael@0: aInvalidRegion->Or(oldShadow, newShadow); michael@0: } michael@0: } michael@0: michael@0: michael@0: void michael@0: nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsRect borderRect = nsRect(offset, mFrame->GetSize()); michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: nsAutoTArray rects; michael@0: ComputeDisjointRectangles(mVisibleRegion, &rects); michael@0: michael@0: PROFILER_LABEL("nsDisplayBoxShadowInner", "Paint"); michael@0: for (uint32_t i = 0; i < rects.Length(); ++i) { michael@0: aCtx->PushState(); michael@0: aCtx->IntersectClip(rects[i]); michael@0: nsCSSRendering::PaintBoxShadowInner(presContext, *aCtx, mFrame, michael@0: borderRect, rects[i]); michael@0: aCtx->PopState(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion, michael@0: aAllowVisibleRegionExpansion)) { michael@0: return false; michael@0: } michael@0: michael@0: // Store the actual visible region michael@0: mVisibleRegion.And(*aVisibleRegion, mVisibleRect); michael@0: return true; michael@0: } michael@0: michael@0: nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList) michael@0: : nsDisplayItem(aBuilder, aFrame) michael@0: , mOverrideZIndex(0) michael@0: { michael@0: mList.AppendToTop(aList); michael@0: UpdateBounds(aBuilder); michael@0: michael@0: if (!aFrame || !aFrame->IsTransformed()) { michael@0: return; michael@0: } michael@0: michael@0: // If the frame is a preserve-3d parent, then we will create transforms michael@0: // inside this list afterwards (see WrapPreserve3DList in nsFrame.cpp). michael@0: // In this case we will always be outside of the transform, so share michael@0: // our parents reference frame. michael@0: if (aFrame->Preserves3DChildren()) { michael@0: mReferenceFrame = michael@0: aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); michael@0: mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); michael@0: return; michael@0: } michael@0: michael@0: // If we're a transformed frame, then we need to find out if we're inside michael@0: // the nsDisplayTransform or outside of it. Frames inside the transform michael@0: // need mReferenceFrame == mFrame, outside needs the next ancestor michael@0: // reference frame. michael@0: // If we're inside the transform, then the nsDisplayItem constructor michael@0: // will have done the right thing. michael@0: // If we're outside the transform, then we should have only one child michael@0: // (since nsDisplayTransform wraps all actual content), and that child michael@0: // will have the correct reference frame set (since nsDisplayTransform michael@0: // handles this explictly). michael@0: // michael@0: // Preserve-3d can cause us to have multiple nsDisplayTransform michael@0: // children. michael@0: nsDisplayItem *i = mList.GetBottom(); michael@0: if (i && (!i->GetAbove() || i->GetType() == TYPE_TRANSFORM) && michael@0: i->Frame() == mFrame) { michael@0: mReferenceFrame = i->ReferenceFrame(); michael@0: mToReferenceFrame = i->ToReferenceFrame(); michael@0: } michael@0: } michael@0: michael@0: nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayItem* aItem) michael@0: : nsDisplayItem(aBuilder, aFrame) michael@0: , mOverrideZIndex(0) michael@0: { michael@0: mList.AppendToTop(aItem); michael@0: UpdateBounds(aBuilder); michael@0: michael@0: if (!aFrame || !aFrame->IsTransformed()) { michael@0: return; michael@0: } michael@0: michael@0: if (aFrame->Preserves3DChildren()) { michael@0: mReferenceFrame = michael@0: aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); michael@0: mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); michael@0: return; michael@0: } michael@0: michael@0: // See the previous nsDisplayWrapList constructor michael@0: if (aItem->Frame() == aFrame) { michael@0: mReferenceFrame = aItem->ReferenceFrame(); michael@0: mToReferenceFrame = aItem->ToReferenceFrame(); michael@0: } michael@0: } michael@0: michael@0: nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayItem* aItem, michael@0: const nsIFrame* aReferenceFrame, michael@0: const nsPoint& aToReferenceFrame) michael@0: : nsDisplayItem(aBuilder, aFrame, aReferenceFrame, aToReferenceFrame) michael@0: , mOverrideZIndex(0) michael@0: { michael@0: mList.AppendToTop(aItem); michael@0: mBounds = mList.GetBounds(aBuilder); michael@0: } michael@0: michael@0: nsDisplayWrapList::~nsDisplayWrapList() { michael@0: mList.DeleteAll(); michael@0: } michael@0: michael@0: void michael@0: nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, michael@0: HitTestState* aState, nsTArray *aOutFrames) { michael@0: mList.HitTest(aBuilder, aRect, aState, aOutFrames); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { michael@0: *aSnap = false; michael@0: return mBounds; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: // Convert the passed in visible region to our appunits. michael@0: nsRegion visibleRegion; michael@0: // mVisibleRect has been clipped to GetClippedBounds michael@0: visibleRegion.And(*aVisibleRegion, mVisibleRect); michael@0: nsRegion originalVisibleRegion = visibleRegion; michael@0: michael@0: bool retval = michael@0: mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion, michael@0: mVisibleRect, michael@0: aAllowVisibleRegionExpansion); michael@0: michael@0: nsRegion removed; michael@0: // removed = originalVisibleRegion - visibleRegion michael@0: removed.Sub(originalVisibleRegion, visibleRegion); michael@0: // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications michael@0: // SubtractFromVisibleRegion does) michael@0: aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: nsRegion michael@0: nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) { michael@0: *aSnap = false; michael@0: nsRegion result; michael@0: if (mList.IsOpaque()) { michael@0: // Everything within GetBounds that's visible is opaque. michael@0: result = GetBounds(aBuilder, aSnap); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: bool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { michael@0: // We could try to do something but let's conservatively just return false. michael@0: return false; michael@0: } michael@0: michael@0: bool nsDisplayWrapList::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame) { michael@0: NS_WARNING("nsDisplayWrapList::IsVaryingRelativeToMovingFrame called unexpectedly"); michael@0: // We could try to do something but let's conservatively just return true. michael@0: return true; michael@0: } michael@0: michael@0: void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: NS_ERROR("nsDisplayWrapList should have been flattened away for painting"); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if all descendant display items can be placed in the same michael@0: * ThebesLayer --- GetLayerState returns LAYER_INACTIVE or LAYER_NONE, michael@0: * and they all have the expected animated geometry root. michael@0: */ michael@0: static LayerState michael@0: RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters, michael@0: const nsDisplayList& aList, michael@0: nsIFrame* aExpectedAnimatedGeometryRootForChildren) michael@0: { michael@0: LayerState result = LAYER_INACTIVE; michael@0: for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { michael@0: if (result == LAYER_INACTIVE && michael@0: nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) != michael@0: aExpectedAnimatedGeometryRootForChildren) { michael@0: result = LAYER_ACTIVE; michael@0: } michael@0: michael@0: LayerState state = i->GetLayerState(aBuilder, aManager, aParameters); michael@0: if ((state == LAYER_ACTIVE || state == LAYER_ACTIVE_FORCE) && michael@0: state > result) { michael@0: result = state; michael@0: } michael@0: if (state == LAYER_ACTIVE_EMPTY && state > result) { michael@0: result = LAYER_ACTIVE_FORCE; michael@0: } michael@0: if (state == LAYER_NONE) { michael@0: nsDisplayList* list = i->GetSameCoordinateSystemChildren(); michael@0: if (list) { michael@0: LayerState childState = michael@0: RequiredLayerStateForChildren(aBuilder, aManager, aParameters, *list, michael@0: aExpectedAnimatedGeometryRootForChildren); michael@0: if (childState > result) { michael@0: result = childState; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsRect nsDisplayWrapList::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: nsRect bounds; michael@0: for (nsDisplayItem* i = mList.GetBottom(); i; i = i->GetAbove()) { michael@0: bounds.UnionRect(bounds, i->GetComponentAlphaBounds(aBuilder)); michael@0: } michael@0: return bounds; michael@0: } michael@0: michael@0: static nsresult michael@0: WrapDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, michael@0: nsDisplayList* aList, nsDisplayWrapper* aWrapper) { michael@0: if (!aList->GetTop()) michael@0: return NS_OK; michael@0: nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList); michael@0: if (!item) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: // aList was emptied michael@0: aList->AppendToTop(item); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: WrapEachDisplayItem(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayList* aList, nsDisplayWrapper* aWrapper) { michael@0: nsDisplayList newList; michael@0: nsDisplayItem* item; michael@0: while ((item = aList->RemoveBottom())) { michael@0: item = aWrapper->WrapItem(aBuilder, item); michael@0: if (!item) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: newList.AppendToTop(item); michael@0: } michael@0: // aList was emptied michael@0: aList->AppendToTop(&newList); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsDisplayWrapper::WrapLists(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, const nsDisplayListSet& aIn, const nsDisplayListSet& aOut) michael@0: { michael@0: nsresult rv = WrapListsInPlace(aBuilder, aFrame, aIn); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (&aOut == &aIn) michael@0: return NS_OK; michael@0: aOut.BorderBackground()->AppendToTop(aIn.BorderBackground()); michael@0: aOut.BlockBorderBackgrounds()->AppendToTop(aIn.BlockBorderBackgrounds()); michael@0: aOut.Floats()->AppendToTop(aIn.Floats()); michael@0: aOut.Content()->AppendToTop(aIn.Content()); michael@0: aOut.PositionedDescendants()->AppendToTop(aIn.PositionedDescendants()); michael@0: aOut.Outlines()->AppendToTop(aIn.Outlines()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsDisplayWrapper::WrapListsInPlace(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, const nsDisplayListSet& aLists) michael@0: { michael@0: nsresult rv; michael@0: if (WrapBorderBackground()) { michael@0: // Our border-backgrounds are in-flow michael@0: rv = WrapDisplayList(aBuilder, aFrame, aLists.BorderBackground(), this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: // Our block border-backgrounds are in-flow michael@0: rv = WrapDisplayList(aBuilder, aFrame, aLists.BlockBorderBackgrounds(), this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // The floats are not in flow michael@0: rv = WrapEachDisplayItem(aBuilder, aLists.Floats(), this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // Our child content is in flow michael@0: rv = WrapDisplayList(aBuilder, aFrame, aLists.Content(), this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // The positioned descendants may not be in-flow michael@0: rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: // The outlines may not be in-flow michael@0: return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this); michael@0: } michael@0: michael@0: nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList) michael@0: : nsDisplayWrapList(aBuilder, aFrame, aList) { michael@0: MOZ_COUNT_CTOR(nsDisplayOpacity); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayOpacity::~nsDisplayOpacity() { michael@0: MOZ_COUNT_DTOR(nsDisplayOpacity); michael@0: } michael@0: #endif michael@0: michael@0: nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) { michael@0: *aSnap = false; michael@0: // We are never opaque, if our opacity was < 1 then we wouldn't have michael@0: // been created. michael@0: return nsRegion(); michael@0: } michael@0: michael@0: // nsDisplayOpacity uses layers for rendering michael@0: already_AddRefed michael@0: nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: if (mFrame->StyleDisplay()->mOpacity == 0 && mFrame->GetContent() && michael@0: !nsLayoutUtils::HasAnimations(mFrame->GetContent(), eCSSProperty_opacity)) { michael@0: return nullptr; michael@0: } michael@0: nsRefPtr container = aManager->GetLayerBuilder()-> michael@0: BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, michael@0: aContainerParameters, nullptr); michael@0: if (!container) michael@0: return nullptr; michael@0: michael@0: container->SetOpacity(mFrame->StyleDisplay()->mOpacity); michael@0: nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder, michael@0: this, mFrame, michael@0: eCSSProperty_opacity); michael@0: return container.forget(); michael@0: } michael@0: michael@0: /** michael@0: * This doesn't take into account layer scaling --- the layer may be michael@0: * rendered at a higher (or lower) resolution, affecting the retained layer michael@0: * size --- but this should be good enough. michael@0: */ michael@0: static bool michael@0: IsItemTooSmallForActiveLayer(nsDisplayItem* aItem) michael@0: { michael@0: nsIntRect visibleDevPixels = aItem->GetVisibleRect().ToOutsidePixels( michael@0: aItem->Frame()->PresContext()->AppUnitsPerDevPixel()); michael@0: static const int MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS = 16; michael@0: return visibleDevPixels.Size() < michael@0: nsIntSize(MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS, MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayOpacity::NeedsActiveLayer() michael@0: { michael@0: if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity) && michael@0: !IsItemTooSmallForActiveLayer(this)) michael@0: return true; michael@0: if (mFrame->GetContent()) { michael@0: if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(), michael@0: eCSSProperty_opacity)) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: if (NeedsActiveLayer()) michael@0: return false; michael@0: michael@0: nsDisplayItem* child = mList.GetBottom(); michael@0: // Only try folding our opacity down if we have a single michael@0: // child. We could potentially do this also if we had multiple michael@0: // children as long as they don't overlap. michael@0: if (!child || child->GetAbove()) { michael@0: return false; michael@0: } michael@0: michael@0: return child->ApplyOpacity(aBuilder, mFrame->StyleDisplay()->mOpacity, mClip); michael@0: } michael@0: michael@0: nsDisplayItem::LayerState michael@0: nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) { michael@0: if (NeedsActiveLayer()) michael@0: return LAYER_ACTIVE; michael@0: michael@0: return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, michael@0: nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder)); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: // Our children are translucent so we should not allow them to subtract michael@0: // area from aVisibleRegion. We do need to find out what is visible under michael@0: // our children in the temporary compositing buffer, because if our children michael@0: // paint our entire bounds opaquely then we don't need an alpha channel in michael@0: // the temporary compositing buffer. michael@0: nsRect bounds = GetClippedBounds(aBuilder); michael@0: nsRegion visibleUnderChildren; michael@0: visibleUnderChildren.And(*aVisibleRegion, bounds); michael@0: nsRect allowExpansion = bounds.Intersect(aAllowVisibleRegionExpansion); michael@0: return michael@0: nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren, michael@0: allowExpansion); michael@0: } michael@0: michael@0: bool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { michael@0: if (aItem->GetType() != TYPE_OPACITY) michael@0: return false; michael@0: // items for the same content element should be merged into a single michael@0: // compositing group michael@0: // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity michael@0: if (aItem->Frame()->GetContent() != mFrame->GetContent()) michael@0: return false; michael@0: if (aItem->GetClip() != GetClip()) michael@0: return false; michael@0: MergeFromTrackingMergedFrames(static_cast(aItem)); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: nsDisplayOpacity::WriteDebugInfo(nsACString& aTo) michael@0: { michael@0: aTo += nsPrintfCString(" (opacity %f)", mFrame->StyleDisplay()->mOpacity); michael@0: } michael@0: #endif michael@0: michael@0: nsDisplayMixBlendMode::nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList, michael@0: uint32_t aFlags) michael@0: : nsDisplayWrapList(aBuilder, aFrame, aList) { michael@0: MOZ_COUNT_CTOR(nsDisplayMixBlendMode); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayMixBlendMode::~nsDisplayMixBlendMode() { michael@0: MOZ_COUNT_DTOR(nsDisplayMixBlendMode); michael@0: } michael@0: #endif michael@0: michael@0: nsRegion nsDisplayMixBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) { michael@0: *aSnap = false; michael@0: // We are never considered opaque michael@0: return nsRegion(); michael@0: } michael@0: michael@0: // nsDisplayMixBlendMode uses layers for rendering michael@0: already_AddRefed michael@0: nsDisplayMixBlendMode::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: ContainerLayerParameters newContainerParameters = aContainerParameters; michael@0: newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; michael@0: michael@0: nsRefPtr container = aManager->GetLayerBuilder()-> michael@0: BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, michael@0: newContainerParameters, nullptr); michael@0: if (!container) { michael@0: return nullptr; michael@0: } michael@0: michael@0: container->DeprecatedSetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode)); michael@0: michael@0: return container.forget(); michael@0: } michael@0: michael@0: bool nsDisplayMixBlendMode::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: // Our children are need their backdrop so we should not allow them to subtract michael@0: // area from aVisibleRegion. We do need to find out what is visible under michael@0: // our children in the temporary compositing buffer, because if our children michael@0: // paint our entire bounds opaquely then we don't need an alpha channel in michael@0: // the temporary compositing buffer. michael@0: nsRect bounds = GetClippedBounds(aBuilder); michael@0: nsRegion visibleUnderChildren; michael@0: visibleUnderChildren.And(*aVisibleRegion, bounds); michael@0: nsRect allowExpansion = bounds.Intersect(aAllowVisibleRegionExpansion); michael@0: return michael@0: nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren, michael@0: allowExpansion); michael@0: } michael@0: michael@0: bool nsDisplayMixBlendMode::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { michael@0: if (aItem->GetType() != TYPE_MIX_BLEND_MODE) michael@0: return false; michael@0: // items for the same content element should be merged into a single michael@0: // compositing group michael@0: // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity michael@0: if (aItem->Frame()->GetContent() != mFrame->GetContent()) michael@0: return false; michael@0: if (aItem->GetClip() != GetClip()) michael@0: return false; michael@0: MergeFromTrackingMergedFrames(static_cast(aItem)); michael@0: return true; michael@0: } michael@0: michael@0: nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList, michael@0: uint32_t aFlags) michael@0: : nsDisplayWrapList(aBuilder, aFrame, aList) { michael@0: MOZ_COUNT_CTOR(nsDisplayBlendContainer); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayBlendContainer::~nsDisplayBlendContainer() { michael@0: MOZ_COUNT_DTOR(nsDisplayBlendContainer); michael@0: } michael@0: #endif michael@0: michael@0: // nsDisplayBlendContainer uses layers for rendering michael@0: already_AddRefed michael@0: nsDisplayBlendContainer::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: // turn off anti-aliasing in the parent stacking context because it changes michael@0: // how the group is initialized. michael@0: ContainerLayerParameters newContainerParameters = aContainerParameters; michael@0: newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; michael@0: michael@0: nsRefPtr container = aManager->GetLayerBuilder()-> michael@0: BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, michael@0: newContainerParameters, nullptr); michael@0: if (!container) { michael@0: return nullptr; michael@0: } michael@0: michael@0: container->SetForceIsolatedGroup(true); michael@0: return container.forget(); michael@0: } michael@0: michael@0: bool nsDisplayBlendContainer::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { michael@0: if (aItem->GetType() != TYPE_BLEND_CONTAINER) michael@0: return false; michael@0: // items for the same content element should be merged into a single michael@0: // compositing group michael@0: // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity michael@0: if (aItem->Frame()->GetContent() != mFrame->GetContent()) michael@0: return false; michael@0: if (aItem->GetClip() != GetClip()) michael@0: return false; michael@0: MergeFromTrackingMergedFrames(static_cast(aItem)); michael@0: return true; michael@0: } michael@0: michael@0: nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList, michael@0: uint32_t aFlags, ViewID aScrollTarget) michael@0: : nsDisplayWrapList(aBuilder, aFrame, aList) michael@0: , mFlags(aFlags) michael@0: , mScrollTarget(aScrollTarget) { michael@0: MOZ_COUNT_CTOR(nsDisplayOwnLayer); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayOwnLayer::~nsDisplayOwnLayer() { michael@0: MOZ_COUNT_DTOR(nsDisplayOwnLayer); michael@0: } michael@0: #endif michael@0: michael@0: // nsDisplayOpacity uses layers for rendering michael@0: already_AddRefed michael@0: nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: nsRefPtr layer = aManager->GetLayerBuilder()-> michael@0: BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, michael@0: aContainerParameters, nullptr); michael@0: if (mFlags & VERTICAL_SCROLLBAR) { michael@0: layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::VERTICAL); michael@0: } michael@0: if (mFlags & HORIZONTAL_SCROLLBAR) { michael@0: layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::HORIZONTAL); michael@0: } michael@0: michael@0: if (mFlags & GENERATE_SUBDOC_INVALIDATIONS) { michael@0: mFrame->PresContext()->SetNotifySubDocInvalidationData(layer); michael@0: } michael@0: return layer.forget(); michael@0: } michael@0: michael@0: nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList, michael@0: uint32_t aFlags) michael@0: : nsDisplayOwnLayer(aBuilder, aFrame, aList, aFlags) michael@0: , mScrollParentId(aBuilder->GetCurrentScrollParentId()) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplaySubDocument); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplaySubDocument::~nsDisplaySubDocument() { michael@0: MOZ_COUNT_DTOR(nsDisplaySubDocument); michael@0: } michael@0: #endif michael@0: michael@0: already_AddRefed michael@0: nsDisplaySubDocument::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: nsRefPtr layer = nsDisplayOwnLayer::BuildLayer( michael@0: aBuilder, aManager, aContainerParameters); michael@0: michael@0: if (!(mFlags & GENERATE_SCROLLABLE_LAYER)) { michael@0: return layer.forget(); michael@0: } michael@0: michael@0: NS_ASSERTION(layer->AsContainerLayer(), "nsDisplayOwnLayer should have made a ContainerLayer"); michael@0: if (ContainerLayer* container = layer->AsContainerLayer()) { michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame(); michael@0: bool isRootContentDocument = presContext->IsRootContentDocument(); michael@0: michael@0: bool usingDisplayport = false; michael@0: bool usingCriticalDisplayport = false; michael@0: nsRect displayport, criticalDisplayport; michael@0: ViewID scrollId = FrameMetrics::NULL_SCROLL_ID; michael@0: if (rootScrollFrame) { michael@0: nsIContent* content = rootScrollFrame->GetContent(); michael@0: if (content) { michael@0: usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport); michael@0: usingCriticalDisplayport = michael@0: nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport); michael@0: michael@0: if (isRootContentDocument) { michael@0: scrollId = nsLayoutUtils::FindOrCreateIDFor(content); michael@0: } else { michael@0: nsLayoutUtils::FindIDFor(content, &scrollId); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsRect viewport = mFrame->GetRect() - michael@0: mFrame->GetPosition() + michael@0: mFrame->GetOffsetToCrossDoc(ReferenceFrame()); michael@0: michael@0: container->SetScrollHandoffParentId(mScrollParentId); michael@0: RecordFrameMetrics(mFrame, rootScrollFrame, ReferenceFrame(), michael@0: container, mList.GetVisibleRect(), viewport, michael@0: (usingDisplayport ? &displayport : nullptr), michael@0: (usingCriticalDisplayport ? &criticalDisplayport : nullptr), michael@0: scrollId, isRootContentDocument, aContainerParameters); michael@0: } michael@0: michael@0: return layer.forget(); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplaySubDocument::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) michael@0: { michael@0: bool usingDisplayPort = michael@0: nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); michael@0: michael@0: if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { michael@0: *aSnap = false; michael@0: return mFrame->GetRect() + aBuilder->ToReferenceFrame(mFrame); michael@0: } michael@0: michael@0: return nsDisplayOwnLayer::GetBounds(aBuilder, aSnap); michael@0: } michael@0: michael@0: bool michael@0: nsDisplaySubDocument::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) michael@0: { michael@0: nsRect displayport; michael@0: bool usingDisplayPort = michael@0: nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext(), &displayport); michael@0: michael@0: if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) { michael@0: return nsDisplayWrapList::ComputeVisibility(aBuilder, aVisibleRegion, michael@0: aAllowVisibleRegionExpansion); michael@0: } michael@0: michael@0: nsRegion childVisibleRegion; michael@0: // The visible region for the children may be much bigger than the hole we michael@0: // are viewing the children from, so that the compositor process has enough michael@0: // content to asynchronously pan while content is being refreshed. michael@0: childVisibleRegion = displayport + mFrame->GetOffsetToCrossDoc(ReferenceFrame()); michael@0: michael@0: nsRect boundedRect = michael@0: childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder)); michael@0: nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion); michael@0: bool visible = mList.ComputeVisibilityForSublist( michael@0: aBuilder, &childVisibleRegion, boundedRect, allowExpansion, michael@0: usingDisplayPort ? mFrame : nullptr); michael@0: // We don't allow this computation to influence aVisibleRegion, on the michael@0: // assumption that the layer can be asynchronously scrolled so we'll michael@0: // definitely need all the content under it. michael@0: michael@0: return visible; michael@0: } michael@0: michael@0: bool michael@0: nsDisplaySubDocument::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: bool usingDisplayPort = michael@0: nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); michael@0: michael@0: if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { michael@0: return true; michael@0: } michael@0: michael@0: return nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(aBuilder); michael@0: } michael@0: michael@0: nsRegion michael@0: nsDisplaySubDocument::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) michael@0: { michael@0: bool usingDisplayPort = michael@0: nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); michael@0: michael@0: if ((mFlags & GENERATE_SCROLLABLE_LAYER) && usingDisplayPort) { michael@0: *aSnap = false; michael@0: return nsRegion(); michael@0: } michael@0: michael@0: return nsDisplayOwnLayer::GetOpaqueRegion(aBuilder, aSnap); michael@0: } michael@0: michael@0: nsDisplayResolution::nsDisplayResolution(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList, michael@0: uint32_t aFlags) michael@0: : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags) { michael@0: MOZ_COUNT_CTOR(nsDisplayResolution); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayResolution::~nsDisplayResolution() { michael@0: MOZ_COUNT_DTOR(nsDisplayResolution); michael@0: } michael@0: #endif michael@0: michael@0: already_AddRefed michael@0: nsDisplayResolution::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: nsIPresShell* presShell = mFrame->PresContext()->PresShell(); michael@0: ContainerLayerParameters containerParameters( michael@0: presShell->GetXResolution(), presShell->GetYResolution(), nsIntPoint(), michael@0: aContainerParameters); michael@0: michael@0: nsRefPtr layer = nsDisplaySubDocument::BuildLayer( michael@0: aBuilder, aManager, containerParameters); michael@0: layer->SetPostScale(1.0f / presShell->GetXResolution(), michael@0: 1.0f / presShell->GetYResolution()); michael@0: return layer.forget(); michael@0: } michael@0: michael@0: nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, michael@0: nsDisplayList* aList) michael@0: : nsDisplayOwnLayer(aBuilder, aFrame, aList) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayStickyPosition); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayStickyPosition::~nsDisplayStickyPosition() { michael@0: MOZ_COUNT_DTOR(nsDisplayStickyPosition); michael@0: } michael@0: #endif michael@0: michael@0: already_AddRefed michael@0: nsDisplayStickyPosition::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: nsRefPtr layer = michael@0: nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters); michael@0: michael@0: StickyScrollContainer* stickyScrollContainer = StickyScrollContainer:: michael@0: GetStickyScrollContainerForFrame(mFrame); michael@0: if (!stickyScrollContainer) { michael@0: return layer.forget(); michael@0: } michael@0: michael@0: nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame()); michael@0: nsPresContext* presContext = scrollFrame->PresContext(); michael@0: michael@0: // Sticky position frames whose scroll frame is the root scroll frame are michael@0: // reflowed into the scroll-port size if one has been set. michael@0: nsSize scrollFrameSize = scrollFrame->GetSize(); michael@0: if (scrollFrame == presContext->PresShell()->GetRootScrollFrame() && michael@0: presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) { michael@0: scrollFrameSize = presContext->PresShell()-> michael@0: GetScrollPositionClampingScrollPortSize(); michael@0: } michael@0: michael@0: nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame, michael@0: nsRect(scrollFrame->GetOffsetToCrossDoc(ReferenceFrame()), scrollFrameSize), michael@0: mFrame, presContext, aContainerParameters); michael@0: michael@0: ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor( michael@0: stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent()); michael@0: michael@0: float factor = presContext->AppUnitsPerDevPixel(); michael@0: nsRect outer; michael@0: nsRect inner; michael@0: stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner); michael@0: LayerRect stickyOuter(NSAppUnitsToFloatPixels(outer.x, factor) * michael@0: aContainerParameters.mXScale, michael@0: NSAppUnitsToFloatPixels(outer.y, factor) * michael@0: aContainerParameters.mYScale, michael@0: NSAppUnitsToFloatPixels(outer.width, factor) * michael@0: aContainerParameters.mXScale, michael@0: NSAppUnitsToFloatPixels(outer.height, factor) * michael@0: aContainerParameters.mYScale); michael@0: LayerRect stickyInner(NSAppUnitsToFloatPixels(inner.x, factor) * michael@0: aContainerParameters.mXScale, michael@0: NSAppUnitsToFloatPixels(inner.y, factor) * michael@0: aContainerParameters.mYScale, michael@0: NSAppUnitsToFloatPixels(inner.width, factor) * michael@0: aContainerParameters.mXScale, michael@0: NSAppUnitsToFloatPixels(inner.height, factor) * michael@0: aContainerParameters.mYScale); michael@0: layer->SetStickyPositionData(scrollId, stickyOuter, stickyInner); michael@0: michael@0: return layer.forget(); michael@0: } michael@0: michael@0: bool nsDisplayStickyPosition::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { michael@0: if (aItem->GetType() != TYPE_STICKY_POSITION) michael@0: return false; michael@0: // Items with the same fixed position frame can be merged. michael@0: nsDisplayStickyPosition* other = static_cast(aItem); michael@0: if (other->mFrame != mFrame) michael@0: return false; michael@0: if (aItem->GetClip() != GetClip()) michael@0: return false; michael@0: MergeFromTrackingMergedFrames(other); michael@0: return true; michael@0: } michael@0: michael@0: nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayList* aList, michael@0: nsIFrame* aForFrame, michael@0: nsIFrame* aScrolledFrame, michael@0: nsIFrame* aScrollFrame) michael@0: : nsDisplayWrapList(aBuilder, aForFrame, aList) michael@0: , mScrollFrame(aScrollFrame) michael@0: , mScrolledFrame(aScrolledFrame) michael@0: , mScrollParentId(aBuilder->GetCurrentScrollParentId()) michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: MOZ_COUNT_CTOR(nsDisplayScrollLayer); michael@0: #endif michael@0: michael@0: NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), michael@0: "Need a child frame with content"); michael@0: } michael@0: michael@0: nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem, michael@0: nsIFrame* aForFrame, michael@0: nsIFrame* aScrolledFrame, michael@0: nsIFrame* aScrollFrame) michael@0: : nsDisplayWrapList(aBuilder, aForFrame, aItem) michael@0: , mScrollFrame(aScrollFrame) michael@0: , mScrolledFrame(aScrolledFrame) michael@0: , mScrollParentId(aBuilder->GetCurrentScrollParentId()) michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: MOZ_COUNT_CTOR(nsDisplayScrollLayer); michael@0: #endif michael@0: michael@0: NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), michael@0: "Need a child frame with content"); michael@0: } michael@0: michael@0: nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aForFrame, michael@0: nsIFrame* aScrolledFrame, michael@0: nsIFrame* aScrollFrame) michael@0: : nsDisplayWrapList(aBuilder, aForFrame) michael@0: , mScrollFrame(aScrollFrame) michael@0: , mScrolledFrame(aScrolledFrame) michael@0: , mScrollParentId(aBuilder->GetCurrentScrollParentId()) michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: MOZ_COUNT_CTOR(nsDisplayScrollLayer); michael@0: #endif michael@0: michael@0: NS_ASSERTION(mScrolledFrame && mScrolledFrame->GetContent(), michael@0: "Need a child frame with content"); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayScrollLayer::~nsDisplayScrollLayer() michael@0: { michael@0: MOZ_COUNT_DTOR(nsDisplayScrollLayer); michael@0: } michael@0: #endif michael@0: michael@0: nsRect michael@0: nsDisplayScrollLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) michael@0: { michael@0: nsIScrollableFrame* sf = do_QueryFrame(mScrollFrame); michael@0: if (sf) { michael@0: *aSnap = false; michael@0: return sf->GetScrollPortRect() + aBuilder->ToReferenceFrame(mScrollFrame); michael@0: } michael@0: return nsDisplayWrapList::GetBounds(aBuilder, aSnap); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) { michael@0: nsRefPtr layer = aManager->GetLayerBuilder()-> michael@0: BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, michael@0: aContainerParameters, nullptr); michael@0: michael@0: // Get the already set unique ID for scrolling this content remotely. michael@0: // Or, if not set, generate a new ID. michael@0: nsIContent* content = mScrolledFrame->GetContent(); michael@0: ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(content); michael@0: michael@0: nsRect viewport = mScrollFrame->GetRect() - michael@0: mScrollFrame->GetPosition() + michael@0: mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame()); michael@0: michael@0: bool usingDisplayport = false; michael@0: bool usingCriticalDisplayport = false; michael@0: nsRect displayport, criticalDisplayport; michael@0: if (content) { michael@0: usingDisplayport = nsLayoutUtils::GetDisplayPort(content, &displayport); michael@0: usingCriticalDisplayport = michael@0: nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport); michael@0: } michael@0: layer->SetScrollHandoffParentId(mScrollParentId); michael@0: RecordFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), layer, michael@0: mList.GetVisibleRect(), viewport, michael@0: (usingDisplayport ? &displayport : nullptr), michael@0: (usingCriticalDisplayport ? &criticalDisplayport : nullptr), michael@0: scrollId, false, aContainerParameters); michael@0: michael@0: return layer.forget(); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayScrollLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: if (nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), nullptr)) { michael@0: return true; michael@0: } michael@0: michael@0: return nsDisplayWrapList::ShouldBuildLayerEvenIfInvisible(aBuilder); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) michael@0: { michael@0: nsRect displayport; michael@0: bool usingDisplayPort = michael@0: nsLayoutUtils::GetDisplayPort(mScrolledFrame->GetContent(), &displayport); michael@0: nsRegion childVisibleRegion; michael@0: if (usingDisplayPort) { michael@0: // The visible region for the children may be much bigger than the hole we michael@0: // are viewing the children from, so that the compositor process has enough michael@0: // content to asynchronously pan while content is being refreshed. michael@0: childVisibleRegion = displayport + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame()); michael@0: } else { michael@0: bool snap; michael@0: childVisibleRegion = GetBounds(aBuilder, &snap); michael@0: } michael@0: michael@0: nsRect boundedRect = michael@0: childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder)); michael@0: nsRect allowExpansion = boundedRect.Intersect(aAllowVisibleRegionExpansion); michael@0: bool visible = mList.ComputeVisibilityForSublist( michael@0: aBuilder, &childVisibleRegion, boundedRect, allowExpansion, michael@0: usingDisplayPort ? mScrollFrame : nullptr); michael@0: // We don't allow this computation to influence aVisibleRegion, on the michael@0: // assumption that the layer can be asynchronously scrolled so we'll michael@0: // definitely need all the content under it. michael@0: michael@0: return visible; michael@0: } michael@0: michael@0: LayerState michael@0: nsDisplayScrollLayer::GetLayerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) michael@0: { michael@0: // Force this as a layer so we can scroll asynchronously. michael@0: // This causes incorrect rendering for rounded clips! michael@0: return LAYER_ACTIVE_FORCE; michael@0: } michael@0: michael@0: // Check if we are going to clip an abs pos item that we don't contain. michael@0: // Root scroll frames clip all their descendants, so we don't need to worry michael@0: // about them. michael@0: bool michael@0: WouldCauseIncorrectClippingOnAbsPosItem(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayScrollLayer* aItem) michael@0: { michael@0: nsIFrame* scrollFrame = aItem->GetScrollFrame(); michael@0: nsIPresShell* presShell = scrollFrame->PresContext()->PresShell(); michael@0: if (scrollFrame == presShell->GetRootScrollFrame()) { michael@0: return false; michael@0: } michael@0: nsIFrame* scrolledFrame = aItem->GetScrolledFrame(); michael@0: nsIFrame* frame = aItem->Frame(); michael@0: if (frame == scrolledFrame || !frame->IsAbsolutelyPositioned() || michael@0: nsLayoutUtils::IsAncestorFrameCrossDoc(scrollFrame, frame, presShell->GetRootFrame())) { michael@0: return false; michael@0: } michael@0: if (!aItem->GetClip().IsRectAffectedByClip(aItem->GetChildren()->GetBounds(aBuilder))) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayScrollLayer::TryMerge(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem) michael@0: { michael@0: if (aItem->GetType() != TYPE_SCROLL_LAYER) { michael@0: return false; michael@0: } michael@0: nsDisplayScrollLayer* other = static_cast(aItem); michael@0: if (other->mScrolledFrame != this->mScrolledFrame) { michael@0: return false; michael@0: } michael@0: if (aItem->GetClip() != GetClip()) { michael@0: return false; michael@0: } michael@0: michael@0: if (WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this) || michael@0: WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, other)) { michael@0: return false; michael@0: } michael@0: michael@0: NS_ASSERTION(other->mReferenceFrame == mReferenceFrame, michael@0: "Must have the same reference frame!"); michael@0: michael@0: FrameProperties props = mScrolledFrame->Properties(); michael@0: props.Set(nsIFrame::ScrollLayerCount(), michael@0: reinterpret_cast(GetScrollLayerCount() - 1)); michael@0: michael@0: // Swap frames with the other item before doing MergeFrom. michael@0: // XXX - This ensures that the frame associated with a scroll layer after michael@0: // merging is the first, rather than the last. This tends to change less, michael@0: // ensuring we're more likely to retain the associated gfx layer. michael@0: // See Bug 729534 and Bug 731641. michael@0: nsIFrame* tmp = mFrame; michael@0: mFrame = other->mFrame; michael@0: other->mFrame = tmp; michael@0: MergeFromTrackingMergedFrames(other); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: PropagateClip(nsDisplayListBuilder* aBuilder, const DisplayItemClip& aClip, michael@0: nsDisplayList* aList) michael@0: { michael@0: for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) { michael@0: DisplayItemClip clip(i->GetClip()); michael@0: clip.IntersectWith(aClip); michael@0: i->SetClip(aBuilder, clip); michael@0: nsDisplayList* list = i->GetSameCoordinateSystemChildren(); michael@0: if (list) { michael@0: PropagateClip(aBuilder, aClip, list); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsDisplayScrollLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: bool badAbsPosClip = WouldCauseIncorrectClippingOnAbsPosItem(aBuilder, this); michael@0: if (GetScrollLayerCount() > 1 || badAbsPosClip) { michael@0: // Propagate our clip to our children. The clip for the scroll frame is michael@0: // on this item, but not our child items so that they can draw non-visible michael@0: // parts of the display port. But if we are flattening we failed and can't michael@0: // draw the extra content, so it needs to be clipped. michael@0: // But don't induce our clip on abs pos frames that we shouldn't be clipping. michael@0: if (!badAbsPosClip) { michael@0: PropagateClip(aBuilder, GetClip(), &mList); michael@0: } michael@0: return true; michael@0: } michael@0: if (mFrame != mScrolledFrame) { michael@0: mMergedFrames.AppendElement(mFrame); michael@0: mFrame = mScrolledFrame; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: intptr_t michael@0: nsDisplayScrollLayer::GetScrollLayerCount() michael@0: { michael@0: FrameProperties props = mScrolledFrame->Properties(); michael@0: #ifdef DEBUG michael@0: bool hasCount = false; michael@0: intptr_t result = reinterpret_cast( michael@0: props.Get(nsIFrame::ScrollLayerCount(), &hasCount)); michael@0: // If this aborts, then the property was either not added before scroll michael@0: // layers were created or the property was deleted to early. If the latter, michael@0: // make sure that nsDisplayScrollInfoLayer is on the bottom of the list so michael@0: // that it is processed last. michael@0: NS_ABORT_IF_FALSE(hasCount, "nsDisplayScrollLayer should always be defined"); michael@0: return result; michael@0: #else michael@0: return reinterpret_cast(props.Get(nsIFrame::ScrollLayerCount())); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: nsDisplayScrollLayer::WriteDebugInfo(nsACString& aTo) michael@0: { michael@0: aTo += nsPrintfCString(" (scrollframe %p scrolledframe %p)", michael@0: mScrollFrame, mScrolledFrame); michael@0: } michael@0: #endif michael@0: michael@0: nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer( michael@0: nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aScrolledFrame, michael@0: nsIFrame* aScrollFrame) michael@0: : nsDisplayScrollLayer(aBuilder, aScrollFrame, aScrolledFrame, aScrollFrame) michael@0: { michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer); michael@0: #endif michael@0: } michael@0: michael@0: nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer() michael@0: { michael@0: FrameProperties props = mScrolledFrame->Properties(); michael@0: props.Remove(nsIFrame::ScrollLayerCount()); michael@0: MOZ_COUNT_DTOR(nsDisplayScrollInfoLayer); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayScrollInfoLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) michael@0: { michael@0: return nsDisplayWrapList::GetBounds(aBuilder, aSnap); michael@0: } michael@0: michael@0: LayerState michael@0: nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) michael@0: { michael@0: return LAYER_ACTIVE_EMPTY; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayScrollInfoLayer::TryMerge(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayScrollInfoLayer::ShouldFlattenAway(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: // Layer metadata for a particular scroll frame needs to be unique. Only michael@0: // one nsDisplayScrollLayer (with rendered content) or one michael@0: // nsDisplayScrollInfoLayer (with only the metadata) should survive the michael@0: // visibility computation. michael@0: return GetScrollLayerCount() == 1; michael@0: } michael@0: michael@0: nsDisplayZoom::nsDisplayZoom(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList, michael@0: int32_t aAPD, int32_t aParentAPD, michael@0: uint32_t aFlags) michael@0: : nsDisplaySubDocument(aBuilder, aFrame, aList, aFlags) michael@0: , mAPD(aAPD), mParentAPD(aParentAPD) { michael@0: MOZ_COUNT_CTOR(nsDisplayZoom); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplayZoom::~nsDisplayZoom() { michael@0: MOZ_COUNT_DTOR(nsDisplayZoom); michael@0: } michael@0: #endif michael@0: michael@0: nsRect nsDisplayZoom::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) michael@0: { michael@0: nsRect bounds = nsDisplaySubDocument::GetBounds(aBuilder, aSnap); michael@0: *aSnap = false; michael@0: return bounds.ConvertAppUnitsRoundOut(mAPD, mParentAPD); michael@0: } michael@0: michael@0: void nsDisplayZoom::HitTest(nsDisplayListBuilder *aBuilder, michael@0: const nsRect& aRect, michael@0: HitTestState *aState, michael@0: nsTArray *aOutFrames) michael@0: { michael@0: nsRect rect; michael@0: // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1 michael@0: // rect as well instead of possibly rounding the width or height to zero. michael@0: if (aRect.width == 1 && aRect.height == 1) { michael@0: rect.MoveTo(aRect.TopLeft().ConvertAppUnits(mParentAPD, mAPD)); michael@0: rect.width = rect.height = 1; michael@0: } else { michael@0: rect = aRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD); michael@0: } michael@0: mList.HitTest(aBuilder, rect, aState, aOutFrames); michael@0: } michael@0: michael@0: void nsDisplayZoom::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: mList.PaintForFrame(aBuilder, aCtx, mFrame, nsDisplayList::PAINT_DEFAULT); michael@0: } michael@0: michael@0: bool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder, michael@0: nsRegion *aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) michael@0: { michael@0: // Convert the passed in visible region to our appunits. michael@0: nsRegion visibleRegion; michael@0: // mVisibleRect has been clipped to GetClippedBounds michael@0: visibleRegion.And(*aVisibleRegion, mVisibleRect); michael@0: visibleRegion = visibleRegion.ConvertAppUnitsRoundOut(mParentAPD, mAPD); michael@0: nsRegion originalVisibleRegion = visibleRegion; michael@0: michael@0: nsRect transformedVisibleRect = michael@0: mVisibleRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD); michael@0: nsRect allowExpansion = michael@0: aAllowVisibleRegionExpansion.ConvertAppUnitsRoundIn(mParentAPD, mAPD); michael@0: bool retval; michael@0: // If we are to generate a scrollable layer we call michael@0: // nsDisplaySubDocument::ComputeVisibility to make the necessary adjustments michael@0: // for ComputeVisibility, it does all it's calculations in the child APD. michael@0: bool usingDisplayPort = michael@0: nsLayoutUtils::ViewportHasDisplayPort(mFrame->PresContext()); michael@0: if (!(mFlags & GENERATE_SCROLLABLE_LAYER) || !usingDisplayPort) { michael@0: retval = michael@0: mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion, michael@0: transformedVisibleRect, michael@0: allowExpansion); michael@0: } else { michael@0: retval = michael@0: nsDisplaySubDocument::ComputeVisibility(aBuilder, &visibleRegion, michael@0: allowExpansion); michael@0: } michael@0: michael@0: nsRegion removed; michael@0: // removed = originalVisibleRegion - visibleRegion michael@0: removed.Sub(originalVisibleRegion, visibleRegion); michael@0: // Convert removed region to parent appunits. michael@0: removed = removed.ConvertAppUnitsRoundIn(mAPD, mParentAPD); michael@0: // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications michael@0: // SubtractFromVisibleRegion does) michael@0: aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////// michael@0: // nsDisplayTransform Implementation michael@0: // michael@0: michael@0: // Write #define UNIFIED_CONTINUATIONS here to have the transform property try michael@0: // to transform content with continuations as one unified block instead of michael@0: // several smaller ones. This is currently disabled because it doesn't work michael@0: // correctly, since when the frames are initially being reflowed, their michael@0: // continuations all compute their bounding rects independently of each other michael@0: // and consequently get the wrong value. Write #define DEBUG_HIT here to have michael@0: // the nsDisplayTransform class dump out a bunch of information about hit michael@0: // detection. michael@0: #undef UNIFIED_CONTINUATIONS michael@0: #undef DEBUG_HIT michael@0: michael@0: /* Returns the bounds of a frame as defined for transforms. If michael@0: * UNIFIED_CONTINUATIONS is not defined, this is simply the frame's bounding michael@0: * rectangle, translated to the origin. Otherwise, returns the smallest michael@0: * rectangle containing a frame and all of its continuations. For example, if michael@0: * there is a element with several continuations split over several michael@0: * lines, this function will return the rectangle containing all of those michael@0: * continuations. This rectangle is relative to the origin of the frame's local michael@0: * coordinate space. michael@0: */ michael@0: #ifndef UNIFIED_CONTINUATIONS michael@0: michael@0: nsRect michael@0: nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!"); michael@0: michael@0: if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { michael@0: // TODO: SVG needs to define what percentage translations resolve against. michael@0: return nsRect(); michael@0: } michael@0: michael@0: return nsRect(nsPoint(0, 0), aFrame->GetSize()); michael@0: } michael@0: michael@0: #else michael@0: michael@0: nsRect michael@0: nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!"); michael@0: michael@0: nsRect result; michael@0: michael@0: if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { michael@0: // TODO: SVG needs to define what percentage translations resolve against. michael@0: return result; michael@0: } michael@0: michael@0: /* Iterate through the continuation list, unioning together all the michael@0: * bounding rects. michael@0: */ michael@0: for (const nsIFrame *currFrame = aFrame->FirstContinuation(); michael@0: currFrame != nullptr; michael@0: currFrame = currFrame->GetNextContinuation()) michael@0: { michael@0: /* Get the frame rect in local coordinates, then translate back to the michael@0: * original coordinates. michael@0: */ michael@0: result.UnionRect(result, nsRect(currFrame->GetOffsetTo(aFrame), michael@0: currFrame->GetSize())); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, michael@0: nsDisplayList *aList, ComputeTransformFunction aTransformGetter, michael@0: uint32_t aIndex) michael@0: : nsDisplayItem(aBuilder, aFrame) michael@0: , mStoredList(aBuilder, aFrame, aList) michael@0: , mTransformGetter(aTransformGetter) michael@0: , mIndex(aIndex) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayTransform); michael@0: NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); michael@0: NS_ABORT_IF_FALSE(!aFrame->IsTransformed(), "Can't specify a transform getter for a transformed frame!"); michael@0: mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip()); michael@0: } michael@0: michael@0: nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, michael@0: nsDisplayList *aList, uint32_t aIndex) michael@0: : nsDisplayItem(aBuilder, aFrame) michael@0: , mStoredList(aBuilder, aFrame, aList) michael@0: , mTransformGetter(nullptr) michael@0: , mIndex(aIndex) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayTransform); michael@0: NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); michael@0: mReferenceFrame = michael@0: aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); michael@0: mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); michael@0: mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip()); michael@0: } michael@0: michael@0: nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, michael@0: nsDisplayItem *aItem, uint32_t aIndex) michael@0: : nsDisplayItem(aBuilder, aFrame) michael@0: , mStoredList(aBuilder, aFrame, aItem) michael@0: , mTransformGetter(nullptr) michael@0: , mIndex(aIndex) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplayTransform); michael@0: NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); michael@0: mReferenceFrame = michael@0: aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame)); michael@0: mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame); michael@0: mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip()); michael@0: } michael@0: michael@0: /* Returns the delta specified by the -moz-transform-origin property. michael@0: * This is a positive delta, meaning that it indicates the direction to move michael@0: * to get from (0, 0) of the frame to the transform origin. This function is michael@0: * called off the main thread. michael@0: */ michael@0: /* static */ gfxPoint3D michael@0: nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame, michael@0: float aAppUnitsPerPixel, michael@0: const nsRect* aBoundsOverride) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Can't get delta for a null frame!"); michael@0: NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(), michael@0: "Shouldn't get a delta for an untransformed frame!"); michael@0: michael@0: if (!aFrame->IsTransformed()) { michael@0: return gfxPoint3D(); michael@0: } michael@0: michael@0: /* For both of the coordinates, if the value of -moz-transform is a michael@0: * percentage, it's relative to the size of the frame. Otherwise, if it's michael@0: * a distance, it's already computed for us! michael@0: */ michael@0: const nsStyleDisplay* display = aFrame->StyleDisplay(); michael@0: nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride : michael@0: nsDisplayTransform::GetFrameBoundsForTransform(aFrame)); michael@0: michael@0: /* Allows us to access named variables by index. */ michael@0: float coords[3]; michael@0: const nscoord* dimensions[2] = michael@0: {&boundingRect.width, &boundingRect.height}; michael@0: michael@0: for (uint8_t index = 0; index < 2; ++index) { michael@0: /* If the -moz-transform-origin specifies a percentage, take the percentage michael@0: * of the size of the box. michael@0: */ michael@0: const nsStyleCoord &coord = display->mTransformOrigin[index]; michael@0: if (coord.GetUnit() == eStyleUnit_Calc) { michael@0: const nsStyleCoord::Calc *calc = coord.GetCalcValue(); michael@0: coords[index] = michael@0: NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * michael@0: calc->mPercent + michael@0: NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); michael@0: } else if (coord.GetUnit() == eStyleUnit_Percent) { michael@0: coords[index] = michael@0: NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * michael@0: coord.GetPercentValue(); michael@0: } else { michael@0: NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); michael@0: coords[index] = michael@0: NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); michael@0: } michael@0: if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) && michael@0: coord.GetUnit() != eStyleUnit_Percent) { michael@0: // values represent offsets from the origin of the SVG element's michael@0: // user space, not the top left of its bounds, so we must adjust for that: michael@0: nscoord offset = michael@0: (index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y; michael@0: coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel); michael@0: } michael@0: } michael@0: michael@0: coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), michael@0: aAppUnitsPerPixel); michael@0: /* Adjust based on the origin of the rectangle. */ michael@0: coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel); michael@0: coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel); michael@0: michael@0: return gfxPoint3D(coords[0], coords[1], coords[2]); michael@0: } michael@0: michael@0: /* Returns the delta specified by the -moz-perspective-origin property. michael@0: * This is a positive delta, meaning that it indicates the direction to move michael@0: * to get from (0, 0) of the frame to the perspective origin. This function is michael@0: * called off the main thread. michael@0: */ michael@0: /* static */ gfxPoint3D michael@0: nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame, michael@0: float aAppUnitsPerPixel) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Can't get delta for a null frame!"); michael@0: NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(), michael@0: "Shouldn't get a delta for an untransformed frame!"); michael@0: michael@0: if (!aFrame->IsTransformed()) { michael@0: return gfxPoint3D(); michael@0: } michael@0: michael@0: /* For both of the coordinates, if the value of -moz-perspective-origin is a michael@0: * percentage, it's relative to the size of the frame. Otherwise, if it's michael@0: * a distance, it's already computed for us! michael@0: */ michael@0: michael@0: //TODO: Should this be using our bounds or the parent's bounds? michael@0: // How do we handle aBoundsOverride in the latter case? michael@0: nsIFrame* parent = aFrame->GetParentStyleContextFrame(); michael@0: if (!parent) { michael@0: return gfxPoint3D(); michael@0: } michael@0: const nsStyleDisplay* display = parent->StyleDisplay(); michael@0: nsRect boundingRect = nsDisplayTransform::GetFrameBoundsForTransform(parent); michael@0: michael@0: /* Allows us to access named variables by index. */ michael@0: gfxPoint3D result; michael@0: result.z = 0.0f; michael@0: gfxFloat* coords[2] = {&result.x, &result.y}; michael@0: const nscoord* dimensions[2] = michael@0: {&boundingRect.width, &boundingRect.height}; michael@0: michael@0: for (uint8_t index = 0; index < 2; ++index) { michael@0: /* If the -moz-transform-origin specifies a percentage, take the percentage michael@0: * of the size of the box. michael@0: */ michael@0: const nsStyleCoord &coord = display->mPerspectiveOrigin[index]; michael@0: if (coord.GetUnit() == eStyleUnit_Calc) { michael@0: const nsStyleCoord::Calc *calc = coord.GetCalcValue(); michael@0: *coords[index] = michael@0: NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * michael@0: calc->mPercent + michael@0: NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); michael@0: } else if (coord.GetUnit() == eStyleUnit_Percent) { michael@0: *coords[index] = michael@0: NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * michael@0: coord.GetPercentValue(); michael@0: } else { michael@0: NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); michael@0: *coords[index] = michael@0: NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); michael@0: } michael@0: } michael@0: michael@0: nsPoint parentOffset = aFrame->GetOffsetTo(parent); michael@0: gfxPoint3D gfxOffset( michael@0: NSAppUnitsToFloatPixels(parentOffset.x, aAppUnitsPerPixel), michael@0: NSAppUnitsToFloatPixels(parentOffset.y, aAppUnitsPerPixel), michael@0: 0.0f); michael@0: michael@0: return result - gfxOffset; michael@0: } michael@0: michael@0: nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame, michael@0: float aAppUnitsPerPixel, michael@0: const nsRect* aBoundsOverride) michael@0: : mFrame(aFrame) michael@0: , mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform) michael@0: , mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride)) michael@0: , mToPerspectiveOrigin(GetDeltaToPerspectiveOrigin(aFrame, aAppUnitsPerPixel)) michael@0: , mChildPerspective(0) michael@0: { michael@0: const nsStyleDisplay* parentDisp = nullptr; michael@0: nsStyleContext* parentStyleContext = aFrame->StyleContext()->GetParent(); michael@0: if (parentStyleContext) { michael@0: parentDisp = parentStyleContext->StyleDisplay(); michael@0: } michael@0: if (parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord) { michael@0: mChildPerspective = parentDisp->mChildPerspective.GetCoordValue(); michael@0: } michael@0: } michael@0: michael@0: /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that michael@0: * translates from local coordinate space to transform coordinate space, then michael@0: * hands it back. michael@0: */ michael@0: gfx3DMatrix michael@0: nsDisplayTransform::GetResultingTransformMatrix(const FrameTransformProperties& aProperties, michael@0: const nsPoint& aOrigin, michael@0: float aAppUnitsPerPixel, michael@0: const nsRect* aBoundsOverride, michael@0: nsIFrame** aOutAncestor) michael@0: { michael@0: return GetResultingTransformMatrixInternal(aProperties, aOrigin, aAppUnitsPerPixel, michael@0: aBoundsOverride, aOutAncestor); michael@0: } michael@0: michael@0: gfx3DMatrix michael@0: nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, michael@0: const nsPoint& aOrigin, michael@0: float aAppUnitsPerPixel, michael@0: const nsRect* aBoundsOverride, michael@0: nsIFrame** aOutAncestor) michael@0: { michael@0: FrameTransformProperties props(aFrame, michael@0: aAppUnitsPerPixel, michael@0: aBoundsOverride); michael@0: michael@0: return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel, michael@0: aBoundsOverride, aOutAncestor); michael@0: } michael@0: michael@0: gfx3DMatrix michael@0: nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties, michael@0: const nsPoint& aOrigin, michael@0: float aAppUnitsPerPixel, michael@0: const nsRect* aBoundsOverride, michael@0: nsIFrame** aOutAncestor) michael@0: { michael@0: const nsIFrame *frame = aProperties.mFrame; michael@0: michael@0: if (aOutAncestor) { michael@0: *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame); michael@0: } michael@0: michael@0: /* Account for the -moz-transform-origin property by translating the michael@0: * coordinate space to the new origin. michael@0: */ michael@0: gfxPoint3D newOrigin = michael@0: gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel), michael@0: NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel), michael@0: 0.0f); michael@0: michael@0: /* Get the underlying transform matrix. This requires us to get the michael@0: * bounds of the frame. michael@0: */ michael@0: nsRect bounds = (aBoundsOverride ? *aBoundsOverride : michael@0: nsDisplayTransform::GetFrameBoundsForTransform(frame)); michael@0: michael@0: /* Get the matrix, then change its basis to factor in the origin. */ michael@0: bool dummy; michael@0: gfx3DMatrix result; michael@0: // Call IsSVGTransformed() regardless of the value of michael@0: // disp->mSpecifiedTransform, since we still need any transformFromSVGParent. michael@0: mozilla::gfx::Matrix svgTransform, transformFromSVGParent; michael@0: bool hasSVGTransforms = michael@0: frame && frame->IsSVGTransformed(&svgTransform, &transformFromSVGParent); michael@0: /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */ michael@0: if (aProperties.mTransformList) { michael@0: result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead, michael@0: frame ? frame->StyleContext() : nullptr, michael@0: frame ? frame->PresContext() : nullptr, michael@0: dummy, bounds, aAppUnitsPerPixel); michael@0: } else if (hasSVGTransforms) { michael@0: // Correct the translation components for zoom: michael@0: float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() / michael@0: aAppUnitsPerPixel; michael@0: svgTransform._31 *= pixelsPerCSSPx; michael@0: svgTransform._32 *= pixelsPerCSSPx; michael@0: result = gfx3DMatrix::From2D(ThebesMatrix(svgTransform)); michael@0: } michael@0: michael@0: if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) { michael@0: // Correct the translation components for zoom: michael@0: float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() / michael@0: aAppUnitsPerPixel; michael@0: transformFromSVGParent._31 *= pixelsPerCSSPx; michael@0: transformFromSVGParent._32 *= pixelsPerCSSPx; michael@0: result = result * gfx3DMatrix::From2D(ThebesMatrix(transformFromSVGParent)); michael@0: } michael@0: michael@0: if (aProperties.mChildPerspective > 0.0) { michael@0: gfx3DMatrix perspective; michael@0: perspective._34 = michael@0: -1.0 / NSAppUnitsToFloatPixels(aProperties.mChildPerspective, aAppUnitsPerPixel); michael@0: /* At the point when perspective is applied, we have been translated to the transform origin. michael@0: * The translation to the perspective origin is the difference between these values. michael@0: */ michael@0: result = result * nsLayoutUtils::ChangeMatrixBasis(aProperties.mToPerspectiveOrigin - aProperties.mToTransformOrigin, perspective); michael@0: } michael@0: michael@0: gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x), michael@0: hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y), michael@0: 0); michael@0: michael@0: if (frame && frame->Preserves3D()) { michael@0: // Include the transform set on our parent michael@0: NS_ASSERTION(frame->GetParent() && michael@0: frame->GetParent()->IsTransformed() && michael@0: frame->GetParent()->Preserves3DChildren(), michael@0: "Preserve3D mismatch!"); michael@0: FrameTransformProperties props(frame->GetParent(), michael@0: aAppUnitsPerPixel, michael@0: nullptr); michael@0: gfx3DMatrix parent = michael@0: GetResultingTransformMatrixInternal(props, michael@0: aOrigin - frame->GetPosition(), michael@0: aAppUnitsPerPixel, nullptr, aOutAncestor); michael@0: return nsLayoutUtils::ChangeMatrixBasis(rounded + aProperties.mToTransformOrigin, result) * parent; michael@0: } michael@0: michael@0: return nsLayoutUtils::ChangeMatrixBasis michael@0: (rounded + aProperties.mToTransformOrigin, result); michael@0: } michael@0: michael@0: bool michael@0: nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity)) { michael@0: return true; michael@0: } michael@0: michael@0: if (nsLayoutUtils::IsAnimationLoggingEnabled()) { michael@0: nsCString message; michael@0: message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for opacity animation"); michael@0: CommonElementAnimationData::LogAsyncAnimationFailure(message, michael@0: Frame()->GetContent()); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: return ShouldPrerenderTransformedContent(aBuilder, michael@0: Frame(), michael@0: nsLayoutUtils::IsAnimationLoggingEnabled()); michael@0: } michael@0: michael@0: /* static */ bool michael@0: nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, michael@0: bool aLogAnimations) michael@0: { michael@0: // Elements whose transform has been modified recently, or which michael@0: // have a compositor-animated transform, can be prerendered. An element michael@0: // might have only just had its transform animated in which case michael@0: // the ActiveLayerManager may not have been notified yet. michael@0: if (!ActiveLayerTracker::IsStyleAnimated(aFrame, eCSSProperty_transform) && michael@0: (!aFrame->GetContent() || michael@0: !nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(), michael@0: eCSSProperty_transform))) { michael@0: if (aLogAnimations) { michael@0: nsCString message; michael@0: message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation"); michael@0: CommonElementAnimationData::LogAsyncAnimationFailure(message, michael@0: aFrame->GetContent()); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsSize refSize = aBuilder->RootReferenceFrame()->GetSize(); michael@0: // Only prerender if the transformed frame's size (in the reference michael@0: // frames coordinate space) is <= the reference frame size (~viewport), michael@0: // allowing a 1/8th fuzz factor for shadows, borders, etc. michael@0: refSize += nsSize(refSize.width / 8, refSize.height / 8); michael@0: nsRect frameRect = aFrame->GetVisualOverflowRectRelativeToSelf(); michael@0: michael@0: frameRect = michael@0: nsLayoutUtils::TransformFrameRectToAncestor(aFrame, frameRect, michael@0: aBuilder->RootReferenceFrame()); michael@0: michael@0: if (frameRect.Size() <= refSize) { michael@0: return true; michael@0: } michael@0: michael@0: if (aLogAnimations) { michael@0: nsCString message; michael@0: message.AppendLiteral("Performance warning: Async animation disabled because frame size ("); michael@0: message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.width)); michael@0: message.AppendLiteral(", "); michael@0: message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameRect.height)); michael@0: message.AppendLiteral(") is bigger than the viewport ("); michael@0: message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.width)); michael@0: message.AppendLiteral(", "); michael@0: message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.height)); michael@0: message.AppendLiteral(")"); michael@0: CommonElementAnimationData::LogAsyncAnimationFailure(message, michael@0: aFrame->GetContent()); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */ michael@0: static bool IsFrameVisible(nsIFrame* aFrame, const gfx3DMatrix& aMatrix) michael@0: { michael@0: if (aMatrix.IsSingular()) { michael@0: return false; michael@0: } michael@0: if (aFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN && michael@0: aMatrix.IsBackfaceVisible()) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: const gfx3DMatrix& michael@0: nsDisplayTransform::GetTransform() michael@0: { michael@0: if (mTransform.IsIdentity()) { michael@0: float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: gfxPoint3D newOrigin = michael@0: gfxPoint3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale), michael@0: NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), michael@0: 0.0f); michael@0: if (mTransformGetter) { michael@0: mTransform = mTransformGetter(mFrame, scale); michael@0: mTransform = nsLayoutUtils::ChangeMatrixBasis(newOrigin, mTransform); michael@0: } else { michael@0: mTransform = michael@0: GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale); michael@0: michael@0: /** michael@0: * Shift the coorindates to be relative to our reference frame instead of relative to this frame. michael@0: * When we have preserve-3d, our reference frame is already guaranteed to be an ancestor of the michael@0: * preserve-3d chain, so we only need to do this once. michael@0: */ michael@0: bool hasSVGTransforms = mFrame->IsSVGTransformed(); michael@0: gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x), michael@0: hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y), michael@0: 0); michael@0: mTransform.Translate(rounded); michael@0: } michael@0: } michael@0: return mTransform; michael@0: } michael@0: michael@0: bool michael@0: nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: return ShouldPrerenderTransformedContent(aBuilder, mFrame, false); michael@0: } michael@0: michael@0: already_AddRefed nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder, michael@0: LayerManager *aManager, michael@0: const ContainerLayerParameters& aContainerParameters) michael@0: { michael@0: const gfx3DMatrix& newTransformMatrix = GetTransform(); michael@0: michael@0: if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN && michael@0: newTransformMatrix.IsBackfaceVisible()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: uint32_t flags = ShouldPrerenderTransformedContent(aBuilder, mFrame, false) ? michael@0: FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0; michael@0: nsRefPtr container = aManager->GetLayerBuilder()-> michael@0: BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetChildren(), michael@0: aContainerParameters, &newTransformMatrix, flags); michael@0: michael@0: if (!container) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags, michael@0: // so we never need to explicitely unset this flag. michael@0: if (mFrame->Preserves3D() || mFrame->Preserves3DChildren()) { michael@0: container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D); michael@0: } else { michael@0: container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_PRESERVE_3D); michael@0: } michael@0: michael@0: nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder, michael@0: this, mFrame, michael@0: eCSSProperty_transform); michael@0: if (ShouldPrerenderTransformedContent(aBuilder, mFrame, false)) { michael@0: container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(), michael@0: /*the value is irrelevant*/nullptr); michael@0: container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_MAY_CHANGE_TRANSFORM); michael@0: } else { michael@0: container->RemoveUserData(nsIFrame::LayerIsPrerenderedDataKey()); michael@0: container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_MAY_CHANGE_TRANSFORM); michael@0: } michael@0: return container.forget(); michael@0: } michael@0: michael@0: nsDisplayItem::LayerState michael@0: nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) { michael@0: // If the transform is 3d, or the layer takes part in preserve-3d sorting michael@0: // then we *always* want this to be an active layer. michael@0: if (!GetTransform().Is2D() || mFrame->Preserves3D()) { michael@0: return LAYER_ACTIVE_FORCE; michael@0: } michael@0: // Here we check if the *post-transform* bounds of this item are big enough michael@0: // to justify an active layer. michael@0: if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_transform) && michael@0: !IsItemTooSmallForActiveLayer(this)) michael@0: return LAYER_ACTIVE; michael@0: if (mFrame->GetContent()) { michael@0: if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(), michael@0: eCSSProperty_transform)) { michael@0: return LAYER_ACTIVE; michael@0: } michael@0: } michael@0: michael@0: const nsStyleDisplay* disp = mFrame->StyleDisplay(); michael@0: if ((disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM)) { michael@0: return LAYER_ACTIVE; michael@0: } michael@0: michael@0: // Expect the child display items to have this frame as their animated michael@0: // geometry root (since it will be their reference frame). If they have a michael@0: // different animated geometry root, we'll make this an active layer so the michael@0: // animation can be accelerated. michael@0: return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, michael@0: *mStoredList.GetChildren(), Frame()); michael@0: } michael@0: michael@0: bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder, michael@0: nsRegion *aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) michael@0: { michael@0: /* As we do this, we need to be sure to michael@0: * untransform the visible rect, since we want everything that's painting to michael@0: * think that it's painting in its original rectangular coordinate space. michael@0: * If we can't untransform, take the entire overflow rect */ michael@0: nsRect untransformedVisibleRect; michael@0: if (ShouldPrerenderTransformedContent(aBuilder, mFrame) || michael@0: !UntransformVisibleRect(aBuilder, &untransformedVisibleRect)) michael@0: { michael@0: untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf(); michael@0: } michael@0: nsRegion untransformedVisible = untransformedVisibleRect; michael@0: // Call RecomputeVisiblity instead of ComputeVisibility since michael@0: // nsDisplayItem::ComputeVisibility should only be called from michael@0: // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item) michael@0: mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef DEBUG_HIT michael@0: #include michael@0: #endif michael@0: michael@0: /* HitTest does some fun stuff with matrix transforms to obtain the answer. */ michael@0: void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder, michael@0: const nsRect& aRect, michael@0: HitTestState *aState, michael@0: nsTArray *aOutFrames) michael@0: { michael@0: /* Here's how this works: michael@0: * 1. Get the matrix. If it's singular, abort (clearly we didn't hit michael@0: * anything). michael@0: * 2. Invert the matrix. michael@0: * 3. Use it to transform the rect into the correct space. michael@0: * 4. Pass that rect down through to the list's version of HitTest. michael@0: */ michael@0: // GetTransform always operates in dev pixels. michael@0: float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: gfx3DMatrix matrix = GetTransform(); michael@0: michael@0: if (!IsFrameVisible(mFrame, matrix)) { michael@0: return; michael@0: } michael@0: michael@0: /* We want to go from transformed-space to regular space. michael@0: * Thus we have to invert the matrix, which normally does michael@0: * the reverse operation (e.g. regular->transformed) michael@0: */ michael@0: bool snap; michael@0: nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); michael@0: gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.y, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.width, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.height, factor)); michael@0: michael@0: /* Now, apply the transform and pass it down the channel. */ michael@0: nsRect resultingRect; michael@0: if (aRect.width == 1 && aRect.height == 1) { michael@0: // Magic width/height indicating we're hit testing a point, not a rect michael@0: gfxPoint point; michael@0: if (!matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor), michael@0: NSAppUnitsToFloatPixels(aRect.y, factor)), michael@0: childGfxBounds, michael@0: &point)) { michael@0: return; michael@0: } michael@0: michael@0: resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor), michael@0: NSFloatPixelsToAppUnits(float(point.y), factor), michael@0: 1, 1); michael@0: michael@0: } else { michael@0: gfxRect originalRect(NSAppUnitsToFloatPixels(aRect.x, factor), michael@0: NSAppUnitsToFloatPixels(aRect.y, factor), michael@0: NSAppUnitsToFloatPixels(aRect.width, factor), michael@0: NSAppUnitsToFloatPixels(aRect.height, factor)); michael@0: michael@0: gfxRect rect = matrix.UntransformBounds(originalRect, childGfxBounds); michael@0: michael@0: resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor), michael@0: NSFloatPixelsToAppUnits(float(rect.Y()), factor), michael@0: NSFloatPixelsToAppUnits(float(rect.Width()), factor), michael@0: NSFloatPixelsToAppUnits(float(rect.Height()), factor)); michael@0: } michael@0: michael@0: if (resultingRect.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: michael@0: #ifdef DEBUG_HIT michael@0: printf("Frame: %p\n", dynamic_cast(mFrame)); michael@0: printf(" Untransformed point: (%f, %f)\n", resultingRect.X(), resultingRect.Y()); michael@0: uint32_t originalFrameCount = aOutFrames.Length(); michael@0: #endif michael@0: michael@0: mStoredList.HitTest(aBuilder, resultingRect, aState, aOutFrames); michael@0: michael@0: #ifdef DEBUG_HIT michael@0: if (originalFrameCount != aOutFrames.Length()) michael@0: printf(" Hit! Time: %f, first frame: %p\n", static_cast(clock()), michael@0: dynamic_cast(aOutFrames.ElementAt(0))); michael@0: printf("=== end of hit test ===\n"); michael@0: #endif michael@0: michael@0: } michael@0: michael@0: float michael@0: nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint) michael@0: { michael@0: // GetTransform always operates in dev pixels. michael@0: float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: gfx3DMatrix matrix = GetTransform(); michael@0: michael@0: NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!"); michael@0: michael@0: bool snap; michael@0: nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); michael@0: gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.y, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.width, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.height, factor)); michael@0: michael@0: gfxPoint point; michael@0: DebugOnly result = matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor), michael@0: NSAppUnitsToFloatPixels(aPoint.y, factor)), michael@0: childGfxBounds, michael@0: &point); michael@0: NS_ASSERTION(result, "Why are we trying to get the depth for a point we didn't hit?"); michael@0: michael@0: gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0)); michael@0: return transformed.z; michael@0: } michael@0: michael@0: /* The bounding rectangle for the object is the overflow rectangle translated michael@0: * by the reference point. michael@0: */ michael@0: nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap) michael@0: { michael@0: nsRect untransformedBounds = michael@0: ShouldPrerenderTransformedContent(aBuilder, mFrame) ? michael@0: mFrame->GetVisualOverflowRectRelativeToSelf() : michael@0: mStoredList.GetBounds(aBuilder, aSnap); michael@0: *aSnap = false; michael@0: // GetTransform always operates in dev pixels. michael@0: float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: return nsLayoutUtils::MatrixTransformRect(untransformedBounds, michael@0: GetTransform(), michael@0: factor); michael@0: } michael@0: michael@0: /* The transform is opaque iff the transform consists solely of scales and michael@0: * translations and if the underlying content is opaque. Thus if the transform michael@0: * is of the form michael@0: * michael@0: * |a c e| michael@0: * |b d f| michael@0: * |0 0 1| michael@0: * michael@0: * We need b and c to be zero. michael@0: * michael@0: * We also need to check whether the underlying opaque content completely fills michael@0: * our visible rect. We use UntransformRect which expands to the axis-aligned michael@0: * bounding rect, but that's OK since if michael@0: * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it michael@0: * certainly contains the actual (non-axis-aligned) untransformed rect. michael@0: */ michael@0: nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder, michael@0: bool* aSnap) michael@0: { michael@0: *aSnap = false; michael@0: nsRect untransformedVisible; michael@0: // If we're going to prerender all our content, pretend like we michael@0: // don't have opqaue content so that everything under us is rendered michael@0: // as well. That will increase graphics memory usage if our frame michael@0: // covers the entire window, but it allows our transform to be michael@0: // updated extremely cheaply, without invalidating any other michael@0: // content. michael@0: if (ShouldPrerenderTransformedContent(aBuilder, mFrame) || michael@0: !UntransformVisibleRect(aBuilder, &untransformedVisible)) { michael@0: return nsRegion(); michael@0: } michael@0: michael@0: const gfx3DMatrix& matrix = GetTransform(); michael@0: michael@0: nsRegion result; michael@0: gfxMatrix matrix2d; michael@0: bool tmpSnap; michael@0: if (matrix.Is2D(&matrix2d) && michael@0: matrix2d.PreservesAxisAlignedRectangles() && michael@0: mStoredList.GetOpaqueRegion(aBuilder, &tmpSnap).Contains(untransformedVisible)) { michael@0: result = mVisibleRect; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /* The transform is uniform if it fills the entire bounding rect and the michael@0: * wrapped list is uniform. See GetOpaqueRegion for discussion of why this michael@0: * works. michael@0: */ michael@0: bool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor) michael@0: { michael@0: nsRect untransformedVisible; michael@0: if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) { michael@0: return false; michael@0: } michael@0: const gfx3DMatrix& matrix = GetTransform(); michael@0: michael@0: gfxMatrix matrix2d; michael@0: return matrix.Is2D(&matrix2d) && michael@0: matrix2d.PreservesAxisAlignedRectangles() && michael@0: mStoredList.GetVisibleRect().Contains(untransformedVisible) && michael@0: mStoredList.IsUniform(aBuilder, aColor); michael@0: } michael@0: michael@0: /* If UNIFIED_CONTINUATIONS is defined, we can merge two display lists that michael@0: * share the same underlying content. Otherwise, doing so results in graphical michael@0: * glitches. michael@0: */ michael@0: #ifndef UNIFIED_CONTINUATIONS michael@0: michael@0: bool michael@0: nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder, michael@0: nsDisplayItem *aItem) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: #else michael@0: michael@0: bool michael@0: nsDisplayTransform::TryMerge(nsDisplayListBuilder *aBuilder, michael@0: nsDisplayItem *aItem) michael@0: { michael@0: NS_PRECONDITION(aItem, "Why did you try merging with a null item?"); michael@0: NS_PRECONDITION(aBuilder, "Why did you try merging with a null builder?"); michael@0: michael@0: /* Make sure that we're dealing with two transforms. */ michael@0: if (aItem->GetType() != TYPE_TRANSFORM) michael@0: return false; michael@0: michael@0: /* Check to see that both frames are part of the same content. */ michael@0: if (aItem->Frame()->GetContent() != mFrame->GetContent()) michael@0: return false; michael@0: michael@0: if (aItem->GetClip() != GetClip()) michael@0: return false; michael@0: michael@0: /* Now, move everything over to this frame and signal that michael@0: * we merged things! michael@0: */ michael@0: mStoredList.MergeFrom(&static_cast(aItem)->mStoredList); michael@0: return true; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /* TransformRect takes in as parameters a rectangle (in app space) and returns michael@0: * the smallest rectangle (in app space) containing the transformed image of michael@0: * that rectangle. That is, it takes the four corners of the rectangle, michael@0: * transforms them according to the matrix associated with the specified frame, michael@0: * then returns the smallest rectangle containing the four transformed points. michael@0: * michael@0: * @param aUntransformedBounds The rectangle (in app units) to transform. michael@0: * @param aFrame The frame whose transformation should be applied. michael@0: * @param aOrigin The delta from the frame origin to the coordinate space origin michael@0: * @param aBoundsOverride (optional) Force the frame bounds to be the michael@0: * specified bounds. michael@0: * @return The smallest rectangle containing the image of the transformed michael@0: * rectangle. michael@0: */ michael@0: nsRect nsDisplayTransform::TransformRect(const nsRect &aUntransformedBounds, michael@0: const nsIFrame* aFrame, michael@0: const nsPoint &aOrigin, michael@0: const nsRect* aBoundsOverride) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); michael@0: michael@0: float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: return nsLayoutUtils::MatrixTransformRect michael@0: (aUntransformedBounds, michael@0: GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), michael@0: factor); michael@0: } michael@0: michael@0: nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds, michael@0: const nsIFrame* aFrame, michael@0: const nsPoint &aOrigin, michael@0: const nsRect* aBoundsOverride) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); michael@0: michael@0: float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: return nsLayoutUtils::MatrixTransformRectOut michael@0: (aUntransformedBounds, michael@0: GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), michael@0: factor); michael@0: } michael@0: michael@0: bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds, michael@0: const nsRect &aChildBounds, michael@0: const nsIFrame* aFrame, michael@0: const nsPoint &aOrigin, michael@0: nsRect *aOutRect) michael@0: { michael@0: NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); michael@0: michael@0: float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: michael@0: gfx3DMatrix transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr); michael@0: if (transform.IsSingular()) { michael@0: return false; michael@0: } michael@0: michael@0: gfxRect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor), michael@0: NSAppUnitsToFloatPixels(aTransformedBounds.y, factor), michael@0: NSAppUnitsToFloatPixels(aTransformedBounds.width, factor), michael@0: NSAppUnitsToFloatPixels(aTransformedBounds.height, factor)); michael@0: michael@0: gfxRect childGfxBounds(NSAppUnitsToFloatPixels(aChildBounds.x, factor), michael@0: NSAppUnitsToFloatPixels(aChildBounds.y, factor), michael@0: NSAppUnitsToFloatPixels(aChildBounds.width, factor), michael@0: NSAppUnitsToFloatPixels(aChildBounds.height, factor)); michael@0: michael@0: result = transform.UntransformBounds(result, childGfxBounds); michael@0: *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); michael@0: return true; michael@0: } michael@0: michael@0: bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder, michael@0: nsRect *aOutRect) michael@0: { michael@0: const gfx3DMatrix& matrix = GetTransform(); michael@0: if (matrix.IsSingular()) michael@0: return false; michael@0: michael@0: // GetTransform always operates in dev pixels. michael@0: float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: gfxRect result(NSAppUnitsToFloatPixels(mVisibleRect.x, factor), michael@0: NSAppUnitsToFloatPixels(mVisibleRect.y, factor), michael@0: NSAppUnitsToFloatPixels(mVisibleRect.width, factor), michael@0: NSAppUnitsToFloatPixels(mVisibleRect.height, factor)); michael@0: michael@0: bool snap; michael@0: nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); michael@0: gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.y, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.width, factor), michael@0: NSAppUnitsToFloatPixels(childBounds.height, factor)); michael@0: michael@0: /* We want to untransform the matrix, so invert the transformation first! */ michael@0: result = matrix.UntransformBounds(result, childGfxBounds); michael@0: michael@0: *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList) michael@0: : nsDisplayWrapList(aBuilder, aFrame, aList), michael@0: mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf()) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDisplaySVGEffects); michael@0: } michael@0: michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: nsDisplaySVGEffects::~nsDisplaySVGEffects() michael@0: { michael@0: MOZ_COUNT_DTOR(nsDisplaySVGEffects); michael@0: } michael@0: #endif michael@0: michael@0: nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) michael@0: { michael@0: *aSnap = false; michael@0: return nsRegion(); michael@0: } michael@0: michael@0: void michael@0: nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, michael@0: HitTestState* aState, nsTArray *aOutFrames) michael@0: { michael@0: nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2); michael@0: if (nsSVGIntegrationUtils::HitTestFrameForEffects(mFrame, michael@0: rectCenter - ToReferenceFrame())) { michael@0: mList.HitTest(aBuilder, aRect, aState, aOutFrames); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDisplaySVGEffects::PaintAsLayer(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx, michael@0: LayerManager* aManager) michael@0: { michael@0: nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx, mFrame, michael@0: mVisibleRect, michael@0: aBuilder, aManager); michael@0: } michael@0: michael@0: LayerState michael@0: nsDisplaySVGEffects::GetLayerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aParameters) michael@0: { michael@0: return LAYER_SVG_EFFECTS; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: const ContainerLayerParameters& aContainerParameters) michael@0: { michael@0: const nsIContent* content = mFrame->GetContent(); michael@0: bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); michael@0: if (hasSVGLayout) { michael@0: nsISVGChildFrame *svgChildFrame = do_QueryFrame(mFrame); michael@0: if (!svgChildFrame || !mFrame->GetContent()->IsSVG()) { michael@0: NS_ASSERTION(false, "why?"); michael@0: return nullptr; michael@0: } michael@0: if (!static_cast(content)->HasValidDimensions()) { michael@0: return nullptr; // The SVG spec says not to draw filters for this michael@0: } michael@0: } michael@0: michael@0: float opacity = mFrame->StyleDisplay()->mOpacity; michael@0: if (opacity == 0.0f) michael@0: return nullptr; michael@0: michael@0: nsIFrame* firstFrame = michael@0: nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); michael@0: nsSVGEffects::EffectProperties effectProperties = michael@0: nsSVGEffects::GetEffectProperties(firstFrame); michael@0: michael@0: bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); michael@0: effectProperties.GetClipPathFrame(&isOK); michael@0: effectProperties.GetMaskFrame(&isOK); michael@0: michael@0: if (!isOK) { michael@0: return nullptr; michael@0: } michael@0: michael@0: ContainerLayerParameters newContainerParameters = aContainerParameters; michael@0: if (effectProperties.HasValidFilter()) { michael@0: newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; michael@0: } michael@0: michael@0: nsRefPtr container = aManager->GetLayerBuilder()-> michael@0: BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList, michael@0: newContainerParameters, nullptr); michael@0: michael@0: return container.forget(); michael@0: } michael@0: michael@0: bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder, michael@0: nsRegion* aVisibleRegion, michael@0: const nsRect& aAllowVisibleRegionExpansion) { michael@0: nsPoint offset = ToReferenceFrame(); michael@0: nsRect dirtyRect = michael@0: nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mFrame, michael@0: mVisibleRect - offset) + michael@0: offset; michael@0: michael@0: // Our children may be made translucent or arbitrarily deformed so we should michael@0: // not allow them to subtract area from aVisibleRegion. michael@0: nsRegion childrenVisible(dirtyRect); michael@0: nsRect r = dirtyRect.Intersect(mList.GetBounds(aBuilder)); michael@0: mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r, nsRect()); michael@0: return true; michael@0: } michael@0: michael@0: bool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) michael@0: { michael@0: if (aItem->GetType() != TYPE_SVG_EFFECTS) michael@0: return false; michael@0: // items for the same content element should be merged into a single michael@0: // compositing group michael@0: // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects michael@0: if (aItem->Frame()->GetContent() != mFrame->GetContent()) michael@0: return false; michael@0: if (aItem->GetClip() != GetClip()) michael@0: return false; michael@0: nsDisplaySVGEffects* other = static_cast(aItem); michael@0: MergeFromTrackingMergedFrames(other); michael@0: mEffectsBounds.UnionRect(mEffectsBounds, michael@0: other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame)); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: nsDisplaySVGEffects::PrintEffects(nsACString& aTo) michael@0: { michael@0: nsIFrame* firstFrame = michael@0: nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); michael@0: nsSVGEffects::EffectProperties effectProperties = michael@0: nsSVGEffects::GetEffectProperties(firstFrame); michael@0: bool isOK = true; michael@0: nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); michael@0: bool first = true; michael@0: aTo += " effects=("; michael@0: if (mFrame->StyleDisplay()->mOpacity != 1.0f) { michael@0: first = false; michael@0: aTo += nsPrintfCString("opacity(%f)", mFrame->StyleDisplay()->mOpacity); michael@0: } michael@0: if (clipPathFrame) { michael@0: if (!first) { michael@0: aTo += ", "; michael@0: } michael@0: aTo += nsPrintfCString("clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial"); michael@0: first = false; michael@0: } michael@0: if (effectProperties.HasValidFilter()) { michael@0: if (!first) { michael@0: aTo += ", "; michael@0: } michael@0: aTo += "filter"; michael@0: first = false; michael@0: } michael@0: if (effectProperties.GetMaskFrame(&isOK)) { michael@0: if (!first) { michael@0: aTo += ", "; michael@0: } michael@0: aTo += "mask"; michael@0: } michael@0: aTo += ")"; michael@0: } michael@0: #endif michael@0: