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:
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -15,4 +15,6 @@ UNIFIED_SOURCES += [
|
|||||||
"ViewTransition.cpp",
|
"ViewTransition.cpp",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
include("/ipc/chromium/chromium-config.mozbuild")
|
||||||
|
|
||||||
FINAL_LIBRARY = "xul"
|
FINAL_LIBRARY = "xul"
|
||||||
|
|||||||
@@ -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,18 +3450,24 @@ 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);
|
||||||
return &sFileLabelData;
|
return &sFileLabelData;
|
||||||
|
}
|
||||||
|
if (aParentFrame->GetParent() &&
|
||||||
|
aParentFrame->GetParent()->IsComboboxControlFrame()) {
|
||||||
|
static constexpr FrameConstructionData sComboboxLabelData(
|
||||||
|
NS_NewComboboxLabelFrame);
|
||||||
|
return &sComboboxLabelData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (aParentFrame->GetParent() &&
|
if (aElement.GetPseudoElementType() == PseudoStyleType::viewTransitionOld) {
|
||||||
aParentFrame->GetParent()->IsComboboxControlFrame()) {
|
static constexpr FrameConstructionData sViewTransitionOldData(
|
||||||
static constexpr FrameConstructionData sComboboxLabelData(
|
NS_NewImageFrameForViewTransitionOld);
|
||||||
NS_NewComboboxLabelFrame);
|
return &sViewTransitionOldData;
|
||||||
return &sComboboxLabelData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[active-view-transition-on-non-root.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[fractional-translation-from-position.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[fractional-translation-from-transform.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[border-offset-with-padding-inline.tentative.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[named-element-with-fix-pos-child-old.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[new-and-old-sizes-match.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[no-root-capture.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[rtl-with-scrollbar.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[scroller-child-abspos.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[scroller-child.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[scroller.html]
|
||||||
|
expected: FAIL
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[transformed-element-scroll-transform.html]
|
||||||
|
expected: FAIL
|
||||||
Reference in New Issue
Block a user