зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1787072 - Avoid useless reframes setting the src attribute of a broken image that already has an image frame. r=dholbert
Differential Revision: https://phabricator.services.mozilla.com/D155533
This commit is contained in:
Родитель
365f297335
Коммит
97b7562b6b
|
@ -486,9 +486,15 @@ static bool StateChangeMayAffectFrame(const Element& aElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aElement.IsHTMLElement(nsGkAtoms::img)) {
|
if (aElement.IsHTMLElement(nsGkAtoms::img)) {
|
||||||
// Loading state doesn't affect <img>, see
|
if (!brokenChanged) {
|
||||||
// `nsImageFrame::ShouldCreateImageFrameFor`.
|
// Loading state doesn't affect <img>, see
|
||||||
return brokenChanged;
|
// `nsImageFrame::ImageFrameTypeForElement`.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const bool needsImageFrame =
|
||||||
|
nsImageFrame::ImageFrameTypeFor(aElement, *aFrame.Style()) !=
|
||||||
|
nsImageFrame::ImageFrameType::None;
|
||||||
|
return needsImageFrame != aFrame.IsImageFrameOrSubclass();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aElement.IsSVGElement(nsGkAtoms::image)) {
|
if (aElement.IsSVGElement(nsGkAtoms::image)) {
|
||||||
|
@ -2705,7 +2711,7 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement,
|
||||||
// We should really fix the weird primary frame mapping for image maps
|
// We should really fix the weird primary frame mapping for image maps
|
||||||
// (bug 135040)...
|
// (bug 135040)...
|
||||||
if (styleFrame && styleFrame->GetContent() != aElement) {
|
if (styleFrame && styleFrame->GetContent() != aElement) {
|
||||||
MOZ_ASSERT(static_cast<nsImageFrame*>(do_QueryFrame(styleFrame)));
|
MOZ_ASSERT(styleFrame->IsImageFrameOrSubclass());
|
||||||
styleFrame = nullptr;
|
styleFrame = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3515,7 +3515,9 @@ nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement,
|
||||||
const nsCSSFrameConstructor::FrameConstructionData*
|
const nsCSSFrameConstructor::FrameConstructionData*
|
||||||
nsCSSFrameConstructor::FindImgData(const Element& aElement,
|
nsCSSFrameConstructor::FindImgData(const Element& aElement,
|
||||||
ComputedStyle& aStyle) {
|
ComputedStyle& aStyle) {
|
||||||
if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyle)) {
|
if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
|
||||||
|
nsImageFrame::ImageFrameType::ForElementRequest) {
|
||||||
|
// content: url gets handled by the generic code-path.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3527,7 +3529,8 @@ nsCSSFrameConstructor::FindImgData(const Element& aElement,
|
||||||
const nsCSSFrameConstructor::FrameConstructionData*
|
const nsCSSFrameConstructor::FrameConstructionData*
|
||||||
nsCSSFrameConstructor::FindImgControlData(const Element& aElement,
|
nsCSSFrameConstructor::FindImgControlData(const Element& aElement,
|
||||||
ComputedStyle& aStyle) {
|
ComputedStyle& aStyle) {
|
||||||
if (!nsImageFrame::ShouldCreateImageFrameFor(aElement, aStyle)) {
|
if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) !=
|
||||||
|
nsImageFrame::ImageFrameType::ForElementRequest) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5356,7 +5359,8 @@ nsCSSFrameConstructor::FindElementData(const Element& aElement,
|
||||||
|
|
||||||
// Check for 'content: <image-url>' on the element (which makes us ignore
|
// Check for 'content: <image-url>' on the element (which makes us ignore
|
||||||
// 'display' values other than 'none' or 'contents').
|
// 'display' values other than 'none' or 'contents').
|
||||||
if (nsImageFrame::ShouldCreateImageFrameForContent(aElement, aStyle)) {
|
if (nsImageFrame::ShouldCreateImageFrameForContentProperty(aElement,
|
||||||
|
aStyle)) {
|
||||||
static constexpr FrameConstructionData sImgData(
|
static constexpr FrameConstructionData sImgData(
|
||||||
NS_NewImageFrameForContentProperty);
|
NS_NewImageFrameForContentProperty);
|
||||||
return &sImgData;
|
return &sImgData;
|
||||||
|
|
|
@ -7852,6 +7852,11 @@ bool nsIFrame::IsBlockFrameOrSubclass() const {
|
||||||
return !!thisAsBlock;
|
return !!thisAsBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nsIFrame::IsImageFrameOrSubclass() const {
|
||||||
|
const nsImageFrame* asImage = do_QueryFrame(this);
|
||||||
|
return !!asImage;
|
||||||
|
}
|
||||||
|
|
||||||
static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
|
static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
|
||||||
// The block wrappers we use to wrap blocks inside inlines aren't
|
// The block wrappers we use to wrap blocks inside inlines aren't
|
||||||
// described in the CSS spec. We need to make them not be containing
|
// described in the CSS spec. We need to make them not be containing
|
||||||
|
|
|
@ -3325,6 +3325,12 @@ class nsIFrame : public nsQueryFrame {
|
||||||
*/
|
*/
|
||||||
bool IsBlockFrameOrSubclass() const;
|
bool IsBlockFrameOrSubclass() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the frame is an instance of nsImageFrame or one of its
|
||||||
|
* subclasses.
|
||||||
|
*/
|
||||||
|
bool IsImageFrameOrSubclass() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the frame is an instance of SVGGeometryFrame or one
|
* Returns true if the frame is an instance of SVGGeometryFrame or one
|
||||||
* of its subclasses.
|
* of its subclasses.
|
||||||
|
|
|
@ -188,8 +188,8 @@ bool nsDisplayGradient::CreateWebRenderCommands(
|
||||||
StaticRefPtr<nsImageFrame::IconLoad> nsImageFrame::gIconLoad;
|
StaticRefPtr<nsImageFrame::IconLoad> nsImageFrame::gIconLoad;
|
||||||
|
|
||||||
// test if the width and height are fixed, looking at the style data
|
// test if the width and height are fixed, looking at the style data
|
||||||
// This is used by nsImageFrame::ShouldCreateImageFrameFor and should
|
// This is used by nsImageFrame::ImageFrameTypeFor and should not be used for
|
||||||
// not be used for layout decisions.
|
// layout decisions.
|
||||||
static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition) {
|
static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition) {
|
||||||
// check the width and height values in the reflow input's style struct
|
// check the width and height values in the reflow input's style struct
|
||||||
// - if width and height are specified as either coord or percentage, then
|
// - if width and height are specified as either coord or percentage, then
|
||||||
|
@ -834,7 +834,7 @@ static bool HasAltText(const Element& aElement) {
|
||||||
return aElement.HasNonEmptyAttr(nsGkAtoms::alt);
|
return aElement.HasNonEmptyAttr(nsGkAtoms::alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsImageFrame::ShouldCreateImageFrameForContent(
|
bool nsImageFrame::ShouldCreateImageFrameForContentProperty(
|
||||||
const Element& aElement, const ComputedStyle& aStyle) {
|
const Element& aElement, const ComputedStyle& aStyle) {
|
||||||
if (aElement.IsRootOfNativeAnonymousSubtree()) {
|
if (aElement.IsRootOfNativeAnonymousSubtree()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -848,39 +848,42 @@ bool nsImageFrame::ShouldCreateImageFrameForContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we want to use an image frame or just let the frame constructor make
|
// Check if we want to use an image frame or just let the frame constructor make
|
||||||
// us into an inline.
|
// us into an inline, and if so, which kind of image frame should we create.
|
||||||
/* static */
|
/* static */
|
||||||
bool nsImageFrame::ShouldCreateImageFrameFor(const Element& aElement,
|
auto nsImageFrame::ImageFrameTypeFor(const Element& aElement,
|
||||||
const ComputedStyle& aStyle) {
|
const ComputedStyle& aStyle)
|
||||||
if (ShouldCreateImageFrameForContent(aElement, aStyle)) {
|
-> ImageFrameType {
|
||||||
|
if (ShouldCreateImageFrameForContentProperty(aElement, aStyle)) {
|
||||||
// Prefer the content property, for compat reasons, see bug 1484928.
|
// Prefer the content property, for compat reasons, see bug 1484928.
|
||||||
return false;
|
return ImageFrameType::ForContentProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImageOk(aElement.State())) {
|
if (ImageOk(aElement.State())) {
|
||||||
// Image is fine or loading; do the image frame thing
|
// Image is fine or loading; do the image frame thing
|
||||||
return true;
|
return ImageFrameType::ForElementRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aStyle.StyleUIReset()->mMozForceBrokenImageIcon) {
|
if (aStyle.StyleUIReset()->mMozForceBrokenImageIcon) {
|
||||||
return true;
|
return ImageFrameType::ForElementRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if our "do not show placeholders" pref is set, skip the icon
|
// if our "do not show placeholders" pref is set, skip the icon
|
||||||
if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
|
if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
|
||||||
return false;
|
return ImageFrameType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HasAltText(aElement)) {
|
if (!HasAltText(aElement)) {
|
||||||
return true;
|
return ImageFrameType::ForElementRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aElement.OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks) {
|
// FIXME(emilio, bug 1788767): We definitely don't reframe when
|
||||||
// FIXME(emilio): We definitely don't reframe when this changes...
|
// HaveSpecifiedSize changes...
|
||||||
return HaveSpecifiedSize(aStyle.StylePosition());
|
if (aElement.OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks &&
|
||||||
|
HaveSpecifiedSize(aStyle.StylePosition())) {
|
||||||
|
return ImageFrameType::ForElementRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return ImageFrameType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType,
|
void nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType,
|
||||||
|
@ -936,7 +939,6 @@ void nsImageFrame::OnSizeAvailable(imgIRequest* aRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) {
|
void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) {
|
||||||
MOZ_ASSERT(aRequest);
|
|
||||||
if (SizeIsAvailable(aRequest)) {
|
if (SizeIsAvailable(aRequest)) {
|
||||||
// This is valid and for the current request, so update our stored image
|
// This is valid and for the current request, so update our stored image
|
||||||
// container, orienting according to our style.
|
// container, orienting according to our style.
|
||||||
|
@ -954,19 +956,8 @@ void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool intrinsicSizeOrRatioChanged = [&] {
|
|
||||||
// NOTE(emilio): We intentionally want to call both functions and avoid
|
|
||||||
// short-circuiting.
|
|
||||||
bool intrinsicSizeChanged = UpdateIntrinsicSize();
|
|
||||||
bool intrinsicRatioChanged = UpdateIntrinsicRatio();
|
|
||||||
return intrinsicSizeChanged || intrinsicRatioChanged;
|
|
||||||
}();
|
|
||||||
|
|
||||||
if (intrinsicSizeOrRatioChanged) {
|
UpdateIntrinsicSizeAndRatio();
|
||||||
// Our aspect-ratio property value changed, and an embedding <object> or
|
|
||||||
// <embed> might care about that.
|
|
||||||
MaybeSendIntrinsicSizeAndRatioToEmbedder();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GotInitialReflow()) {
|
if (!GotInitialReflow()) {
|
||||||
return;
|
return;
|
||||||
|
@ -974,19 +965,6 @@ void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) {
|
||||||
|
|
||||||
// We're going to need to repaint now either way.
|
// We're going to need to repaint now either way.
|
||||||
InvalidateFrame();
|
InvalidateFrame();
|
||||||
|
|
||||||
if (intrinsicSizeOrRatioChanged) {
|
|
||||||
// Now we need to reflow if we have an unconstrained size and have
|
|
||||||
// already gotten the initial reflow.
|
|
||||||
if (!(mState & IMAGE_SIZECONSTRAINED)) {
|
|
||||||
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
|
|
||||||
NS_FRAME_IS_DIRTY);
|
|
||||||
} else if (PresShell()->IsActive()) {
|
|
||||||
// We've already gotten the initial reflow, and our size hasn't changed,
|
|
||||||
// so we're ready to request a decode.
|
|
||||||
MaybeDecodeForPredictedSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsImageFrame::OnFrameUpdate(imgIRequest* aRequest,
|
void nsImageFrame::OnFrameUpdate(imgIRequest* aRequest,
|
||||||
|
@ -1085,16 +1063,32 @@ void nsImageFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus) {
|
||||||
NotifyNewCurrentRequest(aRequest, aStatus);
|
NotifyNewCurrentRequest(aRequest, aStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nsImageFrame::ElementStateChanged(ElementState aStates) {
|
||||||
|
if (!(aStates & ElementState::BROKEN)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mKind != Kind::ImageElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ImageOk(mContent->AsElement()->State())) {
|
||||||
|
UpdateImage(nullptr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void nsImageFrame::ResponsiveContentDensityChanged() {
|
void nsImageFrame::ResponsiveContentDensityChanged() {
|
||||||
if (!GotInitialReflow()) {
|
UpdateIntrinsicSizeAndRatio();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!mImage) {
|
void nsImageFrame::UpdateIntrinsicSizeAndRatio() {
|
||||||
return;
|
bool intrinsicSizeOrRatioChanged = [&] {
|
||||||
}
|
// NOTE(emilio): We intentionally want to call both functions and avoid
|
||||||
|
// short-circuiting.
|
||||||
|
bool intrinsicSizeChanged = UpdateIntrinsicSize();
|
||||||
|
bool intrinsicRatioChanged = UpdateIntrinsicRatio();
|
||||||
|
return intrinsicSizeChanged || intrinsicRatioChanged;
|
||||||
|
}();
|
||||||
|
|
||||||
if (!UpdateIntrinsicSize() && !UpdateIntrinsicRatio()) {
|
if (!intrinsicSizeOrRatioChanged) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1102,8 +1096,20 @@ void nsImageFrame::ResponsiveContentDensityChanged() {
|
||||||
// <embed> might care about that.
|
// <embed> might care about that.
|
||||||
MaybeSendIntrinsicSizeAndRatioToEmbedder();
|
MaybeSendIntrinsicSizeAndRatioToEmbedder();
|
||||||
|
|
||||||
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
|
if (!GotInitialReflow()) {
|
||||||
NS_FRAME_IS_DIRTY);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we need to reflow if we have an unconstrained size and have
|
||||||
|
// already gotten the initial reflow.
|
||||||
|
if (!HasAnyStateBits(IMAGE_SIZECONSTRAINED)) {
|
||||||
|
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
|
||||||
|
NS_FRAME_IS_DIRTY);
|
||||||
|
} else if (PresShell()->IsActive()) {
|
||||||
|
// We've already gotten the initial reflow, and our size hasn't changed,
|
||||||
|
// so we're ready to request a decode.
|
||||||
|
MaybeDecodeForPredictedSize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsImageFrame::NotifyNewCurrentRequest(imgIRequest* aRequest,
|
void nsImageFrame::NotifyNewCurrentRequest(imgIRequest* aRequest,
|
||||||
|
|
|
@ -99,6 +99,7 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
|
||||||
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) final;
|
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) final;
|
||||||
|
|
||||||
void ResponsiveContentDensityChanged();
|
void ResponsiveContentDensityChanged();
|
||||||
|
void ElementStateChanged(mozilla::dom::ElementState) override;
|
||||||
void SetupForContentURLRequest();
|
void SetupForContentURLRequest();
|
||||||
bool ShouldShowBrokenImageIcon() const;
|
bool ShouldShowBrokenImageIcon() const;
|
||||||
|
|
||||||
|
@ -140,16 +141,21 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
|
||||||
* Returns whether we should replace an element with an image corresponding to
|
* Returns whether we should replace an element with an image corresponding to
|
||||||
* its 'content' CSS property.
|
* its 'content' CSS property.
|
||||||
*/
|
*/
|
||||||
static bool ShouldCreateImageFrameForContent(const mozilla::dom::Element&,
|
static bool ShouldCreateImageFrameForContentProperty(
|
||||||
const ComputedStyle&);
|
const mozilla::dom::Element&, const ComputedStyle&);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to test whether given an element and its style, that element
|
* Function to test whether given an element and its style, that element
|
||||||
* should get an image frame. Note that this method is only used by the
|
* should get an image frame, and if so, which kind of image frame (for
|
||||||
* frame constructor; it's only here because it uses gIconLoad for now.
|
* `content`, or for the element itself).
|
||||||
*/
|
*/
|
||||||
static bool ShouldCreateImageFrameFor(const mozilla::dom::Element&,
|
enum class ImageFrameType {
|
||||||
const ComputedStyle&);
|
ForContentProperty,
|
||||||
|
ForElementRequest,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
static ImageFrameType ImageFrameTypeFor(const mozilla::dom::Element&,
|
||||||
|
const ComputedStyle&);
|
||||||
|
|
||||||
ImgDrawResult DisplayAltFeedback(gfxContext& aRenderingContext,
|
ImgDrawResult DisplayAltFeedback(gfxContext& aRenderingContext,
|
||||||
const nsRect& aDirtyRect, nsPoint aPt,
|
const nsRect& aDirtyRect, nsPoint aPt,
|
||||||
|
@ -218,6 +224,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
|
||||||
void ReflowChildren(nsPresContext*, const ReflowInput&,
|
void ReflowChildren(nsPresContext*, const ReflowInput&,
|
||||||
const mozilla::LogicalSize& aImageSize);
|
const mozilla::LogicalSize& aImageSize);
|
||||||
|
|
||||||
|
void UpdateIntrinsicSizeAndRatio();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, ClassID aID)
|
nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, ClassID aID)
|
||||||
: nsImageFrame(aStyle, aPresContext, aID, Kind::ImageElement) {}
|
: nsImageFrame(aStyle, aPresContext, aID, Kind::ImageElement) {}
|
||||||
|
@ -354,8 +362,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
|
||||||
bool IsPendingLoad(imgIRequest*) const;
|
bool IsPendingLoad(imgIRequest*) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates mImage based on the current image request (cannot be null), and the
|
* Updates mImage based on the current image request, and the image passed in
|
||||||
* image passed in (can be null), and invalidate layout and paint as needed.
|
* (both can be null), and invalidate layout and paint as needed.
|
||||||
*/
|
*/
|
||||||
void UpdateImage(imgIRequest*, imgIContainer*);
|
void UpdateImage(imgIRequest*, imgIContainer*);
|
||||||
|
|
||||||
|
|
|
@ -647,8 +647,12 @@ hr[size="1"] {
|
||||||
border-style: solid none none none;
|
border-style: solid none none none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:-moz-broken::before,
|
/* Note that we only intend for the alt content to show up if the image is
|
||||||
img:-moz-broken::before {
|
* broken. But non-broken images/inputs will have a replaced box, and thus we
|
||||||
|
* won't we don't generate the pseudo-element anyways. This prevents
|
||||||
|
* unnecessary reframing when images become broken / non-broken. */
|
||||||
|
input[type=image]::before,
|
||||||
|
img::before {
|
||||||
content: -moz-alt-content !important;
|
content: -moz-alt-content !important;
|
||||||
unicode-bidi: isolate;
|
unicode-bidi: isolate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,6 +267,7 @@ skip-if = verify || toolkit == 'android' # Bug 1455824
|
||||||
[test_hover_on_part.html]
|
[test_hover_on_part.html]
|
||||||
[test_html_attribute_computed_values.html]
|
[test_html_attribute_computed_values.html]
|
||||||
[test_ident_escaping.html]
|
[test_ident_escaping.html]
|
||||||
|
[test_img_src_causing_reflow.html]
|
||||||
[test_import_preload.html]
|
[test_import_preload.html]
|
||||||
support-files = slow_load.sjs
|
support-files = slow_load.sjs
|
||||||
# Test is slightly racy and on Android it fails frequently enough to be
|
# Test is slightly racy and on Android it fails frequently enough to be
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for bug 1787072</title>
|
||||||
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background-color: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<img> <!-- Initially broken -->
|
||||||
|
<script>
|
||||||
|
add_task(async function() {
|
||||||
|
const utils = SpecialPowers.DOMWindowUtils;
|
||||||
|
const img = document.querySelector("img");
|
||||||
|
img.getBoundingClientRect();
|
||||||
|
|
||||||
|
let origFramesConstructed = utils.framesConstructed;
|
||||||
|
let origFramesReflowed = utils.framesReflowed;
|
||||||
|
|
||||||
|
let error = new Promise(r => img.addEventListener("error", r, { once: true }));
|
||||||
|
|
||||||
|
// Doesn't need to be an actual image.
|
||||||
|
img.src = "/some-valid-url";
|
||||||
|
|
||||||
|
img.getBoundingClientRect();
|
||||||
|
is(origFramesReflowed, utils.framesReflowed, "Shouldn't have reflowed when going broken -> loading");
|
||||||
|
is(origFramesConstructed, utils.framesConstructed, "Shouldn't have reflowed when going broken -> loading");
|
||||||
|
|
||||||
|
await error;
|
||||||
|
|
||||||
|
is(origFramesReflowed, utils.framesReflowed, "Shouldn't have reflowed when going loading -> broken");
|
||||||
|
is(origFramesConstructed, utils.framesConstructed, "Shouldn't have reflowed when going loading -> broken");
|
||||||
|
});
|
||||||
|
</script>
|
Загрузка…
Ссылка в новой задаче