mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 870022 - Part 3.2 - Add sizes support to ResponsiveImageSelector. r=jst
This commit is contained in:
parent
ec902ea67d
commit
642a8f19b0
@ -10,6 +10,12 @@
|
||||
#include "nsPresContext.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
#include "nsCSSParser.h"
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsIMediaList.h"
|
||||
#include "nsRuleNode.h"
|
||||
#include "nsRuleData.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -164,11 +170,36 @@ ResponsiveImageSelector::SetDefaultSource(nsIURI *aURL)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ResponsiveImageSelector::SetSizesFromDescriptor(const nsAString & aSizes)
|
||||
{
|
||||
mSizeQueries.Clear();
|
||||
mSizeValues.Clear();
|
||||
mBestCandidateIndex = -1;
|
||||
|
||||
nsCSSParser cssParser;
|
||||
|
||||
if (!cssParser.ParseSourceSizeList(aSizes, nullptr, 0,
|
||||
mSizeQueries, mSizeValues, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mSizeQueries.Length() > 0;
|
||||
}
|
||||
|
||||
void
|
||||
ResponsiveImageSelector::AppendCandidateIfUnique(const ResponsiveImageCandidate & aCandidate)
|
||||
{
|
||||
// Discard candidates with identical parameters, they will never match
|
||||
int numCandidates = mCandidates.Length();
|
||||
|
||||
// With the exception of Default, which should not be added until we are done
|
||||
// building the list, the spec forbids mixing width and explicit density
|
||||
// selectors in the same set.
|
||||
if (numCandidates && mCandidates[0].Type() != aCandidate.Type()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Discard candidates with identical parameters, they will never match
|
||||
for (int i = 0; i < numCandidates; i++) {
|
||||
if (mCandidates[i].HasSameParameter(aCandidate)) {
|
||||
return;
|
||||
@ -214,7 +245,7 @@ ResponsiveImageSelector::GetSelectedImageDensity()
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
return mCandidates[bestIndex].Density();
|
||||
return mCandidates[bestIndex].Density(this);
|
||||
}
|
||||
|
||||
int
|
||||
@ -244,24 +275,91 @@ ResponsiveImageSelector::GetBestCandidateIndex()
|
||||
// - For now, select the lowest density greater than displayDensity, otherwise
|
||||
// the greatest density available
|
||||
|
||||
int bestIndex = 0; // First index will always be the best so far
|
||||
double bestDensity = mCandidates[bestIndex].Density();
|
||||
for (int i = 1; i < numCandidates; i++) {
|
||||
double candidateDensity = mCandidates[i].Density();
|
||||
// If the list contains computed width candidates, compute the current
|
||||
// effective image width. Note that we currently disallow both computed and
|
||||
// static density candidates in the same selector, so checking the first
|
||||
// candidate is sufficient.
|
||||
int32_t computedWidth = -1;
|
||||
if (numCandidates && mCandidates[0].IsComputedFromWidth()) {
|
||||
DebugOnly<bool> computeResult = \
|
||||
ComputeFinalWidthForCurrentViewport(&computedWidth);
|
||||
MOZ_ASSERT(computeResult,
|
||||
"Computed candidates not allowed without sizes data");
|
||||
|
||||
// If we have a default candidate in the list, don't consider it when using
|
||||
// computed widths. (It has a static 1.0 density that is inapplicable to a
|
||||
// sized-image)
|
||||
if (numCandidates > 1 && mCandidates[numCandidates - 1].Type() ==
|
||||
ResponsiveImageCandidate::eCandidateType_Default) {
|
||||
numCandidates--;
|
||||
}
|
||||
}
|
||||
|
||||
int bestIndex = -1;
|
||||
double bestDensity = -1.0;
|
||||
for (int i = 0; i < numCandidates; i++) {
|
||||
double candidateDensity = \
|
||||
(computedWidth == -1) ? mCandidates[i].Density(this)
|
||||
: mCandidates[i].Density(computedWidth);
|
||||
// - If bestIndex is below display density, pick anything larger.
|
||||
// - Otherwise, prefer if less dense than bestDensity but still above
|
||||
// displayDensity.
|
||||
if ((bestDensity < displayDensity && candidateDensity > bestDensity) ||
|
||||
(candidateDensity > displayDensity && candidateDensity < bestDensity)) {
|
||||
if (bestIndex == -1 ||
|
||||
(bestDensity < displayDensity && candidateDensity > bestDensity) ||
|
||||
(candidateDensity >= displayDensity && candidateDensity < bestDensity)) {
|
||||
bestIndex = i;
|
||||
bestDensity = candidateDensity;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(bestIndex >= 0 && bestIndex < numCandidates);
|
||||
mBestCandidateIndex = bestIndex;
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
bool
|
||||
ResponsiveImageSelector::ComputeFinalWidthForCurrentViewport(int32_t *aWidth)
|
||||
{
|
||||
unsigned int numSizes = mSizeQueries.Length();
|
||||
if (!numSizes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIDocument* doc = mContent ? mContent->OwnerDoc() : nullptr;
|
||||
nsIPresShell *presShell = doc ? doc->GetShell() : nullptr;
|
||||
nsPresContext *pctx = presShell ? presShell->GetPresContext() : nullptr;
|
||||
|
||||
if (!pctx) {
|
||||
MOZ_ASSERT(false, "Unable to find presContext for this content");
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(numSizes == mSizeValues.Length(),
|
||||
"mSizeValues length differs from mSizeQueries");
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < numSizes; i++) {
|
||||
if (mSizeQueries[i]->Matches(pctx, nullptr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nscoord effectiveWidth;
|
||||
if (i == numSizes) {
|
||||
// No match defaults to 100% viewport
|
||||
nsCSSValue defaultWidth(100.0f, eCSSUnit_ViewportWidth);
|
||||
effectiveWidth = nsRuleNode::CalcLengthWithInitialFont(pctx,
|
||||
defaultWidth);
|
||||
} else {
|
||||
effectiveWidth = nsRuleNode::CalcLengthWithInitialFont(pctx,
|
||||
mSizeValues[i]);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(effectiveWidth >= 0);
|
||||
*aWidth = nsPresContext::AppUnitsToIntCSSPixels(std::max(effectiveWidth, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
ResponsiveImageCandidate::ResponsiveImageCandidate()
|
||||
{
|
||||
mType = eCandidateType_Invalid;
|
||||
@ -408,10 +506,33 @@ ResponsiveImageCandidate::HasSameParameter(const ResponsiveImageCandidate & aOth
|
||||
return false;
|
||||
}
|
||||
|
||||
double
|
||||
ResponsiveImageCandidate::Density() const
|
||||
already_AddRefed<nsIURI>
|
||||
ResponsiveImageCandidate::URL() const
|
||||
{
|
||||
nsCOMPtr<nsIURI> url = mURL;
|
||||
return url.forget();
|
||||
}
|
||||
|
||||
double
|
||||
ResponsiveImageCandidate::Density(ResponsiveImageSelector *aSelector) const
|
||||
{
|
||||
if (mType == eCandidateType_ComputedFromWidth) {
|
||||
int32_t width;
|
||||
if (!aSelector->ComputeFinalWidthForCurrentViewport(&width)) {
|
||||
return 1.0;
|
||||
}
|
||||
return Density(width);
|
||||
}
|
||||
|
||||
// Other types don't need matching width
|
||||
MOZ_ASSERT(mType == eCandidateType_Default || mType == eCandidateType_Density,
|
||||
"unhandled candidate type");
|
||||
return Density(-1);
|
||||
}
|
||||
|
||||
double
|
||||
ResponsiveImageCandidate::Density(int32_t aMatchingWidth) const
|
||||
{
|
||||
// When we support 'sizes' this will get more interesting
|
||||
if (mType == eCandidateType_Invalid) {
|
||||
MOZ_ASSERT(false, "Getting density for uninitialized candidate");
|
||||
return 1.0;
|
||||
@ -423,19 +544,30 @@ ResponsiveImageCandidate::Density() const
|
||||
|
||||
if (mType == eCandidateType_Density) {
|
||||
return mValue.mDensity;
|
||||
} else if (mType == eCandidateType_ComputedFromWidth) {
|
||||
if (aMatchingWidth <= 0) {
|
||||
MOZ_ASSERT(false, "0 or negative matching width is invalid per spec");
|
||||
return 1.0;
|
||||
}
|
||||
double density = double(mValue.mWidth) / double(aMatchingWidth);
|
||||
MOZ_ASSERT(density > 0.0);
|
||||
return density;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "Bad candidate type in Density()");
|
||||
MOZ_ASSERT(false, "Unknown candidate type");
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIURI>
|
||||
ResponsiveImageCandidate::URL() const
|
||||
bool
|
||||
ResponsiveImageCandidate::IsComputedFromWidth() const
|
||||
{
|
||||
MOZ_ASSERT(mType != eCandidateType_Invalid,
|
||||
"Getting URL of incomplete candidate");
|
||||
nsCOMPtr<nsIURI> url = mURL;
|
||||
return url.forget();
|
||||
if (mType == eCandidateType_ComputedFromWidth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mType == eCandidateType_Default || mType == eCandidateType_Density,
|
||||
"Unknown candidate type");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -10,6 +10,9 @@
|
||||
#include "nsIContent.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsMediaQuery;
|
||||
class nsCSSValue;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -17,6 +20,7 @@ class ResponsiveImageCandidate;
|
||||
|
||||
class ResponsiveImageSelector : public nsISupports
|
||||
{
|
||||
friend class ResponsiveImageCandidate;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
ResponsiveImageSelector(nsIContent *aContent);
|
||||
@ -25,6 +29,10 @@ public:
|
||||
// replace default source)
|
||||
bool SetCandidatesFromSourceSet(const nsAString & aSrcSet);
|
||||
|
||||
// Fill the source sizes from a valid sizes descriptor. Returns false if
|
||||
// descriptor is invalid.
|
||||
bool SetSizesFromDescriptor(const nsAString & aSizesDescriptor);
|
||||
|
||||
// Set the default source, treated as the least-precedence 1.0 density source.
|
||||
nsresult SetDefaultSource(const nsAString & aSpec);
|
||||
void SetDefaultSource(nsIURI *aURL);
|
||||
@ -48,11 +56,21 @@ private:
|
||||
// Get index of best candidate
|
||||
int GetBestCandidateIndex();
|
||||
|
||||
// Compute a density from a Candidate width. Returns false if sizes were not
|
||||
// specified for this selector.
|
||||
//
|
||||
// aContext is the presContext to use for current viewport sizing, null will
|
||||
// use the associated content's context.
|
||||
bool ComputeFinalWidthForCurrentViewport(int32_t *aWidth);
|
||||
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
// If this array contains an eCandidateType_Default, it should be the last
|
||||
// element, such that the Setters can preserve/replace it respectively.
|
||||
nsTArray<ResponsiveImageCandidate> mCandidates;
|
||||
int mBestCandidateIndex;
|
||||
|
||||
nsTArray< nsAutoPtr<nsMediaQuery> > mSizeQueries;
|
||||
nsTArray<nsCSSValue> mSizeValues;
|
||||
};
|
||||
|
||||
class ResponsiveImageCandidate {
|
||||
@ -78,7 +96,15 @@ public:
|
||||
bool HasSameParameter(const ResponsiveImageCandidate & aOther) const;
|
||||
|
||||
already_AddRefed<nsIURI> URL() const;
|
||||
double Density() const;
|
||||
|
||||
// Compute and return the density relative to a selector.
|
||||
double Density(ResponsiveImageSelector *aSelector) const;
|
||||
// If the width is already known. Useful when iterating over candidates to
|
||||
// avoid having each call re-compute the width.
|
||||
double Density(int32_t aMatchingWidth) const;
|
||||
|
||||
// If this selector is computed from the selector's matching width.
|
||||
bool IsComputedFromWidth() const;
|
||||
|
||||
enum eCandidateType {
|
||||
eCandidateType_Invalid,
|
||||
|
Loading…
Reference in New Issue
Block a user