Bug 1606628 - Remove nsStyleImageRequest. r=tnikkel,heycam

This removes nsStyleImageRequest by moving the load state to LoadData instead
(where other lazy state like the resolved URL and load id lives).

That way we can use cbindgen for more stuff (there's no blocker for using it for
all images now), and we can undo the image tracking shenanigans that I had to do
in bug 1605803 in nsImageFrame.

This removes the mDocGroup member because well, there's no real upside of that
now that quantum DOM is not a thing.

It also removes the static clones of the image requests, and the need for each
computed value instance to have its own request. These were needed because we
needed the image loader for the particular document to observe the image
changes. But we were also tracking the request -> loader for other purposes.
Instead, Now all the images get loaded with GlobalImageObserver as a listener,
which looks in the image map and forwards the notification to all the interested
loaders instead dynamically.

The style value is only responsible to load the image, and no longer tracks /
locks it. Instead, the loader does so, via the image tracker.

Differential Revision: https://phabricator.services.mozilla.com/D58519
This commit is contained in:
Emilio Cobos Álvarez
2020-02-07 20:36:34 +00:00
parent f25a52b0d9
commit 31b33c3c16
18 changed files with 454 additions and 764 deletions

View File

@@ -56,7 +56,6 @@ class ImageTracker {
void SetAnimatingState(bool aAnimating); void SetAnimatingState(bool aAnimating);
void RequestDiscardAll(); void RequestDiscardAll();
void MediaFeatureValuesChangedAllDocuments(const MediaFeatureChange&); void MediaFeatureValuesChangedAllDocuments(const MediaFeatureChange&);
private: private:

View File

@@ -1525,7 +1525,6 @@ void nsPresContext::MediaFeatureValuesChangedAllDocuments(
// Propagate the media feature value change down to any SVG images the // Propagate the media feature value change down to any SVG images the
// document is using. // document is using.
mDocument->StyleImageLoader()->MediaFeatureValuesChangedAllDocuments(aChange);
mDocument->ImageTracker()->MediaFeatureValuesChangedAllDocuments(aChange); mDocument->ImageTracker()->MediaFeatureValuesChangedAllDocuments(aChange);
// And then into any subdocuments. // And then into any subdocuments.

View File

@@ -17,6 +17,7 @@
#include "mozilla/ComputedStyle.h" #include "mozilla/ComputedStyle.h"
#include "mozilla/DebugOnly.h" #include "mozilla/DebugOnly.h"
#include "mozilla/dom/ElementInlines.h" #include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/ImageTracker.h"
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
#include "mozilla/gfx/2D.h" #include "mozilla/gfx/2D.h"
#include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/gfxVars.h"
@@ -1163,7 +1164,7 @@ void nsFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
MaybeScheduleReflowSVGNonDisplayText(this); MaybeScheduleReflowSVGNonDisplayText(this);
Document* doc = PresContext()->Document(); Document* doc = PresContext()->Document();
ImageLoader* imageLoader = doc->StyleImageLoader(); ImageLoader* loader = doc->StyleImageLoader();
// Continuing text frame doesn't initialize its continuation pointer before // Continuing text frame doesn't initialize its continuation pointer before
// reaching here for the first time, so we have to exclude text frames. This // reaching here for the first time, so we have to exclude text frames. This
// doesn't affect correctness because text can't match selectors. // doesn't affect correctness because text can't match selectors.
@@ -1183,12 +1184,12 @@ void nsFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
: nullptr; : nullptr;
const nsStyleImageLayers* newLayers = &StyleBackground()->mImage; const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
AddAndRemoveImageAssociations(*imageLoader, this, oldLayers, newLayers); AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
oldLayers = oldLayers =
aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr; aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
newLayers = &StyleSVGReset()->mMask; newLayers = &StyleSVGReset()->mMask;
AddAndRemoveImageAssociations(*imageLoader, this, oldLayers, newLayers); AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
const nsStyleDisplay* disp = StyleDisplay(); const nsStyleDisplay* disp = StyleDisplay();
bool handleStickyChange = false; bool handleStickyChange = false;
@@ -1315,10 +1316,10 @@ void nsFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
if (oldBorderImage != newBorderImage) { if (oldBorderImage != newBorderImage) {
// stop and restart the image loading/notification // stop and restart the image loading/notification
if (oldBorderImage && HasImageRequest()) { if (oldBorderImage && HasImageRequest()) {
imageLoader->DisassociateRequestFromFrame(oldBorderImage, this); loader->DisassociateRequestFromFrame(oldBorderImage, this);
} }
if (newBorderImage) { if (newBorderImage) {
imageLoader->AssociateRequestToFrame(newBorderImage, this, 0); loader->AssociateRequestToFrame(newBorderImage, this, 0);
} }
} }
@@ -1331,11 +1332,11 @@ void nsFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
if (oldShapeImage != newShapeImage) { if (oldShapeImage != newShapeImage) {
if (oldShapeImage && HasImageRequest()) { if (oldShapeImage && HasImageRequest()) {
imageLoader->DisassociateRequestFromFrame(oldShapeImage, this); loader->DisassociateRequestFromFrame(oldShapeImage, this);
} }
if (newShapeImage) { if (newShapeImage) {
imageLoader->AssociateRequestToFrame( loader->AssociateRequestToFrame(newShapeImage, this,
newShapeImage, this, ImageLoader::REQUEST_REQUIRES_REFLOW); ImageLoader::REQUEST_REQUIRES_REFLOW);
} }
} }

View File

@@ -272,7 +272,6 @@ void nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot,
imageLoader->FrameDestroyed(this); imageLoader->FrameDestroyed(this);
imageLoader->RemoveNativeObserver(mListener); imageLoader->RemoveNativeObserver(mListener);
} else if (mContentURLRequest) { } else if (mContentURLRequest) {
PresContext()->Document()->ImageTracker()->Remove(mContentURLRequest);
nsLayoutUtils::DeregisterImageRequest(PresContext(), mContentURLRequest, nsLayoutUtils::DeregisterImageRequest(PresContext(), mContentURLRequest,
&mContentURLRequestRegistered); &mContentURLRequestRegistered);
mContentURLRequest->Cancel(NS_BINDING_ABORTED); mContentURLRequest->Cancel(NS_BINDING_ABORTED);
@@ -368,7 +367,7 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
auto& url = const_cast<StyleComputedUrl&>( auto& url = const_cast<StyleComputedUrl&>(
styleContent->ContentAt(contentIndex).AsUrl()); styleContent->ContentAt(contentIndex).AsUrl());
Document* doc = PresContext()->Document(); Document* doc = PresContext()->Document();
if (RefPtr<imgRequestProxy> proxy = url.LoadImage(*doc)) { if (imgRequestProxy* proxy = url.GetImage()) {
proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest)); proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest));
SetupForContentURLRequest(); SetupForContentURLRequest();
} }
@@ -394,9 +393,6 @@ void nsImageFrame::SetupForContentURLRequest() {
return; return;
} }
// We're not using nsStyleImageRequest, so manually track the image.
PresContext()->Document()->ImageTracker()->Add(mContentURLRequest);
uint32_t status = 0; uint32_t status = 0;
nsresult rv = mContentURLRequest->GetImageStatus(&status); nsresult rv = mContentURLRequest->GetImageStatus(&status);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {

View File

@@ -1115,19 +1115,10 @@ void Gecko_SetGradientImageValue(nsStyleImage* aImage,
aImage->SetGradientData(UniquePtr<StyleGradient>(aGradient)); aImage->SetGradientData(UniquePtr<StyleGradient>(aGradient));
} }
static already_AddRefed<nsStyleImageRequest> CreateStyleImageRequest(
nsStyleImageRequest::Mode aModeFlags, const StyleComputedImageUrl* aUrl) {
RefPtr<nsStyleImageRequest> req = new nsStyleImageRequest(aModeFlags, *aUrl);
return req.forget();
}
void Gecko_SetLayerImageImageValue(nsStyleImage* aImage, void Gecko_SetLayerImageImageValue(nsStyleImage* aImage,
const StyleComputedImageUrl* aUrl) { const StyleComputedImageUrl* aUrl) {
MOZ_ASSERT(aImage && aUrl); MOZ_ASSERT(aImage && aUrl);
aImage->SetImageUrl(*aUrl);
RefPtr<nsStyleImageRequest> req =
CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aUrl);
aImage->SetImageRequest(req.forget());
} }
void Gecko_SetImageElement(nsStyleImage* aImage, nsAtom* aAtom) { void Gecko_SetImageElement(nsStyleImage* aImage, nsAtom* aAtom) {
@@ -1150,50 +1141,25 @@ void Gecko_InitializeImageCropRect(nsStyleImage* aImage) {
nsStyleImage::CropRect{zero, zero, zero, zero})); nsStyleImage::CropRect{zero, zero, zero, zero}));
} }
void Gecko_SetCursorArrayLength(nsStyleUI* aStyleUI, size_t aLen) { void Gecko_SetCursorArrayCapacity(nsStyleUI* aUi, size_t aCapacity) {
aStyleUI->mCursorImages.Clear(); aUi->mCursorImages.Clear();
aStyleUI->mCursorImages.SetLength(aLen); aUi->mCursorImages.SetCapacity(aCapacity);
} }
void Gecko_SetCursorImageValue(nsCursorImage* aCursor, void Gecko_AppendCursorImage(nsStyleUI* aUi,
const StyleComputedImageUrl* aUrl) { const StyleComputedImageUrl* aUrl) {
MOZ_ASSERT(aCursor && aUrl); aUi->mCursorImages.EmplaceBack(*aUrl);
aCursor->mImage =
CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard, aUrl);
} }
void Gecko_CopyCursorArrayFrom(nsStyleUI* aDest, const nsStyleUI* aSrc) { void Gecko_CopyCursorArrayFrom(nsStyleUI* aDest, const nsStyleUI* aSrc) {
aDest->mCursorImages = aSrc->mCursorImages; aDest->mCursorImages = aSrc->mCursorImages;
} }
const nsStyleImageRequest* Gecko_GetImageRequest(const nsStyleImage* aImage) {
MOZ_ASSERT(aImage);
return aImage->ImageRequest();
}
nsAtom* Gecko_GetImageElement(const nsStyleImage* aImage) { nsAtom* Gecko_GetImageElement(const nsStyleImage* aImage) {
MOZ_ASSERT(aImage && aImage->GetType() == eStyleImageType_Element); MOZ_ASSERT(aImage && aImage->GetType() == eStyleImageType_Element);
return const_cast<nsAtom*>(aImage->GetElementId()); return const_cast<nsAtom*>(aImage->GetElementId());
} }
void Gecko_SetListStyleImageNone(nsStyleList* aList) {
aList->mListStyleImage = nullptr;
}
void Gecko_SetListStyleImageImageValue(nsStyleList* aList,
const StyleComputedImageUrl* aUrl) {
MOZ_ASSERT(aList && aUrl);
aList->mListStyleImage =
CreateStyleImageRequest(nsStyleImageRequest::Mode(0), aUrl);
}
void Gecko_CopyListStyleImageFrom(nsStyleList* aList,
const nsStyleList* aSource) {
aList->mListStyleImage = aSource->mListStyleImage;
}
void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity, void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity,
size_t aElemSize) { size_t aElemSize) {
auto base = reinterpret_cast< auto base = reinterpret_cast<
@@ -1879,11 +1845,6 @@ bool Gecko_AssertClassAttrValueIsSane(const nsAttrValue* aValue) {
return true; return true;
} }
void Gecko_LoadData_DeregisterLoad(const StyleLoadData* aData) {
MOZ_ASSERT(aData->load_id != 0);
ImageLoader::DeregisterCSSImageFromAllLoaders(*aData);
}
void Gecko_PrintfStderr(const nsCString* aStr) { void Gecko_PrintfStderr(const nsCString* aStr) {
printf_stderr("%s", aStr->get()); printf_stderr("%s", aStr->get());
} }

