Bug 1288302 - Part 3: Add nsStyleImageRequest. r=xidorn,bholley
MozReview-Commit-ID: F763Dv0Nfzp
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -20,16 +20,17 @@
#include "nsIDocument.h"
#include "nsIPrincipal.h"
#include "nsCSSProps.h"
#include "nsNetUtil.h"
#include "nsPresContext.h"
#include "nsStyleUtil.h"
#include "nsDeviceContext.h"
#include "nsStyleSet.h"
+#include "nsContentUtils.h"
using namespace mozilla;
static bool
IsLocalRefURL(nsStringBuffer* aString)
{
// Find the first non-"C0 controls + space" character.
char16_t* current = static_cast<char16_t*>(aString->Data());
@@ -360,16 +361,27 @@ nsCSSValue::GetAngleValueInDegrees() con
}
imgRequestProxy* nsCSSValue::GetImageValue(nsIDocument* aDocument) const
{
MOZ_ASSERT(mUnit == eCSSUnit_Image, "not an Image value");
return mValue.mImage->mRequests.GetWeak(aDocument);
}
+already_AddRefed<imgRequestProxy>
+nsCSSValue::GetPossiblyStaticImageValue(nsIDocument* aDocument,
+ nsPresContext* aPresContext) const
+{
+ imgRequestProxy* req = GetImageValue(aDocument);
+ if (aPresContext->IsDynamic()) {
+ return do_AddRef(req);
+ }
+ return nsContentUtils::GetStaticRequest(req);
+}
+
nscoord nsCSSValue::GetFixedLength(nsPresContext* aPresContext) const
{
MOZ_ASSERT(mUnit == eCSSUnit_PhysicalMillimeter,
"not a fixed length unit");
float inches = mValue.mFloat / MM_PER_INCH_FLOAT;
return NSToCoordFloorClamped(inches *
float(aPresContext->DeviceContext()->AppUnitsPerPhysicalInch()));
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -864,16 +864,21 @@ public:
mValue.mImage->mString);
}
// Not making this inline because that would force us to include
// imgIRequest.h, which leads to REQUIRES hell, since this header is included
// all over.
imgRequestProxy* GetImageValue(nsIDocument* aDocument) const;
+ // Like GetImageValue, but additionally will pass the imgRequestProxy
+ // through nsContentUtils::GetStaticRequest if aPresContent is static.
+ already_AddRefed<imgRequestProxy> GetPossiblyStaticImageValue(
+ nsIDocument* aDocument, nsPresContext* aPresContext) const;
+
nscoord GetFixedLength(nsPresContext* aPresContext) const;
nscoord GetPixelLength() const;
nsCSSValueFloatColor* GetFloatColorValue() const
{
MOZ_ASSERT(IsFloatColorUnit(), "not a float color value");
return mValue.mFloatColor;
}
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -123,23 +123,20 @@ GetImageRequest(nsPresContext* aPresCont
// showing the first frame, without animation) will be created.
// (The expectation is then that aCallback will set the resulting
// imgRequestProxy in a style struct somewhere.)
static void
SetImageRequest(function<void(imgRequestProxy*)> aCallback,
nsPresContext* aPresContext,
const nsCSSValue& aValue)
{
- imgRequestProxy* req = GetImageRequest(aPresContext, aValue);
- if (aPresContext->IsDynamic()) {
- aCallback(req);
- } else {
- RefPtr<imgRequestProxy> staticReq = nsContentUtils::GetStaticRequest(req);
- aCallback(staticReq);
- }
+ RefPtr<imgRequestProxy> req =
+ aValue.GetPossiblyStaticImageValue(aPresContext->Document(),
+ aPresContext);
+ aCallback(req);
}
template<typename ReferenceBox>
static void
SetStyleShapeSourceToCSSValue(StyleShapeSource<ReferenceBox>* aShapeSource,
const nsCSSValue* aValue,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -88,16 +88,31 @@ EqualImages(imgIRequest *aImage1, imgIRe
}
nsCOMPtr<nsIURI> uri1, uri2;
aImage1->GetURI(getter_AddRefs(uri1));
aImage2->GetURI(getter_AddRefs(uri2));
return EqualURIs(uri1, uri2);
}
+static bool
+DefinitelyEqualImages(nsStyleImageRequest* aRequest1,
+ nsStyleImageRequest* aRequest2)
+{
+ if (aRequest1 == aRequest2) {
+ return true;
+ }
+
+ if (!aRequest1 || !aRequest2) {
+ return false;
+ }
+
+ return aRequest1->DefinitelyEquals(*aRequest2);
+}
+
// A nullsafe wrapper for strcmp. We depend on null-safety.
static int
safe_strcmp(const char16_t* a, const char16_t* b)
{
if (!a || !b) {
return (int)(a - b);
}
return NS_strcmp(a, b);
@@ -1866,16 +1881,184 @@ nsStyleGradient::HasCalc()
if (mStops[i].mLocation.IsCalcUnit()) {
return true;
}
}
return mBgPosX.IsCalcUnit() || mBgPosY.IsCalcUnit() || mAngle.IsCalcUnit() ||
mRadiusX.IsCalcUnit() || mRadiusY.IsCalcUnit();
}
+
+// --------------------
+// nsStyleImageRequest
+
+/**
+ * Runnable to release the nsStyleImageRequest's mRequestProxy,
+ * mImageValue and mImageValue 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<css::ImageValue> aImageValue,
+ already_AddRefed<ImageTracker> aImageTracker)
+ : mModeFlags(aModeFlags)
+ , mRequestProxy(aRequestProxy)
+ , mImageValue(aImageValue)
+ , mImageTracker(aImageTracker)
+ {
+ }
+
+ NS_IMETHOD Run() final
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mRequestProxy) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mImageTracker);
+
+ if (mModeFlags & Mode::Lock) {
+ mRequestProxy->UnlockImage();
+ }
+
+ if (mModeFlags & Mode::Discard) {
+ mRequestProxy->RequestDiscard();
+ }
+
+ if (mModeFlags & Mode::Track) {
+ mImageTracker->Remove(mRequestProxy);
+ }
+
+ return NS_OK;
+ }
+
+protected:
+ virtual ~StyleImageRequestCleanupTask() { MOZ_ASSERT(NS_IsMainThread()); }
+
+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<css::ImageValue> mImageValue;
+ RefPtr<ImageTracker> mImageTracker;
+};
+
+nsStyleImageRequest::nsStyleImageRequest(Mode aModeFlags,
+ imgRequestProxy* aRequestProxy,
+ css::ImageValue* aImageValue,
+ ImageTracker* aImageTracker)
+ : mRequestProxy(aRequestProxy)
+ , mImageValue(aImageValue)
+ , mImageTracker(aImageTracker)
+ , mModeFlags(aModeFlags)
+ , mResolved(true)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aRequestProxy);
+ MOZ_ASSERT(aImageValue);
+ MOZ_ASSERT(aImageTracker);
+
+ MaybeTrackAndLock();
+}
+
+nsStyleImageRequest::nsStyleImageRequest(
+ Mode aModeFlags,
+ nsStringBuffer* aURLBuffer,
+ already_AddRefed<PtrHolder<nsIURI>> aBaseURI,
+ already_AddRefed<PtrHolder<nsIURI>> aReferrer,
+ already_AddRefed<PtrHolder<nsIPrincipal>> aPrincipal)
+ : mModeFlags(aModeFlags)
+ , mResolved(false)
+{
+ mImageValue = new css::ImageValue(aURLBuffer, Move(aBaseURI),
+ Move(aReferrer), Move(aPrincipal));
+}
+
+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 mImageValue, all on the main thread.
+ {
+ RefPtr<StyleImageRequestCleanupTask> task =
+ new StyleImageRequestCleanupTask(mModeFlags,
+ mRequestProxy.forget(),
+ mImageValue.forget(),
+ mImageTracker.forget());
+ if (NS_IsMainThread()) {
+ task->Run();
+ } else {
+ NS_DispatchToMainThread(task.forget());
+ }
+ }
+
+ MOZ_ASSERT(!mRequestProxy);
+ MOZ_ASSERT(!mImageValue);
+ MOZ_ASSERT(!mImageTracker);
+}
+
+bool
+nsStyleImageRequest::Resolve(nsPresContext* aPresContext)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!IsResolved(), "already resolved");
+
+ mResolved = true;
+
+ // For now, just have unique nsCSSValue/ImageValue objects. We should
+ // really store the ImageValue on the Servo specified value, so that we can
+ // share imgRequestProxys that come from the same rule in the same
+ // document.
+ mImageValue->Initialize(aPresContext->Document());
+
+ nsCSSValue value;
+ value.SetImageValue(mImageValue);
+ mRequestProxy = value.GetPossiblyStaticImageValue(aPresContext->Document(),
+ aPresContext);
+
+ if (!mRequestProxy) {
+ // The URL resolution or image load failed.
+ return false;
+ }
+
+ mImageTracker = aPresContext->Document()->ImageTracker();
+ MaybeTrackAndLock();
+ return true;
+}
+
+void
+nsStyleImageRequest::MaybeTrackAndLock()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsResolved());
+ MOZ_ASSERT(mRequestProxy);
+ MOZ_ASSERT(mImageTracker);
+
+ if (mModeFlags & Mode::Track) {
+ mImageTracker->Add(mRequestProxy);
+ }
+
+ if (mModeFlags & Mode::Lock) {
+ mRequestProxy->LockImage();
+ }
+}
+
+bool
+nsStyleImageRequest::DefinitelyEquals(const nsStyleImageRequest& aOther) const
+{
+ return DefinitelyEqualURIs(mImageValue, aOther.mImageValue);
+}
+
// --------------------
// CachedBorderImageData
//
void
CachedBorderImageData::SetCachedSVGViewportSize(
const mozilla::Maybe<nsSize>& aSVGViewportSize)
{
mCachedSVGViewportSize = aSVGViewportSize;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -282,16 +282,107 @@ public:
private:
// Private destructor, to discourage deletion outside of Release():
~nsStyleGradient() {}
nsStyleGradient(const nsStyleGradient& aOther) = delete;
nsStyleGradient& operator=(const nsStyleGradient& aOther) = delete;
};
+/**
+ * A wrapper for an imgRequestProxy that supports off-main-thread creation
+ * and equality comparison.
+ *
+ * An nsStyleImageRequest can be created in two ways:
+ *
+ * 1. Using the constructor that takes an imgRequestProxy. This must
+ * be called from the main thread. The nsStyleImageRequest is
+ * immediately considered "resolved", and the get() method that
+ * returns the imgRequestProxy can be called.
+ *
+ * 2. Using the constructor that takes the URL, base URI, referrer
+ * and principal that can be used to inititiate 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 main thread constructor takes a pointer to the css::ImageValue that
+ * is the specified url() value, while the off-main-thread constructor
+ * creates a new css::ImageValue to represent the url() information passed
+ * to the constructor. This ImageValue is held on to for the comparisons done
+ * in DefinitelyEquals(), so that we don't need to call into the non-OMT-safe
+ * Equals() on the nsIURI objects returned from imgRequestProxy::GetURI().
+ */
+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 {
+ Track = 0x1, // used by all except nsCursorImage
+ Lock = 0x2, // used by all except nsStyleContentData
+ Discard = 0x4, // used only by nsCursorImage
+ };
+
+ // Must be called from the main thread.
+ nsStyleImageRequest(Mode aModeFlags,
+ imgRequestProxy* aRequestProxy,
+ mozilla::css::ImageValue* aImageValue,
+ mozilla::dom::ImageTracker* aImageTracker);
+
+ // Can be called from any thread, but Resolve() must be called later
+ // on the main thread before get() can be used.
+ nsStyleImageRequest(
+ Mode aModeFlags,
+ nsStringBuffer* aURLBuffer,
+ already_AddRefed<mozilla::PtrHolder<nsIURI>> aBaseURI,
+ already_AddRefed<mozilla::PtrHolder<nsIURI>> aReferrer,
+ already_AddRefed<mozilla::PtrHolder<nsIPrincipal>> aPrincipal);
+
+ bool Resolve(nsPresContext* aPresContext);
+ 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 ImageValue objects in the two nsStyleImageRequests
+ // return true from URLValueData::DefinitelyEqualURIs.
+ bool DefinitelyEquals(const nsStyleImageRequest& aOther) const;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsStyleImageRequest);
+
+private:
+ ~nsStyleImageRequest();
+ nsStyleImageRequest& operator=(const nsStyleImageRequest& aOther) = delete;
+
+ void MaybeTrackAndLock();
+
+ RefPtr<imgRequestProxy> mRequestProxy;
+ RefPtr<mozilla::css::ImageValue> mImageValue;
+ RefPtr<mozilla::dom::ImageTracker> mImageTracker;
+
+ Mode mModeFlags;
+ bool mResolved;
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsStyleImageRequest::Mode)
+
enum nsStyleImageType {
eStyleImageType_Null,
eStyleImageType_Image,
eStyleImageType_Gradient,
eStyleImageType_Element
};
struct CachedBorderImageData