Bug 1922335 - Implement basic support for DataSourceSurface-based snapshotting of old contents. r=nical

This implements some basic snapshotting functionality, and hooks it to
the ::view-transition-old pseudo-elements.

Some tests that now fail because we start rendering the old snapshot,
but not at the right position, as for now the view transition pseudo
tree is all statically-positioned. That's all expected for now until we
implement the right styling for them, the top layer behavior, etc.

Main remaining question is how to get a hand on the right layer manager
from nsImageFrame::Destroy.

Differential Revision: https://phabricator.services.mozilla.com/D231977
This commit is contained in:
Emilio Cobos Álvarez
2025-01-14 14:57:57 +00:00
parent 4507fb96aa
commit 2fbd4d89f9
21 changed files with 244 additions and 58 deletions

View File

@@ -3,22 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ViewTransition.h" #include "ViewTransition.h"
#include "nsPresContext.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/dom/BindContext.h" #include "mozilla/dom/BindContext.h"
#include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/Promise-inl.h" #include "mozilla/dom/Promise-inl.h"
#include "mozilla/dom/ViewTransitionBinding.h" #include "mozilla/dom/ViewTransitionBinding.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/ServoStyleConsts.h" #include "mozilla/ServoStyleConsts.h"
#include "mozilla/SVGIntegrationUtils.h"
#include "mozilla/WritingModes.h" #include "mozilla/WritingModes.h"
#include "nsDisplayList.h"
#include "nsITimer.h" #include "nsITimer.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "Units.h" #include "Units.h"
static inline void ImplCycleCollectionTraverse(
nsCycleCollectionTraversalCallback&, const nsRefPtrHashKey<nsAtom>&,
const char* aName, uint32_t aFlags = 0) {
// Nothing, but needed to compile.
}
namespace mozilla::dom { namespace mozilla::dom {
// Set capture's old transform to a <transform-function> that would map // Set capture's old transform to a <transform-function> that would map
@@ -48,9 +48,40 @@ static CSSToCSSMatrix4x4Flagged EffectiveTransform(nsIFrame* aFrame) {
return matrix; return matrix;
} }
static RefPtr<gfx::DataSourceSurface> CaptureFallbackSnapshot(
nsIFrame* aFrame) {
const nsRect rect = aFrame->InkOverflowRectRelativeToSelf();
const auto surfaceRect = LayoutDeviceIntRect::FromAppUnitsToOutside(
rect, aFrame->PresContext()->AppUnitsPerDevPixel());
// TODO: Should we use the DrawTargetRecorder infra or what not?
const auto format = gfx::SurfaceFormat::B8G8R8A8;
RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTarget(
gfxPlatform::GetPlatform()->GetSoftwareBackend(),
surfaceRect.Size().ToUnknownSize(), format);
if (NS_WARN_IF(!dt) || NS_WARN_IF(!dt->IsValid())) {
return nullptr;
}
{
using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
gfxContext thebes(dt);
// TODO: This matches the drawable code we use for -moz-element(), but is
// this right?
const PaintFrameFlags flags = PaintFrameFlags::InTransform;
nsLayoutUtils::PaintFrame(&thebes, aFrame, rect, NS_RGBA(0, 0, 0, 0),
nsDisplayListBuilderMode::Painting, flags);
}
RefPtr<gfx::SourceSurface> surf = dt->GetBackingSurface();
if (NS_WARN_IF(!surf)) {
return nullptr;
}
return surf->GetDataSurface();
}
struct CapturedElementOldState { struct CapturedElementOldState {
// TODO: mImage RefPtr<gfx::DataSourceSurface> mImage;
bool mHasImage = false;
// Encompasses width and height. // Encompasses width and height.
nsSize mSize; nsSize mSize;
@@ -63,7 +94,7 @@ struct CapturedElementOldState {
CapturedElementOldState(nsIFrame* aFrame, CapturedElementOldState(nsIFrame* aFrame,
const nsSize& aSnapshotContainingBlockSize) const nsSize& aSnapshotContainingBlockSize)
: mHasImage(true), : mImage(CaptureFallbackSnapshot(aFrame)),
mSize(aFrame->Style()->IsRootElementStyle() mSize(aFrame->Style()->IsRootElementStyle()
? aSnapshotContainingBlockSize ? aSnapshotContainingBlockSize
: aFrame->GetRect().Size()), : aFrame->GetRect().Size()),
@@ -92,9 +123,9 @@ struct ViewTransition::CapturedElement {
static inline void ImplCycleCollectionTraverse( static inline void ImplCycleCollectionTraverse(
nsCycleCollectionTraversalCallback& aCb, nsCycleCollectionTraversalCallback& aCb,
const UniquePtr<ViewTransition::CapturedElement>& aField, const char* aName, const ViewTransition::CapturedElement& aField, const char* aName,
uint32_t aFlags = 0) { uint32_t aFlags = 0) {
ImplCycleCollectionTraverse(aCb, aField->mNewElement, aName, aFlags); ImplCycleCollectionTraverse(aCb, aField.mNewElement, aName, aFlags);
} }
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ViewTransition, mDocument, NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ViewTransition, mDocument,
@@ -117,6 +148,14 @@ ViewTransition::ViewTransition(Document& aDoc,
ViewTransition::~ViewTransition() { ClearTimeoutTimer(); } ViewTransition::~ViewTransition() { ClearTimeoutTimer(); }
gfx::DataSourceSurface* ViewTransition::GetOldSurface(nsAtom* aName) const {
auto el = mNamedElements.Get(aName);
if (NS_WARN_IF(!el)) {
return nullptr;
}
return el->mOldState.mImage;
}
nsIGlobalObject* ViewTransition::GetParentObject() const { nsIGlobalObject* ViewTransition::GetParentObject() const {
return mDocument ? mDocument->GetParentObject() : nullptr; return mDocument ? mDocument->GetParentObject() : nullptr;
} }
@@ -325,7 +364,7 @@ void ViewTransition::SetupTransitionPseudoElements() {
// Append imagePair to group. // Append imagePair to group.
group->AppendChildTo(imagePair, kNotify, IgnoreErrors()); group->AppendChildTo(imagePair, kNotify, IgnoreErrors());
// If capturedElement's old image is not null, then: // If capturedElement's old image is not null, then:
if (capturedElement.mOldState.mHasImage) { if (capturedElement.mOldState.mImage) {
// Let old be a new ::view-transition-old(), with its view transition // Let old be a new ::view-transition-old(), with its view transition
// name set to transitionName, displaying capturedElement's old image as // name set to transitionName, displaying capturedElement's old image as
// its replaced content. // its replaced content.

View File

@@ -7,7 +7,8 @@
#include "nsRect.h" #include "nsRect.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
#include "nsTHashMap.h" #include "nsAtomHashKeys.h"
#include "nsClassHashtable.h"
class nsIGlobalObject; class nsIGlobalObject;
class nsITimer; class nsITimer;
@@ -16,6 +17,10 @@ namespace mozilla {
class ErrorResult; class ErrorResult;
namespace gfx {
class DataSourceSurface;
}
namespace dom { namespace dom {
class Document; class Document;
@@ -59,6 +64,7 @@ class ViewTransition final : public nsISupports, public nsWrapperCache {
void PerformPendingOperations(); void PerformPendingOperations();
Element* GetRoot() const { return mViewTransitionRoot; } Element* GetRoot() const { return mViewTransitionRoot; }
gfx::DataSourceSurface* GetOldSurface(nsAtom* aName) const;
nsIGlobalObject* GetParentObject() const; nsIGlobalObject* GetParentObject() const;
JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override; JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
@@ -91,7 +97,7 @@ class ViewTransition final : public nsISupports, public nsWrapperCache {
RefPtr<ViewTransitionUpdateCallback> mUpdateCallback; RefPtr<ViewTransitionUpdateCallback> mUpdateCallback;
// https://drafts.csswg.org/css-view-transitions/#viewtransition-named-elements // https://drafts.csswg.org/css-view-transitions/#viewtransition-named-elements
using NamedElements = nsTHashMap<RefPtr<nsAtom>, UniquePtr<CapturedElement>>; using NamedElements = nsClassHashtable<nsAtomHashKey, CapturedElement>;
NamedElements mNamedElements; NamedElements mNamedElements;
// https://drafts.csswg.org/css-view-transitions/#viewtransition-initial-snapshot-containing-block-size // https://drafts.csswg.org/css-view-transitions/#viewtransition-initial-snapshot-containing-block-size

View File

@@ -15,4 +15,6 @@ UNIFIED_SOURCES += [
"ViewTransition.cpp", "ViewTransition.cpp",
] ]
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul" FINAL_LIBRARY = "xul"

View File

@@ -221,6 +221,7 @@ nsIFrame* NS_NewXULImageFrame(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*); nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*); nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*); nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*);
nsIFrame* NS_NewImageFrameForViewTransitionOld(PresShell*, ComputedStyle*);
// Returns true if aFrame is an anonymous flex/grid item. // Returns true if aFrame is an anonymous flex/grid item.
static inline bool IsAnonymousItem(const nsIFrame* aFrame) { static inline bool IsAnonymousItem(const nsIFrame* aFrame) {
@@ -3449,8 +3450,8 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
aParentFrame->GetParent()->IsFieldSetFrame(), aParentFrame->GetParent()->IsFieldSetFrame(),
"Unexpected parent for fieldset content anon box"); "Unexpected parent for fieldset content anon box");
if (aElement.IsInNativeAnonymousSubtree() && if (aElement.IsInNativeAnonymousSubtree()) {
aElement.NodeInfo()->NameAtom() == nsGkAtoms::label && aParentFrame) { if (aElement.NodeInfo()->NameAtom() == nsGkAtoms::label && aParentFrame) {
if (aParentFrame->IsFileControlFrame()) { if (aParentFrame->IsFileControlFrame()) {
static constexpr FrameConstructionData sFileLabelData( static constexpr FrameConstructionData sFileLabelData(
NS_NewFileControlLabelFrame); NS_NewFileControlLabelFrame);
@@ -3463,6 +3464,12 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
return &sComboboxLabelData; return &sComboboxLabelData;
} }
} }
if (aElement.GetPseudoElementType() == PseudoStyleType::viewTransitionOld) {
static constexpr FrameConstructionData sViewTransitionOldData(
NS_NewImageFrameForViewTransitionOld);
return &sViewTransitionOldData;
}
}
static constexpr FrameConstructionDataByTag sHTMLData[] = { static constexpr FrameConstructionDataByTag sHTMLData[] = {
SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData), SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),

View File

@@ -28,6 +28,7 @@
#include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/ReferrerInfo.h" #include "mozilla/dom/ReferrerInfo.h"
#include "mozilla/dom/ResponsiveImageSelector.h" #include "mozilla/dom/ResponsiveImageSelector.h"
#include "mozilla/dom/ViewTransition.h"
#include "mozilla/dom/LargestContentfulPaint.h" #include "mozilla/dom/LargestContentfulPaint.h"
#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/image/WebRenderImageProvider.h"
#include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/RenderRootStateManager.h"
@@ -126,10 +127,10 @@ class nsDisplayGradient final : public nsPaintedDisplayItem {
void Paint(nsDisplayListBuilder*, gfxContext* aCtx) final; void Paint(nsDisplayListBuilder*, gfxContext* aCtx) final;
bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder&, bool CreateWebRenderCommands(wr::DisplayListBuilder&,
mozilla::wr::IpcResourceUpdateQueue&, wr::IpcResourceUpdateQueue&,
const StackingContextHelper&, const StackingContextHelper&,
mozilla::layers::RenderRootStateManager*, layers::RenderRootStateManager*,
nsDisplayListBuilder*) final; nsDisplayListBuilder*) final;
NS_DISPLAY_DECL_NAME("Gradient", TYPE_GRADIENT) NS_DISPLAY_DECL_NAME("Gradient", TYPE_GRADIENT)
@@ -157,8 +158,7 @@ void nsDisplayGradient::Paint(nsDisplayListBuilder* aBuilder,
bool nsDisplayGradient::CreateWebRenderCommands( bool nsDisplayGradient::CreateWebRenderCommands(
wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc, const StackingContextHelper& aSc, layers::RenderRootStateManager* aManager,
mozilla::layers::RenderRootStateManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) { nsDisplayListBuilder* aDisplayListBuilder) {
auto* frame = static_cast<nsImageFrame*>(Frame()); auto* frame = static_cast<nsImageFrame*>(Frame());
nsImageRenderer imageRenderer(frame, frame->GetImageFromStyle(), nsImageRenderer imageRenderer(frame, frame->GetImageFromStyle(),
@@ -399,6 +399,12 @@ nsIFrame* NS_NewImageFrameForListStyleImage(PresShell* aPresShell,
nsImageFrame::Kind::ListStyleImage); nsImageFrame::Kind::ListStyleImage);
} }
nsIFrame* NS_NewImageFrameForViewTransitionOld(PresShell* aPresShell,
ComputedStyle* aStyle) {
return new (aPresShell) nsImageFrame(aStyle, aPresShell->GetPresContext(),
nsImageFrame::Kind::ViewTransitionOld);
}
bool nsImageFrame::ShouldShowBrokenImageIcon() const { bool nsImageFrame::ShouldShowBrokenImageIcon() const {
// NOTE(emilio, https://github.com/w3c/csswg-drafts/issues/2832): WebKit and // NOTE(emilio, https://github.com/w3c/csswg-drafts/issues/2832): WebKit and
// Blink behave differently here for content: url(..), for now adapt to // Blink behave differently here for content: url(..), for now adapt to
@@ -465,6 +471,11 @@ a11y::AccType nsImageFrame::AccessibleType() {
return a11y::eNoType; return a11y::eNoType;
} }
if (mKind == Kind::ViewTransitionOld) {
// View transitions don't show up in the a11y tree.
return a11y::eNoType;
}
// Don't use GetImageMap() to avoid reentrancy into accessibility. // Don't use GetImageMap() to avoid reentrancy into accessibility.
if (HasImageMap()) { if (HasImageMap()) {
return a11y::eHTMLImageMapType; return a11y::eHTMLImageMapType;
@@ -527,6 +538,13 @@ void nsImageFrame::Destroy(DestroyContext& aContext) {
BrokenImageIcon::RemoveObserver(this); BrokenImageIcon::RemoveObserver(this);
} }
if (mViewTransitionData.HasKey()) {
MOZ_ASSERT(mViewTransitionData.mManager);
mViewTransitionData.mManager->AddImageKeyForDiscard(
mViewTransitionData.mImageKey);
mViewTransitionData = {};
}
nsAtomicContainerFrame::Destroy(aContext); nsAtomicContainerFrame::Destroy(aContext);
} }
@@ -612,6 +630,8 @@ static bool SizeIsAvailable(imgIRequest* aRequest) {
const StyleImage* nsImageFrame::GetImageFromStyle() const { const StyleImage* nsImageFrame::GetImageFromStyle() const {
switch (mKind) { switch (mKind) {
case Kind::ViewTransitionOld:
break;
case Kind::ImageLoadingContent: case Kind::ImageLoadingContent:
break; break;
case Kind::ListStyleImage: case Kind::ListStyleImage:
@@ -730,6 +750,8 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
} }
} else if (mKind == Kind::XULImage) { } else if (mKind == Kind::XULImage) {
UpdateXULImage(); UpdateXULImage();
} else if (mKind == Kind::ViewTransitionOld) {
// View transitions have a surface directly.
} else { } else {
const StyleImage* image = GetImageFromStyle(); const StyleImage* image = GetImageFromStyle();
if (image->IsImageRequestType()) { if (image->IsImageRequestType()) {
@@ -859,6 +881,16 @@ IntrinsicSize nsImageFrame::ComputeIntrinsicSize(
return FinishIntrinsicSize(containAxes, intrinsicSize); return FinishIntrinsicSize(containAxes, intrinsicSize);
} }
if (auto* surf = GetViewTransitionSurface()) {
IntrinsicSize intrinsicSize;
auto devPx = LayoutDeviceIntSize::FromUnknownSize(surf->GetSize());
auto size = LayoutDeviceIntSize::ToAppUnits(
devPx, PresContext()->AppUnitsPerDevPixel());
intrinsicSize.width.emplace(size.width);
intrinsicSize.height.emplace(size.height);
return FinishIntrinsicSize(containAxes, intrinsicSize);
}
if (mKind == nsImageFrame::Kind::ListStyleImage) { if (mKind == nsImageFrame::Kind::ListStyleImage) {
// Note: images are handled above, this handles gradients etc. // Note: images are handled above, this handles gradients etc.
const nscoord defaultLength = ListImageDefaultLength(*this); const nscoord defaultLength = ListImageDefaultLength(*this);
@@ -918,6 +950,20 @@ bool nsImageFrame::UpdateIntrinsicSize() {
return mIntrinsicSize != oldIntrinsicSize; return mIntrinsicSize != oldIntrinsicSize;
} }
gfx::DataSourceSurface* nsImageFrame::GetViewTransitionSurface() const {
if (mKind != Kind::ViewTransitionOld) {
return nullptr;
}
auto* vt = PresContext()->Document()->GetActiveViewTransition();
if (NS_WARN_IF(!vt)) {
return nullptr;
}
MOZ_ASSERT(GetContent()->AsElement()->HasName());
nsAtom* name =
GetContent()->AsElement()->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
return vt->GetOldSurface(name);
}
AspectRatio nsImageFrame::ComputeIntrinsicRatioForImage( AspectRatio nsImageFrame::ComputeIntrinsicRatioForImage(
imgIContainer* aImage, bool aIgnoreContainment) const { imgIContainer* aImage, bool aIgnoreContainment) const {
if (!aIgnoreContainment && GetContainSizeAxes().IsAny()) { if (!aIgnoreContainment && GetContainSizeAxes().IsAny()) {
@@ -929,6 +975,11 @@ AspectRatio nsImageFrame::ComputeIntrinsicRatioForImage(
return fromImage; return fromImage;
} }
} }
if (auto* surf = GetViewTransitionSurface()) {
return AspectRatio::FromSize(surf->GetSize());
}
if (ShouldUseMappedAspectRatio()) { if (ShouldUseMappedAspectRatio()) {
const StyleAspectRatio& ratio = StylePosition()->mAspectRatio; const StyleAspectRatio& ratio = StylePosition()->mAspectRatio;
if (ratio.auto_ && ratio.HasRatio()) { if (ratio.auto_ && ratio.HasRatio()) {
@@ -1815,10 +1866,9 @@ class nsDisplayAltFeedback final : public nsPaintedDisplayItem {
} }
bool CreateWebRenderCommands( bool CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder, wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc, const StackingContextHelper& aSc,
mozilla::layers::RenderRootStateManager* aManager, layers::RenderRootStateManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) final { nsDisplayListBuilder* aDisplayListBuilder) final {
// Always sync decode, because these icons are UI, and since they're not // Always sync decode, because these icons are UI, and since they're not
// discardable we'll pay the price of sync decoding at most once. // discardable we'll pay the price of sync decoding at most once.
@@ -1983,10 +2033,9 @@ ImgDrawResult nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext,
} }
ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer( ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer(
nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder, nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources, wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc,
const StackingContextHelper& aSc, layers::RenderRootStateManager* aManager,
mozilla::layers::RenderRootStateManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder, nsPoint aPt, uint32_t aFlags) { nsDisplayListBuilder* aDisplayListBuilder, nsPoint aPt, uint32_t aFlags) {
// Whether we draw the broken or loading icon. // Whether we draw the broken or loading icon.
bool isLoading = mKind != Kind::ImageLoadingContent || bool isLoading = mKind != Kind::ImageLoadingContent ||
@@ -2020,7 +2069,7 @@ ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer(
bool textDrawResult = true; bool textDrawResult = true;
class AutoSaveRestore { class AutoSaveRestore {
public: public:
explicit AutoSaveRestore(mozilla::wr::DisplayListBuilder& aBuilder, explicit AutoSaveRestore(wr::DisplayListBuilder& aBuilder,
bool& aTextDrawResult) bool& aTextDrawResult)
: mBuilder(aBuilder), mTextDrawResult(aTextDrawResult) { : mBuilder(aBuilder), mTextDrawResult(aTextDrawResult) {
mBuilder.Save(); mBuilder.Save();
@@ -2036,7 +2085,7 @@ ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer(
} }
private: private:
mozilla::wr::DisplayListBuilder& mBuilder; wr::DisplayListBuilder& mBuilder;
bool& mTextDrawResult; bool& mTextDrawResult;
}; };
@@ -2289,19 +2338,54 @@ nsRegion nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
return nsRegion(); return nsRegion();
} }
bool nsDisplayImage::CreateWebRenderCommands( void nsDisplayImage::MaybeCreateWebRenderCommandsForViewTransition(
mozilla::wr::DisplayListBuilder& aBuilder, wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, RenderRootStateManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) {
auto* frame = Frame();
MOZ_ASSERT(!frame->mImage);
auto* surf = frame->GetViewTransitionSurface();
if (NS_WARN_IF(!surf)) {
return;
}
if (!frame->mViewTransitionData.HasKey()) {
DataSourceSurface::ScopedMap map(surf, DataSourceSurface::READ);
if (NS_WARN_IF(!map.IsMapped())) {
return;
}
auto key = aManager->WrBridge()->GetNextImageKey();
auto size = surf->GetSize();
auto format = surf->GetFormat();
wr::ImageDescriptor desc(size, format);
Range<uint8_t> bytes(map.GetData(), map.GetStride() * size.height);
if (NS_WARN_IF(!aResources.AddImage(key, desc, bytes))) {
return;
}
// TODO: Discard this image
frame->mViewTransitionData.mImageKey = key;
frame->mViewTransitionData.mManager = aManager;
}
const nsRect destAppUnits = GetDestRect();
const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
const auto destRect =
wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(destAppUnits, factor));
auto rendering = wr::ToImageRendering(frame->UsedImageRendering());
aBuilder.PushImage(destRect, destRect, !BackfaceIsHidden(),
/* aForceAntiAliasing = */ false, rendering,
frame->mViewTransitionData.mImageKey);
}
bool nsDisplayImage::CreateWebRenderCommands(
wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc, RenderRootStateManager* aManager, const StackingContextHelper& aSc, RenderRootStateManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) { nsDisplayListBuilder* aDisplayListBuilder) {
MOZ_ASSERT(mFrame->IsImageFrame() || mFrame->IsImageControlFrame());
auto* frame = Frame(); auto* frame = Frame();
auto* image = frame->mImage.get(); auto* image = frame->mImage.get();
if (!image) { if (!image) {
// TODO: View transitions. MaybeCreateWebRenderCommandsForViewTransition(
aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
return true; return true;
} }
if (frame->HasImageMap()) { if (frame->HasImageMap()) {
// Image layer doesn't support draw focus ring for image map. // Image layer doesn't support draw focus ring for image map.
return false; return false;
@@ -2492,19 +2576,24 @@ void nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
aBuilder, this, clipFlags); aBuilder, this, clipFlags);
if (!mComputedSize.IsEmpty()) { if (!mComputedSize.IsEmpty()) {
const bool isViewTransition = mKind == Kind::ViewTransitionOld;
const bool imageOK = mKind != Kind::ImageLoadingContent || const bool imageOK = mKind != Kind::ImageLoadingContent ||
ImageOk(mContent->AsElement()->State()); ImageOk(mContent->AsElement()->State());
nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest(); nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
const bool isImageFromStyle = const bool isImageFromStyle = mKind != Kind::ImageLoadingContent &&
mKind != Kind::ImageLoadingContent && mKind != Kind::XULImage; mKind != Kind::XULImage && !isViewTransition;
const bool drawAltFeedback = [&] { const bool drawAltFeedback = [&] {
if (!imageOK) { if (!imageOK) {
return true; return true;
} }
// If we're a gradient, we don't need to draw alt feedback.
if (isImageFromStyle && !GetImageFromStyle()->IsImageRequestType()) { if (isImageFromStyle && !GetImageFromStyle()->IsImageRequestType()) {
// If we're a gradient, we don't need to draw alt feedback.
return false;
}
if (isViewTransition) {
// Same for view transitions.
return false; return false;
} }
// XXX(seth): The SizeIsAvailable check here should not be necessary - the // XXX(seth): The SizeIsAvailable check here should not be necessary - the
@@ -2533,7 +2622,7 @@ void nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
} }
} }
} else { } else {
if (mImage) { if (mImage || isViewTransition) {
aLists.Content()->AppendNewToTop<nsDisplayImage>(aBuilder, this); aLists.Content()->AppendNewToTop<nsDisplayImage>(aBuilder, this);
} else if (isImageFromStyle) { } else if (isImageFromStyle) {
aLists.Content()->AppendNewToTop<nsDisplayGradient>(aBuilder, this); aLists.Content()->AppendNewToTop<nsDisplayGradient>(aBuilder, this);

View File

@@ -196,6 +196,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
ContentPropertyAtIndex, ContentPropertyAtIndex,
// For a list-style-image ::marker. // For a list-style-image ::marker.
ListStyleImage, ListStyleImage,
// For a ::view-transition-old pseudo-element
ViewTransitionOld,
}; };
// Creates a suitable continuing frame for this frame. // Creates a suitable continuing frame for this frame.
@@ -214,6 +216,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
ComputedStyle*); ComputedStyle*);
friend nsIFrame* NS_NewImageFrameForListStyleImage(mozilla::PresShell*, friend nsIFrame* NS_NewImageFrameForListStyleImage(mozilla::PresShell*,
ComputedStyle*); ComputedStyle*);
friend nsIFrame* NS_NewImageFrameForViewTransitionOld(mozilla::PresShell*,
ComputedStyle*);
nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, Kind aKind) nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, Kind aKind)
: nsImageFrame(aStyle, aPresContext, kClassID, aKind) {} : nsImageFrame(aStyle, aPresContext, kClassID, aKind) {}
@@ -302,6 +306,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
// and height="". // and height="".
bool ShouldUseMappedAspectRatio() const; bool ShouldUseMappedAspectRatio() const;
mozilla::gfx::DataSourceSurface* GetViewTransitionSurface() const;
/** /**
* Notification that aRequest will now be the current request. * Notification that aRequest will now be the current request.
*/ */
@@ -392,6 +398,15 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
nsCOMPtr<imgIContainer> mImage; nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgIContainer> mPrevImage; nsCOMPtr<imgIContainer> mPrevImage;
struct ViewTransitionData {
// The image key of our snapshot.
mozilla::wr::ImageKey mImageKey{{0}, 0};
// The owner of the key.
RefPtr<mozilla::layers::RenderRootStateManager> mManager;
bool HasKey() const { return mImageKey != mozilla::wr::ImageKey{{0}, 0}; }
} mViewTransitionData;
// The content-box size as if we are not fragmented, cached in the most recent // The content-box size as if we are not fragmented, cached in the most recent
// reflow. // reflow.
nsSize mComputedSize; nsSize mComputedSize;
@@ -458,6 +473,11 @@ class nsDisplayImage final : public nsPaintedDisplayItem {
mozilla::layers::RenderRootStateManager*, mozilla::layers::RenderRootStateManager*,
nsDisplayListBuilder*) final; nsDisplayListBuilder*) final;
void MaybeCreateWebRenderCommandsForViewTransition(
mozilla::wr::DisplayListBuilder&, mozilla::wr::IpcResourceUpdateQueue&,
const StackingContextHelper&, mozilla::layers::RenderRootStateManager*,
nsDisplayListBuilder*);
nsImageFrame* Frame() const { nsImageFrame* Frame() const {
MOZ_ASSERT(mFrame->IsImageFrame() || mFrame->IsImageControlFrame()); MOZ_ASSERT(mFrame->IsImageFrame() || mFrame->IsImageControlFrame());
return static_cast<nsImageFrame*>(mFrame); return static_cast<nsImageFrame*>(mFrame);

View File

@@ -0,0 +1,2 @@
[active-view-transition-on-non-root.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[fractional-translation-from-position.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[fractional-translation-from-transform.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[border-offset-with-padding-inline.tentative.html]
expected: FAIL

View File

@@ -1,5 +1,2 @@
[massive-element-left-of-viewport-partially-onscreen-new.html] [massive-element-left-of-viewport-partially-onscreen-new.html]
# Might need meta viewport. expected: FAIL
expected:
if os == "android": FAIL
PASS

View File

@@ -1,5 +1,2 @@
[massive-element-left-of-viewport-partially-onscreen-old.html] [massive-element-left-of-viewport-partially-onscreen-old.html]
# Might need meta viewport. expected: FAIL
expected:
if os == "android": FAIL
PASS

View File

@@ -0,0 +1,2 @@
[named-element-with-fix-pos-child-old.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[new-and-old-sizes-match.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[no-root-capture.html]
expected: FAIL

View File

@@ -5,3 +5,8 @@
[:only-child should match because ::view-transition-old is not generated (none to element)] [:only-child should match because ::view-transition-old is not generated (none to element)]
expected: FAIL expected: FAIL
[:only-child should not match because ::view-transition-old is generated (element to root)]
expected: FAIL
[:only-child should not match because ::view-transition-old is generated (element to element)]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[rtl-with-scrollbar.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[scroller-child-abspos.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[scroller-child.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[scroller.html]
expected: FAIL

View File

@@ -0,0 +1,2 @@
[transformed-element-scroll-transform.html]
expected: FAIL