View File

@@ -349,7 +349,6 @@ void Gecko_SetImageElement(nsStyleImage* image, nsAtom* atom);
void Gecko_CopyImageValueFrom(nsStyleImage* image, const nsStyleImage* other); void Gecko_CopyImageValueFrom(nsStyleImage* image, const nsStyleImage* other);
void Gecko_InitializeImageCropRect(nsStyleImage* image); void Gecko_InitializeImageCropRect(nsStyleImage* image);
const nsStyleImageRequest* Gecko_GetImageRequest(const nsStyleImage* image);
nsAtom* Gecko_GetImageElement(const nsStyleImage* image); nsAtom* Gecko_GetImageElement(const nsStyleImage* image);
// list-style-image style. // list-style-image style.
@@ -361,11 +360,8 @@ void Gecko_SetListStyleImageImageValue(
void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src); void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src);
// cursor style. // cursor style.
void Gecko_SetCursorArrayLength(nsStyleUI* ui, size_t len); void Gecko_SetCursorArrayCapacity(nsStyleUI*, size_t);
void Gecko_AppendCursorImage(nsStyleUI*, const mozilla::StyleComputedImageUrl*);
void Gecko_SetCursorImageValue(nsCursorImage* aCursor,
const mozilla::StyleComputedImageUrl* url);
void Gecko_CopyCursorArrayFrom(nsStyleUI* dest, const nsStyleUI* src); void Gecko_CopyCursorArrayFrom(nsStyleUI* dest, const nsStyleUI* src);
// Dirtiness tracking. // Dirtiness tracking.
@@ -506,7 +502,7 @@ float Gecko_FontStretch_ToFloat(mozilla::FontStretch aStretch);
void Gecko_FontStretch_SetFloat(mozilla::FontStretch* aStretch, void Gecko_FontStretch_SetFloat(mozilla::FontStretch* aStretch,
float aFloatValue); float aFloatValue);
void Gecko_LoadData_DeregisterLoad(const mozilla::StyleLoadData*); void Gecko_LoadData_Drop(mozilla::StyleLoadData*);
float Gecko_FontSlantStyle_ToFloat(mozilla::FontSlantStyle aStyle); float Gecko_FontSlantStyle_ToFloat(mozilla::FontSlantStyle aStyle);
void Gecko_FontSlantStyle_SetNormal(mozilla::FontSlantStyle*); void Gecko_FontSlantStyle_SetNormal(mozilla::FontSlantStyle*);

View File

@@ -12,6 +12,7 @@
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/ImageTracker.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsLayoutUtils.h" #include "nsLayoutUtils.h"
#include "nsError.h" #include "nsError.h"
@@ -30,15 +31,59 @@ using namespace mozilla::dom;
namespace mozilla { namespace mozilla {
namespace css { namespace css {
// This is a singleton observer which looks in the `GlobalRequestTable` to look
// at which loaders to notify.
struct GlobalImageObserver final : public imgINotificationObserver {
NS_DECL_ISUPPORTS
NS_DECL_IMGINOTIFICATIONOBSERVER
GlobalImageObserver() = default;
private:
virtual ~GlobalImageObserver() = default;
};
NS_IMPL_ADDREF(GlobalImageObserver)
NS_IMPL_RELEASE(GlobalImageObserver)
NS_INTERFACE_MAP_BEGIN(GlobalImageObserver)
NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
NS_INTERFACE_MAP_END
// Data associated with every started load.
struct ImageTableEntry {
// Set of all ImageLoaders that have registered this URL and care for updates
// for it.
nsTHashtable<nsPtrHashKey<ImageLoader>> mImageLoaders;
// The amount of style values that are sharing this image.
uint32_t mSharedCount = 1;
};
using GlobalRequestTable =
nsClassHashtable<nsRefPtrHashKey<imgIRequest>, ImageTableEntry>;
// A table of all loads, keyed by their id mapping them to the set of
// ImageLoaders they have been registered in, and recording their "canonical"
// image request.
//
// We use the load id as the key since we can only access sImages on the
// main thread, but LoadData objects might be destroyed from other threads,
// and we don't want to leave dangling pointers around.
static GlobalRequestTable* sImages = nullptr;
static StaticRefPtr<GlobalImageObserver> sImageObserver;
/* static */ /* static */
void ImageLoader::Init() { void ImageLoader::Init() {
sImages = new nsClassHashtable<nsUint64HashKey, ImageTableEntry>(); sImages = new GlobalRequestTable();
sImageObserver = new GlobalImageObserver();
} }
/* static */ /* static */
void ImageLoader::Shutdown() { void ImageLoader::Shutdown() {
delete sImages; delete sImages;
sImages = nullptr; sImages = nullptr;
sImageObserver = nullptr;
} }
void ImageLoader::DropDocumentReference() { void ImageLoader::DropDocumentReference() {
@@ -49,21 +94,6 @@ void ImageLoader::DropDocumentReference() {
// been destroyed, and it also calls ClearFrames when it is destroyed. // been destroyed, and it also calls ClearFrames when it is destroyed.
ClearFrames(GetPresContext()); ClearFrames(GetPresContext());
for (auto it = mRegisteredImages.Iter(); !it.Done(); it.Next()) {
if (imgRequestProxy* request = it.Data()) {
request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
// Need to check whether the entry exists, since the css::URLValue might
// go away before ImageLoader::DropDocumentReference is called.
uint64_t imageLoadID = it.Key();
if (auto entry = sImages->Lookup(imageLoadID)) {
entry.Data()->mImageLoaders.RemoveEntry(this);
}
}
mRegisteredImages.Clear();
mDocument = nullptr; mDocument = nullptr;
} }
@@ -90,20 +120,31 @@ void ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
nsIFrame* aFrame, FrameFlags aFlags) { nsIFrame* aFrame, FrameFlags aFlags) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<imgINotificationObserver> observer; {
aRequest->GetNotificationObserver(getter_AddRefs(observer)); nsCOMPtr<imgINotificationObserver> observer;
if (!observer) { aRequest->GetNotificationObserver(getter_AddRefs(observer));
// The request has already been canceled, so ignore it. This is ok because if (!observer) {
// we're not going to get any more notifications from a canceled request. // The request has already been canceled, so ignore it. This is ok because
return; // we're not going to get any more notifications from a canceled request.
return;
}
MOZ_ASSERT(observer == sImageObserver);
} }
MOZ_ASSERT(observer == this);
const auto& frameSet = const auto& frameSet =
mRequestToFrameMap.LookupForAdd(aRequest).OrInsert([=]() { mRequestToFrameMap.LookupForAdd(aRequest).OrInsert([=]() {
nsPresContext* presContext = GetPresContext(); mDocument->ImageTracker()->Add(aRequest);
if (presContext) {
if (auto entry = sImages->Lookup(aRequest)) {
DebugOnly<bool> inserted =
entry.Data()->mImageLoaders.EnsureInserted(this);
MOZ_ASSERT(inserted);
} else {
MOZ_ASSERT_UNREACHABLE(
"Shouldn't be associating images not in sImages");
}
if (nsPresContext* presContext = GetPresContext()) {
nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, aRequest, nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, aRequest,
nullptr); nullptr);
} }
@@ -204,100 +245,13 @@ void ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
"We should only add to one map iff we also add to the other map."); "We should only add to one map iff we also add to the other map.");
} }
imgRequestProxy* ImageLoader::RegisterCSSImage(const StyleLoadData& aData) {
MOZ_ASSERT(NS_IsMainThread());
uint64_t loadId = aData.load_id;
if (loadId == 0) {
MOZ_ASSERT_UNREACHABLE("Image should have a valid LoadID");
return nullptr;
}
if (imgRequestProxy* request = mRegisteredImages.GetWeak(loadId)) {
// This document already has a request.
return request;
}
imgRequestProxy* canonicalRequest = nullptr;
{
auto entry = sImages->Lookup(loadId);
if (entry) {
canonicalRequest = entry.Data()->mCanonicalRequest;
}
if (!canonicalRequest) {
// The image was blocked or something.
return nullptr;
}
entry.Data()->mImageLoaders.PutEntry(this);
}
RefPtr<imgRequestProxy> request;
// Ignore errors here. If cloning fails for some reason we'll put a null
// entry in the hash and we won't keep trying to clone.
mInClone = true;
canonicalRequest->SyncClone(this, mDocument, getter_AddRefs(request));
mInClone = false;
MOZ_ASSERT(!mRegisteredImages.Contains(loadId));
imgRequestProxy* requestWeak = request;
mRegisteredImages.Put(loadId, request.forget());
return requestWeak;
}
/* static */
void ImageLoader::DeregisterCSSImageFromAllLoaders(const StyleLoadData& aData) {
uint64_t loadID = aData.load_id;
if (loadID == 0) {
MOZ_ASSERT_UNREACHABLE("Image should have a valid LoadID");
return;
}
if (NS_IsMainThread()) {
DeregisterCSSImageFromAllLoaders(loadID);
} else {
NS_DispatchToMainThread(NS_NewRunnableFunction(
"css::ImageLoader::DeregisterCSSImageFromAllLoaders",
[loadID] { DeregisterCSSImageFromAllLoaders(loadID); }));
}
}
/* static */
void ImageLoader::DeregisterCSSImageFromAllLoaders(uint64_t aImageLoadID) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aImageLoadID != 0);
if (auto e = sImages->Lookup(aImageLoadID)) {
const auto& tableEntry = e.Data();
if (imgRequestProxy* request = tableEntry->mCanonicalRequest) {
request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
for (auto iter = tableEntry->mImageLoaders.Iter(); !iter.Done();
iter.Next()) {
ImageLoader* loader = iter.Get()->GetKey();
if (auto e = loader->mRegisteredImages.Lookup(aImageLoadID)) {
if (imgRequestProxy* request = e.Data()) {
request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
e.Remove();
}
}
e.Remove();
}
}
void ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest, void ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest,
nsIFrame* aFrame) { nsIFrame* aFrame) {
#ifdef DEBUG #ifdef DEBUG
{ {
nsCOMPtr<imgINotificationObserver> observer; nsCOMPtr<imgINotificationObserver> observer;
aRequest->GetNotificationObserver(getter_AddRefs(observer)); aRequest->GetNotificationObserver(getter_AddRefs(observer));
MOZ_ASSERT(!observer || observer == this); MOZ_ASSERT(!observer || observer == sImageObserver);
} }
#endif #endif
@@ -320,15 +274,25 @@ void ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest,
} }
if (frameSet->IsEmpty()) { if (frameSet->IsEmpty()) {
nsPresContext* presContext = GetPresContext(); DeregisterImageRequest(aRequest, GetPresContext());
if (presContext) {
nsLayoutUtils::DeregisterImageRequest(presContext, aRequest, nullptr);
}
entry.Remove(); entry.Remove();
} }
} }
} }
void ImageLoader::DeregisterImageRequest(imgIRequest* aRequest,
nsPresContext* aPresContext) {
mDocument->ImageTracker()->Remove(aRequest);
if (auto entry = sImages->Lookup(aRequest)) {
entry.Data()->mImageLoaders.EnsureRemoved(this);
}
if (aPresContext) {
nsLayoutUtils::DeregisterImageRequest(aPresContext, aRequest, nullptr);
}
}
void ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest, void ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest,
nsIFrame* aFrame) { nsIFrame* aFrame) {
if (auto entry = mFrameToRequestMap.Lookup(aFrame)) { if (auto entry = mFrameToRequestMap.Lookup(aFrame)) {
@@ -412,9 +376,7 @@ void ImageLoader::ClearFrames(nsPresContext* aPresContext) {
} }
#endif #endif
if (aPresContext) { DeregisterImageRequest(request, aPresContext);
nsLayoutUtils::DeregisterImageRequest(aPresContext, request, nullptr);
}
} }
mRequestToFrameMap.Clear(); mRequestToFrameMap.Clear();
@@ -436,25 +398,12 @@ static CORSMode EffectiveCorsMode(nsIURI* aURI,
} }
/* static */ /* static */
void ImageLoader::LoadImage(const StyleComputedImageUrl& aImage, already_AddRefed<imgRequestProxy> ImageLoader::LoadImage(
Document& aLoadingDoc) { const StyleComputedImageUrl& aImage, Document& aLoadingDoc) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
uint64_t loadId = aImage.LoadData().load_id;
if (loadId == 0) {
MOZ_ASSERT_UNREACHABLE("Image should have a valid LoadID");
return;
}
auto lookup = sImages->LookupForAdd(loadId);
if (lookup) {
// This url has already been loaded.
return;
}
const auto& entry = lookup.OrInsert([]() { return new ImageTableEntry(); });
nsIURI* uri = aImage.GetURI(); nsIURI* uri = aImage.GetURI();
if (!uri) { if (!uri) {
return; return nullptr;
} }
int32_t loadFlags = int32_t loadFlags =
@@ -466,13 +415,47 @@ void ImageLoader::LoadImage(const StyleComputedImageUrl& aImage,
RefPtr<imgRequestProxy> request; RefPtr<imgRequestProxy> request;
nsresult rv = nsContentUtils::LoadImage( nsresult rv = nsContentUtils::LoadImage(
uri, &aLoadingDoc, &aLoadingDoc, data.Principal(), 0, data.ReferrerInfo(), uri, &aLoadingDoc, &aLoadingDoc, data.Principal(), 0, data.ReferrerInfo(),
nullptr, loadFlags, NS_LITERAL_STRING("css"), getter_AddRefs(request)); sImageObserver, loadFlags, NS_LITERAL_STRING("css"),
getter_AddRefs(request));
if (NS_FAILED(rv) || !request) { if (NS_FAILED(rv) || !request) {
return nullptr;
}
sImages->LookupForAdd(request).OrInsert([] { return new ImageTableEntry(); });
return request.forget();
}
void ImageLoader::UnloadImage(imgRequestProxy* aImage) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aImage);
auto lookup = sImages->Lookup(aImage);
MOZ_DIAGNOSTIC_ASSERT(lookup, "Unregistered image?");
if (MOZ_UNLIKELY(!lookup)) {
return; return;
} }
entry->mCanonicalRequest = std::move(request); if (MOZ_UNLIKELY(--lookup.Data()->mSharedCount)) {
// Someone else still cares about this image.
return;
}
aImage->CancelAndForgetObserver(NS_BINDING_ABORTED);
lookup.Remove();
}
void ImageLoader::NoteSharedLoad(imgRequestProxy* aImage) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aImage);
auto lookup = sImages->Lookup(aImage);
MOZ_DIAGNOSTIC_ASSERT(lookup, "Unregistered image?");
if (MOZ_UNLIKELY(!lookup)) {
return;
}
lookup.Data()->mSharedCount++;
} }
nsPresContext* ImageLoader::GetPresContext() { nsPresContext* ImageLoader::GetPresContext() {
@@ -611,6 +594,8 @@ void ImageLoader::RequestReflowOnFrame(FrameWithFlags* aFwf,
nsIFrame* frame = aFwf->mFrame; nsIFrame* frame = aFwf->mFrame;
// Actually request the reflow. // Actually request the reflow.
//
// FIXME(emilio): Why requesting reflow on the _parent_?
nsIFrame* parent = frame->GetInFlowParent(); nsIFrame* parent = frame->GetInFlowParent();
parent->PresShell()->FrameNeedsReflow(parent, IntrinsicDirty::StyleChange, parent->PresShell()->FrameNeedsReflow(parent, IntrinsicDirty::StyleChange,
NS_FRAME_IS_DIRTY); NS_FRAME_IS_DIRTY);
@@ -618,21 +603,32 @@ void ImageLoader::RequestReflowOnFrame(FrameWithFlags* aFwf,
// We'll respond to the reflow events by unblocking onload, regardless // We'll respond to the reflow events by unblocking onload, regardless
// of whether the reflow was completed or cancelled. The callback will // of whether the reflow was completed or cancelled. The callback will
// also delete itself when it is called. // also delete itself when it is called.
ImageReflowCallback* unblocker = auto* unblocker = new ImageReflowCallback(this, frame, aRequest);
new ImageReflowCallback(this, frame, aRequest);
parent->PresShell()->PostReflowCallback(unblocker); parent->PresShell()->PostReflowCallback(unblocker);
} }
NS_IMPL_ADDREF(ImageLoader)
NS_IMPL_RELEASE(ImageLoader)
NS_INTERFACE_MAP_BEGIN(ImageLoader)
NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
NS_INTERFACE_MAP_END
NS_IMETHODIMP NS_IMETHODIMP
ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, GlobalImageObserver::Notify(imgIRequest* aRequest, int32_t aType,
const nsIntRect* aData) { const nsIntRect* aData) {
auto entry = sImages->Lookup(aRequest);
MOZ_DIAGNOSTIC_ASSERT(entry);
if (MOZ_UNLIKELY(!entry)) {
return NS_OK;
}
auto& loaders = entry.Data()->mImageLoaders;
nsTArray<RefPtr<ImageLoader>> loadersToNotify(loaders.Count());
for (auto iter = loaders.Iter(); !iter.Done(); iter.Next()) {
loadersToNotify.AppendElement(iter.Get()->GetKey());
}
for (auto& loader : loadersToNotify) {
loader->Notify(aRequest, aType, aData);
}
return NS_OK;
}
nsresult ImageLoader::Notify(imgIRequest* aRequest, int32_t aType,
const nsIntRect* aData) {
#ifdef MOZ_GECKO_PROFILER #ifdef MOZ_GECKO_PROFILER
nsCString uriString; nsCString uriString;
if (profiler_is_active()) { if (profiler_is_active()) {
@@ -724,7 +720,7 @@ nsresult ImageLoader::OnImageIsAnimated(imgIRequest* aRequest) {
} }
nsresult ImageLoader::OnFrameComplete(imgIRequest* aRequest) { nsresult ImageLoader::OnFrameComplete(imgIRequest* aRequest) {
if (!mDocument || mInClone) { if (!mDocument) {
return NS_OK; return NS_OK;
} }
@@ -745,7 +741,7 @@ nsresult ImageLoader::OnFrameComplete(imgIRequest* aRequest) {
} }
nsresult ImageLoader::OnFrameUpdate(imgIRequest* aRequest) { nsresult ImageLoader::OnFrameUpdate(imgIRequest* aRequest) {
if (!mDocument || mInClone) { if (!mDocument) {
return NS_OK; return NS_OK;
} }
@@ -760,7 +756,7 @@ nsresult ImageLoader::OnFrameUpdate(imgIRequest* aRequest) {
} }
nsresult ImageLoader::OnLoadComplete(imgIRequest* aRequest) { nsresult ImageLoader::OnLoadComplete(imgIRequest* aRequest) {
if (!mDocument || mInClone) { if (!mDocument) {
return NS_OK; return NS_OK;
} }
@@ -788,30 +784,6 @@ nsresult ImageLoader::OnLoadComplete(imgIRequest* aRequest) {
return NS_OK; return NS_OK;
} }
void ImageLoader::MediaFeatureValuesChangedAllDocuments(
const MediaFeatureChange& aChange) {
// Inform every CSS image used in the document that media feature values have
// changed. If the same image is used in multiple places, then we can end up
// informing them multiple times. Theme changes are rare though and we don't
// bother trying to ensure we only do this once per image.
//
// Pull the images out into an array and iterate over them, in case the
// image notifications do something that ends up modifying the table.
nsTArray<nsCOMPtr<imgIContainer>> images;
for (auto iter = mRegisteredImages.Iter(); !iter.Done(); iter.Next()) {
imgRequestProxy* req = iter.Data();
nsCOMPtr<imgIContainer> image;
req->GetImage(getter_AddRefs(image));
if (!image) {
continue;
}
images.AppendElement(image->Unwrap());
}
for (imgIContainer* image : images) {
image->MediaFeatureValuesChangedAllDocuments(aChange);
}
}
bool ImageLoader::ImageReflowCallback::ReflowFinished() { bool ImageLoader::ImageReflowCallback::ReflowFinished() {
// Check that the frame is still valid. If it isn't, then onload was // Check that the frame is still valid. If it isn't, then onload was
// unblocked when the frame was removed from the FrameSet in // unblocked when the frame was removed from the FrameSet in
@@ -839,8 +811,5 @@ void ImageLoader::ImageReflowCallback::ReflowCallbackCanceled() {
delete this; delete this;
} }
nsClassHashtable<nsUint64HashKey, ImageLoader::ImageTableEntry>*
ImageLoader::sImages = nullptr;
} // namespace css } // namespace css
} // namespace mozilla } // namespace mozilla

View File

@@ -19,7 +19,6 @@
#include "imgIRequest.h" #include "imgIRequest.h"
#include "imgINotificationObserver.h" #include "imgINotificationObserver.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/MediaFeatureChange.h"
class imgIContainer; class imgIContainer;
class nsIFrame; class nsIFrame;
@@ -38,7 +37,7 @@ namespace css {
* NOTE: All methods must be called from the main thread unless otherwise * NOTE: All methods must be called from the main thread unless otherwise
* specified. * specified.
*/ */
class ImageLoader final : public imgINotificationObserver { class ImageLoader final {
public: public:
static void Init(); static void Init();
static void Shutdown(); static void Shutdown();
@@ -52,17 +51,14 @@ class ImageLoader final : public imgINotificationObserver {
}; };
explicit ImageLoader(dom::Document* aDocument) explicit ImageLoader(dom::Document* aDocument)
: mDocument(aDocument), mInClone(false) { : mDocument(aDocument) {
MOZ_ASSERT(mDocument); MOZ_ASSERT(mDocument);
} }
NS_DECL_ISUPPORTS NS_INLINE_DECL_REFCOUNTING(ImageLoader)
NS_DECL_IMGINOTIFICATIONOBSERVER
void DropDocumentReference(); void DropDocumentReference();
imgRequestProxy* RegisterCSSImage(const StyleLoadData& aImage);
void AssociateRequestToFrame(imgIRequest* aRequest, nsIFrame* aFrame, void AssociateRequestToFrame(imgIRequest* aRequest, nsIFrame* aFrame,
FrameFlags aFlags); FrameFlags aFlags);
@@ -72,26 +68,34 @@ class ImageLoader final : public imgINotificationObserver {
void SetAnimationMode(uint16_t aMode); void SetAnimationMode(uint16_t aMode);
/**
* Called by the document's pres context when media features in all
* SVG images must be re-evaluated.
*/
void MediaFeatureValuesChangedAllDocuments(const MediaFeatureChange& aChange);
// The prescontext for this ImageLoader's document. We need it to be passed // The prescontext for this ImageLoader's document. We need it to be passed
// in because this can be called during presentation destruction after the // in because this can be called during presentation destruction after the
// presshell pointer on the document has been cleared. // presshell pointer on the document has been cleared.
void ClearFrames(nsPresContext* aPresContext); void ClearFrames(nsPresContext* aPresContext);
static void LoadImage(const StyleComputedImageUrl& aImage, dom::Document&); // Triggers an image load.
static already_AddRefed<imgRequestProxy> LoadImage(
const StyleComputedImageUrl&, dom::Document&);
// Cancels the image load for the given LoadData and deregisters it from any // Usually, only one style value owns a given proxy. However, we have a hack
// ImageLoaders it was registered with. // to share image proxies in chrome documents under some circumstances. We
// need to keep track of this so that we don't stop tracking images too early.
// //
// May be called from any thread. // In practice it shouldn't matter as these chrome images are mostly static,
static void DeregisterCSSImageFromAllLoaders(const StyleLoadData&); // but it is always good to keep sanity.
static void NoteSharedLoad(imgRequestProxy*);
// Undoes what `LoadImage` does.
static void UnloadImage(imgRequestProxy*);
// This is called whenever an image we care about notifies the
// GlobalImageObserver.
nsresult Notify(imgIRequest*, int32_t aType, const nsIntRect* aData);
private: private:
// Called when we stop caring about a given request.
void DeregisterImageRequest(imgIRequest*, nsPresContext*);
// This callback is used to unblock document onload after a reflow // This callback is used to unblock document onload after a reflow
// triggered from an image load. // triggered from an image load.
struct ImageReflowCallback final : public nsIReflowCallback { struct ImageReflowCallback final : public nsIReflowCallback {
@@ -161,9 +165,6 @@ class ImageLoader final : public imgINotificationObserver {
void RemoveRequestToFrameMapping(imgIRequest* aRequest, nsIFrame* aFrame); void RemoveRequestToFrameMapping(imgIRequest* aRequest, nsIFrame* aFrame);
void RemoveFrameToRequestMapping(imgIRequest* aRequest, nsIFrame* aFrame); void RemoveFrameToRequestMapping(imgIRequest* aRequest, nsIFrame* aFrame);
// Helper for the public DeregisterCSSImageFromAllLoaders overload above.
static void DeregisterCSSImageFromAllLoaders(uint64_t aLoadID);
// A map of imgIRequests to the nsIFrames that are using them. // A map of imgIRequests to the nsIFrames that are using them.
RequestToFrameMap mRequestToFrameMap; RequestToFrameMap mRequestToFrameMap;
@@ -172,40 +173,6 @@ class ImageLoader final : public imgINotificationObserver {
// A weak pointer to our document. Nulled out by DropDocumentReference. // A weak pointer to our document. Nulled out by DropDocumentReference.
dom::Document* mDocument; dom::Document* mDocument;
// A map of css ComputedUrls, keyed by their LoadID(), to the imgRequestProxy
// representing the load of the image for this ImageLoader's document.
//
// We use the LoadID() as the key since we can only access mRegisteredImages
// on the main thread, but Urls might be destroyed from other threads, and we
// don't want to leave dangling pointers around.
nsRefPtrHashtable<nsUint64HashKey, imgRequestProxy> mRegisteredImages;
// Are we cloning? If so, ignore any notifications we get.
bool mInClone;
// Data associated with every started load.
struct ImageTableEntry {
// Set of all ImageLoaders that have registered this URL.
nsTHashtable<nsPtrHashKey<ImageLoader>> mImageLoaders;
// The "canonical" image request for this URL.
//
// This request is held on to as long as the specified URL is, so that any
// image that has already started loading (or has completed loading) will
// stay alive even if all computed values referencing the image requesst
// have gone away.
RefPtr<imgRequestProxy> mCanonicalRequest;
};
// A table of all loads, keyed by their id mapping them to the set of
// ImageLoaders they have been registered in, and recording their "canonical"
// image request.
//
// We use the load id as the key since we can only access sImages on the
// main thread, but LoadData objects might be destroyed from other threads,
// and we don't want to leave dangling pointers around.
static nsClassHashtable<nsUint64HashKey, ImageTableEntry>* sImages;
}; };
} // namespace css } // namespace css

View File

@@ -115,6 +115,8 @@ enum class CallerType : uint32_t;
class Element; class Element;
class Document; class Document;
class ImageTracker;
} // namespace dom } // namespace dom
namespace ipc { namespace ipc {
@@ -175,6 +177,7 @@ struct StyleBox {
// Work-around weird cbindgen renaming / avoiding moving stuff outside its // Work-around weird cbindgen renaming / avoiding moving stuff outside its
// namespace. // namespace.
using StyleImageTracker = dom::ImageTracker;
using StyleLoader = css::Loader; using StyleLoader = css::Loader;
using StyleLoaderReusableStyleSheets = css::LoaderReusableStyleSheets; using StyleLoaderReusableStyleSheets = css::LoaderReusableStyleSheets;
using StyleCallerType = dom::CallerType; using StyleCallerType = dom::CallerType;

View File

@@ -390,12 +390,14 @@ inline StyleLoadData& StyleCssUrl::LoadData() const {
inline nsIURI* StyleCssUrl::GetURI() const { inline nsIURI* StyleCssUrl::GetURI() const {
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
auto& loadData = LoadData(); auto& loadData = LoadData();
if (!loadData.tried_to_resolve) { if (!(loadData.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_URI)) {
loadData.tried_to_resolve = true; loadData.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_URI;
NS_NewURI(getter_AddRefs(loadData.resolved), SpecifiedSerialization(), RefPtr<nsIURI> resolved;
nullptr, ExtraData().BaseURI()); NS_NewURI(getter_AddRefs(resolved), SpecifiedSerialization(), nullptr,
ExtraData().BaseURI());
loadData.resolved_uri = resolved.forget().take();
} }
return loadData.resolved.get(); return loadData.resolved_uri;
} }
inline nsDependentCSubstring StyleComputedUrl::SpecifiedSerialization() const { inline nsDependentCSubstring StyleComputedUrl::SpecifiedSerialization() const {
@@ -427,6 +429,15 @@ inline bool StyleComputedUrl::HasRef() const {
return false; return false;
} }
inline bool StyleComputedImageUrl::IsImageResolved() const {
return bool(LoadData().flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE);
}
inline imgRequestProxy* StyleComputedImageUrl::GetImage() const {
MOZ_ASSERT(IsImageResolved());
return LoadData().resolved_image;
}
template <> template <>
bool StyleGradient::IsOpaque() const; bool StyleGradient::IsOpaque() const;

View File

@@ -625,11 +625,6 @@ static void AddImageURL(const StyleComputedUrl& aURL,
} }
} }
static void AddImageURL(const nsStyleImageRequest& aRequest,
nsTArray<nsCString>& aURLs) {
AddImageURL(aRequest.GetImageValue(), aURLs);
}
static void AddImageURL(const nsStyleImage& aImage, static void AddImageURL(const nsStyleImage& aImage,
nsTArray<nsCString>& aURLs) { nsTArray<nsCString>& aURLs) {
if (auto* urlValue = aImage.GetURLValue()) { if (auto* urlValue = aImage.GetURLValue()) {
@@ -669,7 +664,7 @@ static void CollectImageURLsForProperty(nsCSSPropertyID aProp,
switch (aProp) { switch (aProp) {
case eCSSProperty_cursor: case eCSSProperty_cursor:
for (auto& image : aStyle.StyleUI()->mCursorImages) { for (auto& image : aStyle.StyleUI()->mCursorImages) {
AddImageURL(*image.mImage, aURLs); AddImageURL(image.mImage, aURLs);
} }
break; break;
case eCSSProperty_background_image: case eCSSProperty_background_image:
@@ -678,11 +673,13 @@ static void CollectImageURLsForProperty(nsCSSPropertyID aProp,
case eCSSProperty_mask_clip: case eCSSProperty_mask_clip:
AddImageURLs(aStyle.StyleSVGReset()->mMask, aURLs); AddImageURLs(aStyle.StyleSVGReset()->mMask, aURLs);
break; break;
case eCSSProperty_list_style_image: case eCSSProperty_list_style_image: {
if (nsStyleImageRequest* image = aStyle.StyleList()->mListStyleImage) { const auto& image = aStyle.StyleList()->mListStyleImage;
AddImageURL(*image, aURLs); if (image.IsUrl()) {
AddImageURL(image.AsUrl(), aURLs);
} }
break; break;
}
case eCSSProperty_border_image_source: case eCSSProperty_border_image_source:
AddImageURL(aStyle.StyleBorder()->mBorderImageSource, aURLs); AddImageURL(aStyle.StyleBorder()->mBorderImageSource, aURLs);
break; break;

View File

@@ -35,6 +35,7 @@
#include "mozilla/dom/ImageTracker.h" #include "mozilla/dom/ImageTracker.h"
#include "mozilla/CORSMode.h" #include "mozilla/CORSMode.h"
#include "mozilla/ClearOnShutdown.h" #include "mozilla/ClearOnShutdown.h"
#include "mozilla/GeckoBindings.h"
#include "mozilla/PreferenceSheet.h" #include "mozilla/PreferenceSheet.h"
#include "mozilla/Likely.h" #include "mozilla/Likely.h"
#include "nsIURI.h" #include "nsIURI.h"
@@ -68,19 +69,6 @@ struct AssertSizeIsLessThan {
#include "nsStyleStructList.h" #include "nsStyleStructList.h"
#undef STYLE_STRUCT #undef STYLE_STRUCT
static bool DefinitelyEqualImages(const nsStyleImageRequest* aRequest1,
const nsStyleImageRequest* aRequest2) {
if (aRequest1 == aRequest2) {
return true;
}
if (!aRequest1 || !aRequest2) {
return false;
}
return aRequest1->DefinitelyEquals(*aRequest2);
}
bool StyleCssUrlData::operator==(const StyleCssUrlData& aOther) const { bool StyleCssUrlData::operator==(const StyleCssUrlData& aOther) const {
// This very intentionally avoids comparing LoadData and such. // This very intentionally avoids comparing LoadData and such.
const auto& extra = extra_data.get(); const auto& extra = extra_data.get();
@@ -95,11 +83,7 @@ bool StyleCssUrlData::operator==(const StyleCssUrlData& aOther) const {
return serialization == aOther.serialization; return serialization == aOther.serialization;
} }
StyleLoadData::~StyleLoadData() { StyleLoadData::~StyleLoadData() { Gecko_LoadData_Drop(this); }
if (load_id != 0) {
css::ImageLoader::DeregisterCSSImageFromAllLoaders(*this);
}
}
already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(nsIURI* aURI) const { already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(nsIURI* aURI) const {
nsCOMPtr<nsIURI> result = GetURI(); nsCOMPtr<nsIURI> result = GetURI();
@@ -122,56 +106,127 @@ already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(
return ResolveLocalRef(aContent->GetBaseURI()); return ResolveLocalRef(aContent->GetBaseURI());
} }
already_AddRefed<imgRequestProxy> StyleComputedUrl::LoadImage( void StyleComputedUrl::ResolveImage(Document& aDocument,
Document& aDocument) { const StyleComputedUrl* aOldImage) {
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
StyleLoadData& data = LoadData();
MOZ_ASSERT(!(data.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE));
data.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE;
nsIURI* docURI = aDocument.GetDocumentURI(); nsIURI* docURI = aDocument.GetDocumentURI();
if (HasRef()) { if (HasRef()) {
bool isEqualExceptRef = false; bool isEqualExceptRef = false;
nsIURI* imageURI = GetURI(); nsIURI* imageURI = GetURI();
if (!imageURI) { if (!imageURI) {
return nullptr; return;
} }
if (NS_SUCCEEDED(imageURI->EqualsExceptRef(docURI, &isEqualExceptRef)) && if (NS_SUCCEEDED(imageURI->EqualsExceptRef(docURI, &isEqualExceptRef)) &&
isEqualExceptRef) { isEqualExceptRef) {
// Prevent loading an internal resource. // Prevent loading an internal resource.
return nullptr; return;
} }
} }
static uint64_t sNextLoadID = 1; MOZ_ASSERT(NS_IsMainThread());
StyleLoadData& data = LoadData(); // TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
if (data.load_id == 0) { // lack of non-http image caching in imagelib (bug 1406134), which causes
data.load_id = sNextLoadID++; // stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
// 1440305, but that seems too risky, and a lot of work to do before 60.
//
// Once that's fixed, the "old style" argument to TriggerImageLoads can go
// away, and same for mSharedCount in the image loader and so on.
const bool reuseProxy = nsContentUtils::IsChromeDoc(&aDocument) &&
aOldImage && aOldImage->IsImageResolved() &&
*this == *aOldImage;
RefPtr<imgRequestProxy> request;
if (reuseProxy) {
request = aOldImage->LoadData().resolved_image;
if (request) {
css::ImageLoader::NoteSharedLoad(request);
}
} else {
// NB: If aDocument is not the original document, we may not be able to load
// images from aDocument. Instead we do the image load from the original
// doc and clone it to aDocument.
Document* loadingDoc = aDocument.GetOriginalDocument();
const bool isPrint = !!loadingDoc;
if (!loadingDoc) {
loadingDoc = &aDocument;
}
// Kick off the load in the loading document.
request = css::ImageLoader::LoadImage(*this, *loadingDoc);
if (isPrint && request) {
RefPtr<imgRequestProxy> ret;
request->GetStaticRequest(&aDocument, getter_AddRefs(ret));
request = std::move(ret);
}
} }
// NB: If aDocument is not the original document, we may not be able to load
// images from aDocument. Instead we do the image load from the original doc
// and clone it to aDocument.
Document* loadingDoc = aDocument.GetOriginalDocument();
const bool isPrint = !!loadingDoc;
if (!loadingDoc) {
loadingDoc = &aDocument;
}
// Kick off the load in the loading document.
css::ImageLoader::LoadImage(*this, *loadingDoc);
// Register the image in the document that's using it.
imgRequestProxy* request =
aDocument.StyleImageLoader()->RegisterCSSImage(data);
if (!request) { if (!request) {
return nullptr; return;
} }
if (!isPrint) {
return do_AddRef(request); data.resolved_image = request.forget().take();
// Boost priority now that we know the image is present in the ComputedStyle
// of some frame.
data.resolved_image->BoostPriority(imgIRequest::CATEGORY_FRAME_STYLE);
}
/**
* Runnable to release the image request's mRequestProxy
* and mImageTracker on the main thread, and to perform
* any necessary unlocking and untracking of the image.
*/
class StyleImageRequestCleanupTask final : public mozilla::Runnable {
public:
explicit StyleImageRequestCleanupTask(StyleLoadData& aData)
: mozilla::Runnable("StyleImageRequestCleanupTask"),
mRequestProxy(dont_AddRef(aData.resolved_image)) {
MOZ_ASSERT(mRequestProxy);
aData.resolved_image = nullptr;
} }
RefPtr<imgRequestProxy> ret;
request->GetStaticRequest(&aDocument, getter_AddRefs(ret)); NS_IMETHOD Run() final {
return ret.forget(); MOZ_ASSERT(NS_IsMainThread());
css::ImageLoader::UnloadImage(mRequestProxy);
return NS_OK;
}
protected:
virtual ~StyleImageRequestCleanupTask() {
MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
"mRequestProxy destructor need to run on the main thread!");
}
private:
// Since we always dispatch this runnable to the main thread, these will be
// released on the main thread when the runnable itself is released.
RefPtr<imgRequestProxy> mRequestProxy;
};
// This is defined here for parallelism with LoadURI.
void Gecko_LoadData_Drop(StyleLoadData* aData) {
if (aData->resolved_image) {
auto task = MakeRefPtr<StyleImageRequestCleanupTask>(*aData);
if (NS_IsMainThread()) {
task->Run();
} else {
// if Resolve was not called at some point, mDocGroup is not set.
SystemGroup::Dispatch(TaskCategory::Other, task.forget());
}
}
// URIs are safe to refcount from any thread.
NS_IF_RELEASE(aData->resolved_uri);
} }
// -------------------- // --------------------
@@ -562,6 +617,7 @@ nsChangeHint nsStyleOutline::CalcDifference(
nsStyleList::nsStyleList(const Document& aDocument) nsStyleList::nsStyleList(const Document& aDocument)
: mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE), : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE),
mQuotes(StyleQuotes::Auto()), mQuotes(StyleQuotes::Auto()),
mListStyleImage(StyleImageUrlOrNone::None()),
mImageRegion(StyleClipRectOrAuto::Auto()), mImageRegion(StyleClipRectOrAuto::Auto()),
mMozListReversed(StyleMozListReversed::False) { mMozListReversed(StyleMozListReversed::False) {
MOZ_COUNT_CTOR(nsStyleList); MOZ_COUNT_CTOR(nsStyleList);
@@ -574,9 +630,9 @@ nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); }
nsStyleList::nsStyleList(const nsStyleList& aSource) nsStyleList::nsStyleList(const nsStyleList& aSource)
: mListStylePosition(aSource.mListStylePosition), : mListStylePosition(aSource.mListStylePosition),
mListStyleImage(aSource.mListStyleImage),
mCounterStyle(aSource.mCounterStyle), mCounterStyle(aSource.mCounterStyle),
mQuotes(aSource.mQuotes), mQuotes(aSource.mQuotes),
mListStyleImage(aSource.mListStyleImage),
mImageRegion(aSource.mImageRegion), mImageRegion(aSource.mImageRegion),
mMozListReversed(aSource.mMozListReversed) { mMozListReversed(aSource.mMozListReversed) {
MOZ_COUNT_CTOR(nsStyleList); MOZ_COUNT_CTOR(nsStyleList);
@@ -586,9 +642,12 @@ void nsStyleList::TriggerImageLoads(Document& aDocument,
const nsStyleList* aOldStyle) { const nsStyleList* aOldStyle) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (mListStyleImage && !mListStyleImage->IsResolved()) { if (mListStyleImage.IsUrl() && !mListStyleImage.AsUrl().IsImageResolved()) {
mListStyleImage->Resolve( auto* oldUrl = aOldStyle && aOldStyle->mListStyleImage.IsUrl()
aDocument, aOldStyle ? aOldStyle->mListStyleImage.get() : nullptr); ? &aOldStyle->mListStyleImage.AsUrl()
: nullptr;
const_cast<StyleComputedImageUrl&>(mListStyleImage.AsUrl())
.ResolveImage(aDocument, oldUrl);
} }
} }
@@ -624,7 +683,7 @@ nsChangeHint nsStyleList::CalcDifference(
} }
// list-style-image and -moz-image-region may affect some XUL elements // list-style-image and -moz-image-region may affect some XUL elements
// regardless of display value, so we still need to check them. // regardless of display value, so we still need to check them.
if (!DefinitelyEqualImages(mListStyleImage, aNewData.mListStyleImage)) { if (mListStyleImage != aNewData.mListStyleImage) {
return NS_STYLE_HINT_REFLOW; return NS_STYLE_HINT_REFLOW;
} }
if (mImageRegion != aNewData.mImageRegion) { if (mImageRegion != aNewData.mImageRegion) {
@@ -639,11 +698,11 @@ nsChangeHint nsStyleList::CalcDifference(
} }
already_AddRefed<nsIURI> nsStyleList::GetListStyleImageURI() const { already_AddRefed<nsIURI> nsStyleList::GetListStyleImageURI() const {
if (!mListStyleImage) { if (!mListStyleImage.IsUrl()) {
return nullptr; return nullptr;
} }
nsCOMPtr<nsIURI> uri = mListStyleImage->GetImageURI(); nsCOMPtr<nsIURI> uri = mListStyleImage.AsUrl().GetURI();
return uri.forget(); return uri.forget();
} }
@@ -1522,154 +1581,6 @@ bool StyleGradient::IsOpaque() const {
return true; return true;
} }
// --------------------
// nsStyleImageRequest
/**
* Runnable to release the nsStyleImageRequest's mRequestProxy
* and mImageTracker on the main thread, and to perform
* any necessary unlocking and untracking of the image.
*/
class StyleImageRequestCleanupTask : public mozilla::Runnable {
public:
typedef nsStyleImageRequest::Mode Mode;
StyleImageRequestCleanupTask(Mode aModeFlags,
already_AddRefed<imgRequestProxy> aRequestProxy,
already_AddRefed<ImageTracker> aImageTracker)
: mozilla::Runnable("StyleImageRequestCleanupTask"),
mModeFlags(aModeFlags),
mRequestProxy(aRequestProxy),
mImageTracker(aImageTracker) {}
NS_IMETHOD Run() final {
MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
"If mRequestProxy is non-null, we need to run on main thread!");
if (!mRequestProxy) {
return NS_OK;
}
if (mModeFlags & Mode::Track) {
MOZ_ASSERT(mImageTracker);
mImageTracker->Remove(mRequestProxy);
} else {
mRequestProxy->UnlockImage();
}
if (mModeFlags & Mode::Discard) {
mRequestProxy->RequestDiscard();
}
return NS_OK;
}
protected:
virtual ~StyleImageRequestCleanupTask() {
MOZ_ASSERT((!mRequestProxy && !mImageTracker) || NS_IsMainThread(),
"mRequestProxy and mImageTracker's destructor need to run "
"on the main thread!");
}
private:
Mode mModeFlags;
// Since we always dispatch this runnable to the main thread, these will be
// released on the main thread when the runnable itself is released.
RefPtr<imgRequestProxy> mRequestProxy;
RefPtr<ImageTracker> mImageTracker;
};
nsStyleImageRequest::nsStyleImageRequest(Mode aModeFlags,
const StyleComputedImageUrl& aImageURL)
: mImageURL(aImageURL), mModeFlags(aModeFlags), mResolved(false) {}
nsStyleImageRequest::~nsStyleImageRequest() {
// We may or may not be being destroyed on the main thread. To clean
// up, we must untrack and unlock the image (depending on mModeFlags),
// and release mRequestProxy and mImageTracker, all on the main thread.
{
RefPtr<StyleImageRequestCleanupTask> task =
new StyleImageRequestCleanupTask(mModeFlags, mRequestProxy.forget(),
mImageTracker.forget());
if (NS_IsMainThread()) {
task->Run();
} else {
if (mDocGroup) {
mDocGroup->Dispatch(TaskCategory::Other, task.forget());
} else {
// if Resolve was not called at some point, mDocGroup is not set.
SystemGroup::Dispatch(TaskCategory::Other, task.forget());
}
}
}
MOZ_ASSERT(!mRequestProxy);
MOZ_ASSERT(!mImageTracker);
}
void nsStyleImageRequest::Resolve(Document& aDocument,
const nsStyleImageRequest* aOldImageRequest) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsResolved(), "already resolved");
mResolved = true;
// TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
// lack of non-http image caching in imagelib (bug 1406134), which causes
// stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
// 1440305, but that seems too risky, and a lot of work to do before 60.
//
// Once that's fixed, the "old style" argument to TriggerImageLoads can go
// away.
if (nsContentUtils::IsChromeDoc(&aDocument) && aOldImageRequest &&
aOldImageRequest->IsResolved() && DefinitelyEquals(*aOldImageRequest)) {
MOZ_ASSERT(aOldImageRequest->mDocGroup == aDocument.GetDocGroup());
MOZ_ASSERT(mModeFlags == aOldImageRequest->mModeFlags);
mDocGroup = aOldImageRequest->mDocGroup;
mImageURL = aOldImageRequest->mImageURL;
mRequestProxy = aOldImageRequest->mRequestProxy;
} else {
mDocGroup = aDocument.GetDocGroup();
mRequestProxy = mImageURL.LoadImage(aDocument);
}
if (!mRequestProxy) {
// The URL resolution or image load failed.
return;
}
// Boost priority now that we know the image is present in the ComputedStyle
// of some frame.
mRequestProxy->BoostPriority(imgIRequest::CATEGORY_FRAME_STYLE);
if (mModeFlags & Mode::Track) {
mImageTracker = aDocument.ImageTracker();
}
MaybeTrackAndLock();
}
void nsStyleImageRequest::MaybeTrackAndLock() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsResolved());
MOZ_ASSERT(mRequestProxy);
if (mModeFlags & Mode::Track) {
MOZ_ASSERT(mImageTracker);
mImageTracker->Add(mRequestProxy);
} else {
MOZ_ASSERT(!mImageTracker);
mRequestProxy->LockImage();
}
}
bool nsStyleImageRequest::DefinitelyEquals(
const nsStyleImageRequest& aOther) const {
return mImageURL == aOther.mImageURL;
}
// -------------------- // --------------------
// CachedBorderImageData // CachedBorderImageData
// //
@@ -1720,8 +1631,7 @@ imgIContainer* CachedBorderImageData::GetSubImage(uint8_t aIndex) {
// nsStyleImage // nsStyleImage
// //
nsStyleImage::nsStyleImage() nsStyleImage::nsStyleImage() : mCropRect(nullptr), mType(eStyleImageType_Null) {
: mType(eStyleImageType_Null), mImage(nullptr), mCropRect(nullptr) {
MOZ_COUNT_CTOR(nsStyleImage); MOZ_COUNT_CTOR(nsStyleImage);
} }
@@ -1733,7 +1643,7 @@ nsStyleImage::~nsStyleImage() {
} }
nsStyleImage::nsStyleImage(const nsStyleImage& aOther) nsStyleImage::nsStyleImage(const nsStyleImage& aOther)
: mType(eStyleImageType_Null), mCropRect(nullptr) { : mCropRect(nullptr), mType(eStyleImageType_Null) {
// We need our own copy constructor because we don't want // We need our own copy constructor because we don't want
// to copy the reference count // to copy the reference count
MOZ_COUNT_CTOR(nsStyleImage); MOZ_COUNT_CTOR(nsStyleImage);
@@ -1752,7 +1662,7 @@ void nsStyleImage::DoCopy(const nsStyleImage& aOther) {
SetNull(); SetNull();
if (aOther.mType == eStyleImageType_Image) { if (aOther.mType == eStyleImageType_Image) {
SetImageRequest(do_AddRef(aOther.mImage)); SetImageUrl(aOther.mImage);
} else if (aOther.mType == eStyleImageType_Gradient) { } else if (aOther.mType == eStyleImageType_Gradient) {
SetGradientData(MakeUnique<StyleGradient>(*aOther.mGradient)); SetGradientData(MakeUnique<StyleGradient>(*aOther.mGradient));
} else if (aOther.mType == eStyleImageType_Element) { } else if (aOther.mType == eStyleImageType_Element) {
@@ -1771,7 +1681,7 @@ void nsStyleImage::SetNull() {
delete mGradient; delete mGradient;
mGradient = nullptr; mGradient = nullptr;
} else if (mType == eStyleImageType_Image) { } else if (mType == eStyleImageType_Image) {
NS_RELEASE(mImage); mImage.~StyleComputedImageUrl();
} else if (mType == eStyleImageType_Element) { } else if (mType == eStyleImageType_Element) {
NS_RELEASE(mElementId); NS_RELEASE(mElementId);
} }
@@ -1780,18 +1690,14 @@ void nsStyleImage::SetNull() {
mCropRect = nullptr; mCropRect = nullptr;
} }
void nsStyleImage::SetImageRequest( void nsStyleImage::SetImageUrl(const StyleComputedImageUrl& aImage) {
already_AddRefed<nsStyleImageRequest> aImage) {
RefPtr<nsStyleImageRequest> image = aImage;
if (mType != eStyleImageType_Null) { if (mType != eStyleImageType_Null) {
SetNull(); SetNull();
} }
if (image) { new (&mImage) StyleComputedImageUrl(aImage);
mImage = image.forget().take(); mType = eStyleImageType_Image;
mType = eStyleImageType_Image;
}
if (mCachedBIData) { if (mCachedBIData) {
mCachedBIData->PurgeCachedImages(); mCachedBIData->PurgeCachedImages();
} }
@@ -1837,22 +1743,6 @@ static int32_t ConvertToPixelCoord(const StyleNumberOrPercentage& aCoord,
return NS_lround(pixelValue); return NS_lround(pixelValue);
} }
already_AddRefed<nsIURI> nsStyleImageRequest::GetImageURI() const {
nsCOMPtr<nsIURI> uri;
if (mRequestProxy) {
mRequestProxy->GetURI(getter_AddRefs(uri));
if (uri) {
return uri.forget();
}
}
// If we had some problem resolving the mRequestProxy, use the URL stored
// in the mImageURL.
uri = mImageURL.GetURI();
return uri.forget();
}
bool nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect, bool nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect,
bool* aIsEntireImage) const { bool* aIsEntireImage) const {
MOZ_ASSERT(mType == eStyleImageType_Image, MOZ_ASSERT(mType == eStyleImageType_Image,
@@ -2009,7 +1899,7 @@ bool nsStyleImage::operator==(const nsStyleImage& aOther) const {
} }
if (mType == eStyleImageType_Image) { if (mType == eStyleImageType_Image) {
return DefinitelyEqualImages(mImage, aOther.mImage); return mImage == aOther.mImage;
} }
if (mType == eStyleImageType_Gradient) { if (mType == eStyleImageType_Gradient) {
@@ -2046,12 +1936,12 @@ already_AddRefed<nsIURI> nsStyleImage::GetImageURI() const {
return nullptr; return nullptr;
} }
nsCOMPtr<nsIURI> uri = mImage->GetImageURI(); nsCOMPtr<nsIURI> uri = mImage.GetURI();
return uri.forget(); return uri.forget();
} }
const StyleComputedImageUrl* nsStyleImage::GetURLValue() const { const StyleComputedImageUrl* nsStyleImage::GetURLValue() const {
return mType == eStyleImageType_Image ? &mImage->GetImageValue() : nullptr; return mType == eStyleImageType_Image ? &mImage : nullptr;
} }
// -------------------- // --------------------
@@ -3253,6 +3143,35 @@ nsChangeHint nsStyleContent::CalcDifference(
return nsChangeHint(0); return nsChangeHint(0);
} }
void nsStyleContent::TriggerImageLoads(Document& aDoc,
const nsStyleContent* aOld) {
if (!mContent.IsItems()) {
return;
}
Span<const StyleContentItem> oldItems;
if (aOld && aOld->mContent.IsItems()) {
oldItems = aOld->mContent.AsItems().AsSpan();
}
auto items = mContent.AsItems().AsSpan();
for (size_t i = 0; i < items.Length(); ++i) {
auto& item = items[i];
if (!item.IsUrl()) {
continue;
}
auto& url = item.AsUrl();
if (url.IsImageResolved()) {
continue;
}
auto* oldUrl = i < oldItems.Length() && oldItems[i].IsUrl()
? &oldItems[i].AsUrl()
: nullptr;
const_cast<StyleComputedImageUrl&>(url).ResolveImage(aDoc, oldUrl);
}
}
// -------------------- // --------------------
// nsStyleTextReset // nsStyleTextReset
// //
@@ -3501,8 +3420,8 @@ LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM) const {
// nsStyleUI // nsStyleUI
// //
nsCursorImage::nsCursorImage() nsCursorImage::nsCursorImage(const StyleComputedImageUrl& aImage)
: mHaveHotspot(false), mHotspotX(0.0f), mHotspotY(0.0f) {} : mHaveHotspot(false), mHotspotX(0.0f), mHotspotY(0.0f), mImage(aImage) {}
nsCursorImage::nsCursorImage(const nsCursorImage& aOther) nsCursorImage::nsCursorImage(const nsCursorImage& aOther)
: mHaveHotspot(aOther.mHaveHotspot), : mHaveHotspot(aOther.mHaveHotspot),
@@ -3528,8 +3447,7 @@ bool nsCursorImage::operator==(const nsCursorImage& aOther) const {
aOther.mHaveHotspot || (aOther.mHotspotX == 0 && aOther.mHotspotY == 0), aOther.mHaveHotspot || (aOther.mHotspotX == 0 && aOther.mHotspotY == 0),
"expected mHotspot{X,Y} to be 0 when mHaveHotspot is false"); "expected mHotspot{X,Y} to be 0 when mHaveHotspot is false");
return mHaveHotspot == aOther.mHaveHotspot && mHotspotX == aOther.mHotspotX && return mHaveHotspot == aOther.mHaveHotspot && mHotspotX == aOther.mHotspotX &&
mHotspotY == aOther.mHotspotY && mHotspotY == aOther.mHotspotY && mImage == aOther.mImage;
DefinitelyEqualImages(mImage, aOther.mImage);
} }
nsStyleUI::nsStyleUI(const Document& aDocument) nsStyleUI::nsStyleUI(const Document& aDocument)
@@ -3564,13 +3482,13 @@ void nsStyleUI::TriggerImageLoads(Document& aDocument,
for (size_t i = 0; i < mCursorImages.Length(); ++i) { for (size_t i = 0; i < mCursorImages.Length(); ++i) {
nsCursorImage& cursor = mCursorImages[i]; nsCursorImage& cursor = mCursorImages[i];
if (cursor.mImage && !cursor.mImage->IsResolved()) { if (!cursor.mImage.IsImageResolved()) {
const nsCursorImage* oldCursor = const nsCursorImage* oldCursor =
(aOldStyle && aOldStyle->mCursorImages.Length() > i) (aOldStyle && aOldStyle->mCursorImages.Length() > i)
? &aOldStyle->mCursorImages[i] ? &aOldStyle->mCursorImages[i]
: nullptr; : nullptr;
cursor.mImage->Resolve(aDocument, cursor.mImage.ResolveImage(aDocument,
oldCursor ? oldCursor->mImage.get() : nullptr); oldCursor ? &oldCursor->mImage : nullptr);
} }
} }
} }

View File

@@ -133,96 +133,6 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleFont {
RefPtr<nsAtom> mLanguage; RefPtr<nsAtom> mLanguage;
}; };
/**
* A wrapper for an imgRequestProxy that supports off-main-thread creation
* and equality comparison.
*
* An nsStyleImageRequest can be created using the constructor that takes the
* URL, base URI, referrer and principal that can be used to initiate an image
* load and produce an imgRequestProxy later.
*
* This can be called from any thread. The nsStyleImageRequest is not
* considered "resolved" at this point, and the Resolve() method must be called
* later to initiate the image load and make calls to get() valid.
*
* Calls to TrackImage(), UntrackImage(), LockImage(), UnlockImage() and
* RequestDiscard() are made to the imgRequestProxy and ImageTracker as
* appropriate, according to the mode flags passed in to the constructor.
*
* The constructor receives a css::URLValue to represent the url()
* information, which is held on to for the comparisons done in
* DefinitelyEquals().
*/
class nsStyleImageRequest {
public:
// Flags describing whether the imgRequestProxy must be tracked in the
// ImageTracker, whether LockImage/UnlockImage calls will be made
// when obtaining and releasing the imgRequestProxy, and whether
// RequestDiscard will be called on release.
enum class Mode : uint8_t {
// The imgRequestProxy will be added to the ImageTracker when resolved
// Without this flag, the nsStyleImageRequest itself will call LockImage/
// UnlockImage on the imgRequestProxy, rather than leaving locking to the
// ImageTracker to manage.
//
// This flag is currently used by all nsStyleImageRequests except
// those for list-style-image and cursor.
Track = 0x1,
// The imgRequestProxy will have its RequestDiscard method called when
// the nsStyleImageRequest is going away.
//
// This is currently used only for cursor images.
Discard = 0x2,
};
// Can be called from any thread, but Resolve() must be called later
// on the main thread before get() can be used.
nsStyleImageRequest(Mode aModeFlags, const mozilla::StyleComputedImageUrl&);
void Resolve(mozilla::dom::Document&,
const nsStyleImageRequest* aOldImageRequest);
bool IsResolved() const { return mResolved; }
imgRequestProxy* get() {
MOZ_ASSERT(IsResolved(), "Resolve() must be called first");
MOZ_ASSERT(NS_IsMainThread());
return mRequestProxy.get();
}
const imgRequestProxy* get() const {
return const_cast<nsStyleImageRequest*>(this)->get();
}
// Returns whether the URLValue objects in the two nsStyleImageRequests
// return true from URLValue::DefinitelyEqualURIs.
bool DefinitelyEquals(const nsStyleImageRequest& aOther) const;
const mozilla::StyleComputedImageUrl& GetImageValue() const {
return mImageURL;
}
already_AddRefed<nsIURI> GetImageURI() const;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsStyleImageRequest);
private:
~nsStyleImageRequest();
nsStyleImageRequest& operator=(const nsStyleImageRequest& aOther) = delete;
void MaybeTrackAndLock();
RefPtr<imgRequestProxy> mRequestProxy;
mozilla::StyleComputedImageUrl mImageURL;
RefPtr<mozilla::dom::ImageTracker> mImageTracker;
// Cache DocGroup for dispatching events in the destructor.
RefPtr<mozilla::dom::DocGroup> mDocGroup;
Mode mModeFlags;
bool mResolved;
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsStyleImageRequest::Mode)
enum nsStyleImageType { enum nsStyleImageType {
eStyleImageType_Null, eStyleImageType_Null,
eStyleImageType_Image, eStyleImageType_Image,
@@ -268,36 +178,34 @@ struct nsStyleImage {
nsStyleImage& operator=(const nsStyleImage& aOther); nsStyleImage& operator=(const nsStyleImage& aOther);
void SetNull(); void SetNull();
void SetImageRequest(already_AddRefed<nsStyleImageRequest> aImage); void SetImageUrl(const mozilla::StyleComputedImageUrl&);
void SetGradientData(mozilla::UniquePtr<mozilla::StyleGradient>); void SetGradientData(mozilla::UniquePtr<mozilla::StyleGradient>);
void SetElementId(already_AddRefed<nsAtom> aElementId); void SetElementId(already_AddRefed<nsAtom> aElementId);
void SetCropRect(mozilla::UniquePtr<CropRect> aCropRect); void SetCropRect(mozilla::UniquePtr<CropRect> aCropRect);
void ResolveImage(mozilla::dom::Document& aDocument, void ResolveImage(mozilla::dom::Document& aDocument,
const nsStyleImage* aOldImage) { const nsStyleImage* aOldImage) {
MOZ_ASSERT(mType != eStyleImageType_Image || mImage); if (mType == eStyleImageType_Image && !mImage.IsImageResolved()) {
if (mType == eStyleImageType_Image && !mImage->IsResolved()) { const auto* oldRequest =
const nsStyleImageRequest* oldRequest =
(aOldImage && aOldImage->GetType() == eStyleImageType_Image) (aOldImage && aOldImage->GetType() == eStyleImageType_Image)
? aOldImage->ImageRequest() ? &aOldImage->ImageUrl()
: nullptr; : nullptr;
mImage->Resolve(aDocument, oldRequest); mImage.ResolveImage(aDocument, oldRequest);
} }
} }
nsStyleImageType GetType() const { return mType; } nsStyleImageType GetType() const { return mType; }
nsStyleImageRequest* ImageRequest() const { const mozilla::StyleComputedImageUrl& ImageUrl() const {
MOZ_ASSERT(mType == eStyleImageType_Image, "Data is not an image!"); MOZ_ASSERT(mType == eStyleImageType_Image, "Data is not an image!");
MOZ_ASSERT(mImage);
return mImage; return mImage;
} }
imgRequestProxy* GetImageData() const { return ImageRequest()->get(); } imgRequestProxy* GetImageData() const { return ImageUrl().GetImage(); }
const mozilla::StyleGradient& GetGradient() const { const mozilla::StyleGradient& GetGradient() const {
NS_ASSERTION(mType == eStyleImageType_Gradient, "Data is not a gradient!"); NS_ASSERTION(mType == eStyleImageType_Gradient, "Data is not a gradient!");
return *mGradient; return *mGradient;
} }
bool IsResolved() const { bool IsResolved() const {
return mType != eStyleImageType_Image || ImageRequest()->IsResolved(); return mType != eStyleImageType_Image || ImageUrl().IsImageResolved();
} }
const nsAtom* GetElementId() const { const nsAtom* GetElementId() const {
NS_ASSERTION(mType == eStyleImageType_Element, "Data is not an element!"); NS_ASSERTION(mType == eStyleImageType_Element, "Data is not an element!");
@@ -391,15 +299,15 @@ struct nsStyleImage {
// allocated since it is only used in border image case. // allocated since it is only used in border image case.
mozilla::UniquePtr<CachedBorderImageData> mCachedBIData; mozilla::UniquePtr<CachedBorderImageData> mCachedBIData;
// This is _currently_ used only in conjunction with eStyleImageType_Image.
mozilla::UniquePtr<CropRect> mCropRect;
nsStyleImageType mType; nsStyleImageType mType;
union { union {
nsStyleImageRequest* mImage; mozilla::StyleComputedImageUrl mImage;
mozilla::StyleGradient* mGradient; mozilla::StyleGradient* mGradient;
nsAtom* mElementId; nsAtom* mElementId;
}; };
// This is _currently_ used only in conjunction with eStyleImageType_Image.
mozilla::UniquePtr<CropRect> mCropRect;
}; };
struct nsStyleImageLayers { struct nsStyleImageLayers {
@@ -920,6 +828,10 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList {
nsStyleList(const nsStyleList& aStyleList); nsStyleList(const nsStyleList& aStyleList);
~nsStyleList(); ~nsStyleList();
private:
nsStyleList& operator=(const nsStyleList& aOther) = delete;
public:
void TriggerImageLoads(mozilla::dom::Document&, const nsStyleList*); void TriggerImageLoads(mozilla::dom::Document&, const nsStyleList*);
static constexpr bool kHasTriggerImageLoads = true; static constexpr bool kHasTriggerImageLoads = true;
@@ -927,7 +839,8 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList {
const nsStyleDisplay& aOldDisplay) const; const nsStyleDisplay& aOldDisplay) const;
imgRequestProxy* GetListStyleImage() const { imgRequestProxy* GetListStyleImage() const {
return mListStyleImage ? mListStyleImage->get() : nullptr; return mListStyleImage.IsUrl() ? mListStyleImage.AsUrl().GetImage()
: nullptr;
} }
nsRect GetImageRegion() const { nsRect GetImageRegion() const {
@@ -940,18 +853,15 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList {
already_AddRefed<nsIURI> GetListStyleImageURI() const; already_AddRefed<nsIURI> GetListStyleImageURI() const;
uint8_t mListStylePosition; uint8_t mListStylePosition;
RefPtr<nsStyleImageRequest> mListStyleImage;
mozilla::CounterStylePtr mCounterStyle; mozilla::CounterStylePtr mCounterStyle;
private:
nsStyleList& operator=(const nsStyleList& aOther) = delete;
public:
mozilla::StyleQuotes mQuotes; mozilla::StyleQuotes mQuotes;
mozilla::StyleClipRectOrAuto mImageRegion; // the rect to use within an image mozilla::StyleImageUrlOrNone mListStyleImage;
mozilla::StyleMozListReversed
mMozListReversed; // true in an <ol reversed> scope // the rect to use within an image.
mozilla::StyleClipRectOrAuto mImageRegion;
// true in an <ol reversed> scope.
mozilla::StyleMozListReversed mMozListReversed;
}; };
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition { struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition {
@@ -1919,7 +1829,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleContent {
explicit nsStyleContent(const mozilla::dom::Document&); explicit nsStyleContent(const mozilla::dom::Document&);
nsStyleContent(const nsStyleContent& aContent); nsStyleContent(const nsStyleContent& aContent);
~nsStyleContent(); ~nsStyleContent();
static constexpr bool kHasTriggerImageLoads = false;
void TriggerImageLoads(mozilla::dom::Document&, const nsStyleContent*);
static constexpr bool kHasTriggerImageLoads = true;
size_t ContentCount() const { size_t ContentCount() const {
return mContent.IsItems() ? mContent.AsItems().Length() : 0; return mContent.IsItems() ? mContent.AsItems().Length() : 0;
@@ -1959,10 +1871,10 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
struct nsCursorImage { struct nsCursorImage {
bool mHaveHotspot; bool mHaveHotspot;
float mHotspotX, mHotspotY; float mHotspotX, mHotspotY;
RefPtr<nsStyleImageRequest> mImage; mozilla::StyleComputedImageUrl mImage;
nsCursorImage(); explicit nsCursorImage(const mozilla::StyleComputedImageUrl&);
nsCursorImage(const nsCursorImage& aOther); nsCursorImage(const nsCursorImage&);
nsCursorImage& operator=(const nsCursorImage& aOther); nsCursorImage& operator=(const nsCursorImage& aOther);
@@ -1971,7 +1883,7 @@ struct nsCursorImage {
return !(*this == aOther); return !(*this == aOther);
} }
imgRequestProxy* GetImage() const { return mImage->get(); } imgRequestProxy* GetImage() const { return mImage.GetImage(); }
}; };
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUI { struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUI {

View File

@@ -876,17 +876,16 @@ void SVGMaskObserverList::ResolveImage(uint32_t aIndex) {
const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset(); const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset();
MOZ_ASSERT(aIndex < svgReset->mMask.mImageCount); MOZ_ASSERT(aIndex < svgReset->mMask.mImageCount);
nsStyleImage& image = auto& image =
const_cast<nsStyleImage&>(svgReset->mMask.mLayers[aIndex].mImage); const_cast<nsStyleImage&>(svgReset->mMask.mLayers[aIndex].mImage);
if (!image.IsResolved()) { if (!image.IsResolved()) {
MOZ_ASSERT(image.GetType() == nsStyleImageType::eStyleImageType_Image); MOZ_ASSERT(image.GetType() == nsStyleImageType::eStyleImageType_Image);
image.ResolveImage(*mFrame->PresContext()->Document(), nullptr); image.ResolveImage(*mFrame->PresContext()->Document(), nullptr);
mozilla::css::ImageLoader* imageLoader = Document* doc = mFrame->PresContext()->Document();
mFrame->PresContext()->Document()->StyleImageLoader();
if (imgRequestProxy* req = image.GetImageData()) { if (imgRequestProxy* req = image.GetImageData()) {
imageLoader->AssociateRequestToFrame(req, mFrame, 0); doc->StyleImageLoader()->AssociateRequestToFrame(req, mFrame, 0);
} }
} }
} }

View File

@@ -15,7 +15,6 @@ use crate::gecko_bindings::structs::{self, Matrix4x4Components};
use crate::gecko_bindings::structs::{nsStyleImage, nsresult}; use crate::gecko_bindings::structs::{nsStyleImage, nsresult};
use crate::stylesheets::RulesMutateError; use crate::stylesheets::RulesMutateError;
use crate::values::computed::transform::Matrix3D; use crate::values::computed::transform::Matrix3D;
use crate::values::computed::url::ComputedImageUrl;
use crate::values::computed::{Gradient, Image, TextAlign}; use crate::values::computed::{Gradient, Image, TextAlign};
use crate::values::generics::image::GenericImage; use crate::values::generics::image::GenericImage;
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
@@ -63,7 +62,7 @@ impl nsStyleImage {
match self.mType { match self.mType {
nsStyleImageType::eStyleImageType_Null => None, nsStyleImageType::eStyleImageType_Null => None,
nsStyleImageType::eStyleImageType_Image => { nsStyleImageType::eStyleImageType_Image => {
let url = self.get_image_url(); let url = self.__bindgen_anon_1.mImage.as_ref().clone();
if self.mCropRect.mPtr.is_null() { if self.mCropRect.mPtr.is_null() {
Some(GenericImage::Url(url)) Some(GenericImage::Url(url))
} else { } else {
@@ -88,13 +87,6 @@ impl nsStyleImage {
}, },
} }
} }
unsafe fn get_image_url(&self) -> ComputedImageUrl {
let image_request = bindings::Gecko_GetImageRequest(self)
.as_ref()
.expect("Null image request?");
ComputedImageUrl::from_image_request(image_request)
}
} }
pub mod basic_shape { pub mod basic_shape {

View File

@@ -6,8 +6,6 @@
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs; use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::nsStyleImageRequest;
use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::stylesheets::{CorsMode, UrlExtraData}; use crate::stylesheets::{CorsMode, UrlExtraData};
use crate::values::computed::{Context, ToComputedValue}; use crate::values::computed::{Context, ToComputedValue};
@@ -150,31 +148,52 @@ struct LoadDataKey(*const LoadDataSource);
unsafe impl Sync for LoadDataKey {} unsafe impl Sync for LoadDataKey {}
unsafe impl Send for LoadDataKey {} unsafe impl Send for LoadDataKey {}
/// The load data for a given URL. This is mutable from C++, for now at least. bitflags! {
/// Various bits of mutable state that are kept for image loads.
#[repr(C)]
pub struct LoadDataFlags: u8 {
/// Whether we tried to resolve the uri at least once.
const TRIED_TO_RESOLVE_URI = 1 << 0;
/// Whether we tried to resolve the image at least once.
const TRIED_TO_RESOLVE_IMAGE = 1 << 1;
}
}
/// This is usable and movable from multiple threads just fine, as long as it's
/// not cloned (it is not clonable), and the methods that mutate it run only on
/// the main thread (when all the other threads we care about are paused).
unsafe impl Sync for LoadData {}
unsafe impl Send for LoadData {}
/// The load data for a given URL. This is mutable from C++, and shouldn't be
/// accessed from rust for anything.
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
pub struct LoadData { pub struct LoadData {
resolved: RefPtr<structs::nsIURI>, /// A strong reference to the imgRequestProxy, if any, that should be
load_id: u64, /// released on drop.
tried_to_resolve: bool, ///
/// These are raw pointers because they are not safe to reference-count off
/// the main thread.
resolved_image: *mut structs::imgRequestProxy,
/// A strong reference to the resolved URI of this image.
resolved_uri: *mut structs::nsIURI,
/// A few flags that are set when resolving the image or such.
flags: LoadDataFlags,
} }
impl Drop for LoadData { impl Drop for LoadData {
fn drop(&mut self) { fn drop(&mut self) {
if self.load_id != 0 { unsafe { bindings::Gecko_LoadData_Drop(self) }
unsafe {
bindings::Gecko_LoadData_DeregisterLoad(self);
}
}
} }
} }
impl Default for LoadData { impl Default for LoadData {
fn default() -> Self { fn default() -> Self {
Self { Self {
resolved: RefPtr::null(), resolved_image: std::ptr::null_mut(),
load_id: 0, resolved_uri: std::ptr::null_mut(),
tried_to_resolve: false, flags: LoadDataFlags::empty(),
} }
} }
} }
@@ -342,13 +361,6 @@ impl ToCss for ComputedUrl {
#[repr(transparent)] #[repr(transparent)]
pub struct ComputedImageUrl(pub ComputedUrl); pub struct ComputedImageUrl(pub ComputedUrl);
impl ComputedImageUrl {
/// Convert from nsStyleImageRequest to ComputedImageUrl.
pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
image_request.mImageURL.clone()
}
}
impl ToCss for ComputedImageUrl { impl ToCss for ComputedImageUrl {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where

View File

@@ -25,14 +25,9 @@ use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
use crate::gecko_bindings::bindings::Gecko_CopyCursorArrayFrom; use crate::gecko_bindings::bindings::Gecko_CopyCursorArrayFrom;
use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom; use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom; use crate::gecko_bindings::bindings::Gecko_CopyImageValueFrom;
use crate::gecko_bindings::bindings::Gecko_CopyListStyleImageFrom;
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength; use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
use crate::gecko_bindings::bindings::Gecko_SetCursorArrayLength;
use crate::gecko_bindings::bindings::Gecko_SetCursorImageValue;
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
use crate::gecko_bindings::bindings::Gecko_SetListStyleImageNone;
use crate::gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
use crate::gecko_bindings::bindings::Gecko_SetNullImageValue; use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
use crate::gecko_bindings::structs; use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::nsCSSPropertyID; use crate::gecko_bindings::structs::nsCSSPropertyID;
@@ -53,7 +48,6 @@ use crate::values::computed::BorderStyle;
use crate::values::computed::font::FontSize; use crate::values::computed::font::FontSize;
use crate::values::generics::column::ColumnCount; use crate::values::generics::column::ColumnCount;
use crate::values::generics::image::ImageLayer; use crate::values::generics::image::ImageLayer;
use crate::values::generics::url::UrlOrNone;
pub mod style_structs { pub mod style_structs {
@@ -2108,43 +2102,7 @@ fn static_assert() {
<% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %> <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
</%self:impl_trait> </%self:impl_trait>
<%self:impl_trait style_struct_name="List" <%self:impl_trait style_struct_name="List" skip_longhands="list-style-type">
skip_longhands="list-style-image list-style-type">
pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) {
match image {
UrlOrNone::None => {
unsafe {
Gecko_SetListStyleImageNone(&mut *self.gecko);
}
}
UrlOrNone::Url(ref url) => {
unsafe {
Gecko_SetListStyleImageImageValue(&mut *self.gecko, url);
}
}
}
}
pub fn copy_list_style_image_from(&mut self, other: &Self) {
unsafe { Gecko_CopyListStyleImageFrom(&mut *self.gecko, &*other.gecko); }
}
pub fn reset_list_style_image(&mut self, other: &Self) {
self.copy_list_style_image_from(other)
}
pub fn clone_list_style_image(&self) -> longhands::list_style_image::computed_value::T {
if self.gecko.mListStyleImage.mRawPtr.is_null() {
return UrlOrNone::None;
}
unsafe {
let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr;
UrlOrNone::Url(ComputedImageUrl::from_image_request(gecko_image_request))
}
}
pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) { pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
use nsstring::{nsACString, nsCStr}; use nsstring::{nsACString, nsCStr};
use self::longhands::list_style_type::computed_value::T; use self::longhands::list_style_type::computed_value::T;
@@ -2433,14 +2391,11 @@ clip-path
pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) { pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
self.gecko.mCursor = v.keyword; self.gecko.mCursor = v.keyword;
unsafe { unsafe {
Gecko_SetCursorArrayLength(&mut *self.gecko, v.images.len()); bindings::Gecko_SetCursorArrayCapacity(&mut *self.gecko, v.images.len());
} }
for i in 0..v.images.len() { for i in 0..v.images.len() {
unsafe { unsafe {
Gecko_SetCursorImageValue( bindings::Gecko_AppendCursorImage(&mut *self.gecko, &v.images[i].url);
&mut self.gecko.mCursorImages[i],
&v.images[i].url
);
} }
match v.images[i].hotspot { match v.images[i].hotspot {
@@ -2473,10 +2428,7 @@ clip-path
let keyword = self.gecko.mCursor; let keyword = self.gecko.mCursor;
let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| { let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| {
let url = unsafe { let url = gecko_cursor_image.mImage.clone();
let gecko_image_request = gecko_cursor_image.mImage.mRawPtr.as_ref().unwrap();
ComputedImageUrl::from_image_request(&gecko_image_request)
};
let hotspot = let hotspot =
if gecko_cursor_image.mHaveHotspot { if gecko_cursor_image.mHaveHotspot {

View File

@@ -170,6 +170,7 @@ include = [
"ComputedUrl", "ComputedUrl",
"ComputedImageUrl", "ComputedImageUrl",
"UrlOrNone", "UrlOrNone",
"ImageUrlOrNone",
"Filter", "Filter",
"Gradient", "Gradient",
"GridTemplateAreas", "GridTemplateAreas",
@@ -206,6 +207,7 @@ renaming_overrides_prefixing = true
"nscolor" = "nscolor" "nscolor" = "nscolor"
"nsAtom" = "nsAtom" "nsAtom" = "nsAtom"
"nsIURI" = "nsIURI" "nsIURI" = "nsIURI"
"imgRequestProxy" = "imgRequestProxy"
"nsCompatibility" = "nsCompatibility" "nsCompatibility" = "nsCompatibility"
"SharedFontList" = "SharedFontList" "SharedFontList" = "SharedFontList"
"nsSimpleContentList" = "nsSimpleContentList" "nsSimpleContentList" = "nsSimpleContentList"
@@ -651,7 +653,11 @@ renaming_overrides_prefixing = true
inline StyleCorsMode CorsMode() const; inline StyleCorsMode CorsMode() const;
already_AddRefed<nsIURI> ResolveLocalRef(nsIURI* aBase) const; already_AddRefed<nsIURI> ResolveLocalRef(nsIURI* aBase) const;
already_AddRefed<nsIURI> ResolveLocalRef(const nsIContent* aContent) const; already_AddRefed<nsIURI> ResolveLocalRef(const nsIContent* aContent) const;
already_AddRefed<imgRequestProxy> LoadImage(mozilla::dom::Document&);
// Only relevant for images.
inline bool IsImageResolved() const;
inline imgRequestProxy* GetImage() const;
void ResolveImage(dom::Document&, const StyleComputedUrl* aOldImage);
""" """
"GenericGradient" = """ "GenericGradient" = """