Files
palemoon27/dom/base/ResponsiveImageSelector.cpp
T
roytam1 1e24b22ef4 remove nsINetUtil_ESR_38, fixup and import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1150366 - Remove duplicate d3.js. r=jsantell (71f991da6)
- Bug 1175352 - Refactor LoadInfo arguments to be more self contained (r=bent) (3f7d08164)
- Bug 1181533 - Add nsCRTGlue.h and prtime.h includes to nsDownloadManager.cpp to fix --disable-safe-browsing build bustage. r=froydnj (af338edf1)
- Bug 905127 - Part 1 - Make some functions from nsNetUtil not inline. r=jduell Bug 905127 - Part 2 - remove unnecessary nsNetUtil.h includes r=jduell Bug 905127 - Make some functions from nsNetUtil not inline. r=jduell (bc47d5b3a)
- Bug 1117650 - Part 1: Move all CORS tests into dom/security/test. r=sicking (6f727387c)
- Bug 1117650 - Part 2: Move all MixedContent tests into dom/security/test. r=tanvi (73f273829)
- Bug 1004703 - ignore 'unsafe-inline' if nonce- or hash-source specifed - tests (r=sstamm) (ebf411159)
- Bug 921493 - CSP: test whitelisting of scheme-relative sources (r=dveditz) (f0618b189)
- Bug 1139569 - Optimize update function for element editors in markup view;r=mratcliffe (c60d38dca)
- Partil of Bug 1139644 - Make markupview use sdk timers instead of individual timers for each window;r=pbrosset (8bc1a4d66)
- Bug 1146568 - Avoid unsafe CPOW usage warnings in browser_markupview_tag_edit_08.js; r=bgrins Making use of the devtools test frame-script to set the test node's attributes instead of using a CPOW. (61d7eed64)
- Bug 1146568 - Avoid unsafe CPOW usage in markupview tests, in assertAttributes helper; r=bgrins (efc492d5d)
- Bug 858038 - Allow moving elements in the markupview by drag/drop; r=pbrosset (51d79c15c)
- Bug 1139644 - Flash only relevant attributes in markup view when changed;r=pbrosset (29807fa46)
- Bug 1147128 - Make sure attribute shows up in markup view after removing and setting to the previous value;r=mratcliffe (1947f7417)
- Bug 1147325 - Clear box model timer on markup view destroy. r=pbrosset Prevents 'this._inspector.toolbox is null' spam in mochitest-dt (1d6808b4a)
- Bug 921102 - 1 - Linkify URIs in the inspector; r=tromey, miker This first part adds a parser for node attributes which, given some node information and an attribute name, generates a small AST-like array of objects that tells which parts of the attribute (if any) are links, and what they link to. Using this, the markup-view generates the right HTML structure to display these parts as links. This part 1 doesn't yet allow users to follow these links. (c500e270e)
- Bug 1139667 - CSP tests for fetch(). r=ckirschb (df030457f)
- Bug 1086999 - CSP: Asterisk (*) wildcard should not allow blob:, data:, or filesystem: when matching source expressions - tests (r=sstamm) (dee0f2239)
- Bug 1117650 - Part 3: Move all CSP tests into dom/security/test. r=sstamm (29c95475c)
- Bug 1146566 - 1 - Use devtools common frame-script in markupview tests and add helper; r=bgrins (b10e42468)
- Bug 1146566 - 2 - Avoid using CPOWs in test browser_markupview_tag_edit_03.js; r=bgrins This change updates the browser_markupview_tag_edit_03.js test by making use of the getDomElementInfo message listener to retrieve information about the tested nodes without having to go through CPOWs. (07f0ee3ff)
- Bug 1036324 - Adds option to walker.parents() to not traverse DocShellTreeItems of different types (24123a5d2)
- Bug 921102 - 2 - Markup-view tests for attribute links; r=miker (f6da81799)
- Bug 901250 - Add scroll into view menu item for the inspector. r=bgrins (bfc7518b1)
- Bug 994055 - Part 1: Add a toggle sidebar panel button to the inspector. r=miker This adds a new icon, next to the searchbox (like in the debugger) to expand or collapse the sidebar panel in the inspector. The state is *not* persisted yet when the toolbox is closed. The button is hidden when the toolbox host switches to 'side'. (87730e20a)
- Bug 994055 - Part 2: Add tests for the inspector sidebar toggle button. r=miker (2872c3fbd)
- Bug 921102 - 3 - Open/copy markup-view attribute links; r=bgrins This part adds contextual menu items that become enabled when the user right clicks on an attribute that has a link. Depending on the nature of the link, a new tab will be opened or a node selected. The user can also choose to copy the link in the clipboard. (9b93485f6)
- Bug 921102 - 4 - Tests for the open/copy links on markup-view attributes; r=bgrins (861ba51f6)
2021-05-21 09:14:47 +08:00

733 lines
21 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/ResponsiveImageSelector.h"
#include "nsIURI.h"
#include "nsIDocument.h"
#include "nsContentUtils.h"
#include "nsPresContext.h"
#include "nsCSSParser.h"
#include "nsCSSProps.h"
#include "nsIMediaList.h"
#include "nsRuleNode.h"
#include "nsRuleData.h"
// For IsPictureEnabled() -- the candidate parser needs to be aware of sizes
// support being enabled
#include "HTMLPictureElement.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION(ResponsiveImageSelector, mOwnerNode)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ResponsiveImageSelector, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ResponsiveImageSelector, Release)
static bool
ParseInteger(const nsAString& aString, int32_t& aInt)
{
nsContentUtils::ParseHTMLIntegerResultFlags parseResult;
aInt = nsContentUtils::ParseHTMLInteger(aString, &parseResult);
return !(parseResult &
( nsContentUtils::eParseHTMLInteger_Error |
nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput |
nsContentUtils::eParseHTMLInteger_IsPercent |
nsContentUtils::eParseHTMLInteger_NonStandard ));
}
ResponsiveImageSelector::ResponsiveImageSelector(nsIContent *aContent)
: mOwnerNode(aContent),
mSelectedCandidateIndex(-1)
{
}
ResponsiveImageSelector::ResponsiveImageSelector(nsIDocument *aDocument)
: mOwnerNode(aDocument),
mSelectedCandidateIndex(-1)
{
}
ResponsiveImageSelector::~ResponsiveImageSelector()
{}
// http://www.whatwg.org/specs/web-apps/current-work/#processing-the-image-candidates
bool
ResponsiveImageSelector::SetCandidatesFromSourceSet(const nsAString & aSrcSet)
{
ClearSelectedCandidate();
nsCOMPtr<nsIURI> docBaseURI = mOwnerNode ? mOwnerNode->GetBaseURI() : nullptr;
if (!docBaseURI) {
MOZ_ASSERT(false,
"Should not be parsing SourceSet without a document");
return false;
}
// Preserve the default source if we have one, it has a separate setter.
uint32_t prevNumCandidates = mCandidates.Length();
nsString defaultURLString;
if (prevNumCandidates && (mCandidates[prevNumCandidates - 1].Type() ==
ResponsiveImageCandidate::eCandidateType_Default)) {
defaultURLString = mCandidates[prevNumCandidates - 1].URLString();
}
mCandidates.Clear();
nsAString::const_iterator iter, end;
aSrcSet.BeginReading(iter);
aSrcSet.EndReading(end);
// Read URL / descriptor pairs
while (iter != end) {
nsAString::const_iterator url, urlEnd, descriptor;
// Skip whitespace and commas.
// Extra commas at this point are a non-fatal syntax error.
for (; iter != end && (nsContentUtils::IsHTMLWhitespace(*iter) ||
*iter == char16_t(',')); ++iter);
if (iter == end) {
break;
}
url = iter;
// Find end of url
for (;iter != end && !nsContentUtils::IsHTMLWhitespace(*iter); ++iter);
// Omit trailing commas from URL.
// Multiple commas are a non-fatal error.
while (iter != url) {
if (*(--iter) != char16_t(',')) {
iter++;
break;
}
}
const nsDependentSubstring &urlStr = Substring(url, iter);
MOZ_ASSERT(url != iter, "Shouldn't have empty URL at this point");
ResponsiveImageCandidate candidate;
if (candidate.ConsumeDescriptors(iter, end)) {
candidate.SetURLSpec(urlStr);
AppendCandidateIfUnique(candidate);
}
}
bool parsedCandidates = mCandidates.Length() > 0;
// Re-add default to end of list
if (!defaultURLString.IsEmpty()) {
AppendDefaultCandidate(defaultURLString);
}
return parsedCandidates;
}
uint32_t
ResponsiveImageSelector::NumCandidates(bool aIncludeDefault)
{
uint32_t candidates = mCandidates.Length();
// If present, the default candidate is the last item
if (!aIncludeDefault && candidates &&
(mCandidates[candidates - 1].Type() ==
ResponsiveImageCandidate::eCandidateType_Default)) {
candidates--;
}
return candidates;
}
nsIContent*
ResponsiveImageSelector::Content()
{
return mOwnerNode->IsContent() ? mOwnerNode->AsContent() : nullptr;
}
nsIDocument*
ResponsiveImageSelector::Document()
{
return mOwnerNode->OwnerDoc();
}
void
ResponsiveImageSelector::SetDefaultSource(const nsAString& aURLString)
{
ClearSelectedCandidate();
// Check if the last element of our candidates is a default
int32_t candidates = mCandidates.Length();
if (candidates && (mCandidates[candidates - 1].Type() ==
ResponsiveImageCandidate::eCandidateType_Default)) {
mCandidates.RemoveElementAt(candidates - 1);
}
// Add new default if set
if (!aURLString.IsEmpty()) {
AppendDefaultCandidate(aURLString);
}
}
void
ResponsiveImageSelector::ClearSelectedCandidate()
{
mSelectedCandidateIndex = -1;
mSelectedCandidateURL = nullptr;
}
bool
ResponsiveImageSelector::SetSizesFromDescriptor(const nsAString & aSizes)
{
ClearSelectedCandidate();
mSizeQueries.Clear();
mSizeValues.Clear();
nsCSSParser cssParser;
if (!cssParser.ParseSourceSizeList(aSizes, nullptr, 0,
mSizeQueries, mSizeValues, true)) {
return false;
}
return mSizeQueries.Length() > 0;
}
void
ResponsiveImageSelector::AppendCandidateIfUnique(const ResponsiveImageCandidate & aCandidate)
{
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;
}
}
mCandidates.AppendElement(aCandidate);
}
void
ResponsiveImageSelector::AppendDefaultCandidate(const nsAString& aURLString)
{
NS_ENSURE_TRUE(!aURLString.IsEmpty(), /* void */);
ResponsiveImageCandidate defaultCandidate;
defaultCandidate.SetParameterDefault();
defaultCandidate.SetURLSpec(aURLString);
// We don't use MaybeAppend since we want to keep this even if it can never
// match, as it may if the source set changes.
mCandidates.AppendElement(defaultCandidate);
}
already_AddRefed<nsIURI>
ResponsiveImageSelector::GetSelectedImageURL()
{
SelectImage();
nsCOMPtr<nsIURI> url = mSelectedCandidateURL;
return url.forget();
}
bool
ResponsiveImageSelector::GetSelectedImageURLSpec(nsAString& aResult)
{
SelectImage();
if (mSelectedCandidateIndex == -1) {
return false;
}
aResult.Assign(mCandidates[mSelectedCandidateIndex].URLString());
return true;
}
double
ResponsiveImageSelector::GetSelectedImageDensity()
{
int bestIndex = GetSelectedCandidateIndex();
if (bestIndex < 0) {
return 1.0;
}
return mCandidates[bestIndex].Density(this);
}
bool
ResponsiveImageSelector::SelectImage(bool aReselect)
{
if (!aReselect && mSelectedCandidateIndex != -1) {
// Already have selection
return false;
}
int oldBest = mSelectedCandidateIndex;
ClearSelectedCandidate();
int numCandidates = mCandidates.Length();
if (!numCandidates) {
return oldBest != -1;
}
nsIDocument* doc = Document();
nsIPresShell *shell = doc ? doc->GetShell() : nullptr;
nsPresContext *pctx = shell ? shell->GetPresContext() : nullptr;
nsCOMPtr<nsIURI> baseURI = mOwnerNode ? mOwnerNode->GetBaseURI() : nullptr;
if (!pctx || !doc || !baseURI) {
MOZ_ASSERT(false, "Unable to find document prescontext and base URI");
return oldBest != -1;
}
double displayDensity = pctx->CSSPixelsToDevPixels(1.0f);
// Per spec, "In a UA-specific manner, choose one image source"
// - For now, select the lowest density greater than displayDensity, otherwise
// the greatest density available
// 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 (bestIndex == -1 ||
(bestDensity < displayDensity && candidateDensity > bestDensity) ||
(candidateDensity >= displayDensity && candidateDensity < bestDensity)) {
bestIndex = i;
bestDensity = candidateDensity;
}
}
MOZ_ASSERT(bestIndex >= 0 && bestIndex < numCandidates);
// Resolve URL
nsresult rv;
const nsAString& urlStr = mCandidates[bestIndex].URLString();
nsCOMPtr<nsIURI> candidateURL;
rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(candidateURL),
urlStr, doc, baseURI);
mSelectedCandidateURL = NS_SUCCEEDED(rv) ? candidateURL : nullptr;
mSelectedCandidateIndex = bestIndex;
return mSelectedCandidateIndex != oldBest;
}
int
ResponsiveImageSelector::GetSelectedCandidateIndex()
{
SelectImage();
return mSelectedCandidateIndex;
}
bool
ResponsiveImageSelector::ComputeFinalWidthForCurrentViewport(int32_t *aWidth)
{
unsigned int numSizes = mSizeQueries.Length();
nsIDocument* doc = Document();
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;
mValue.mDensity = 1.0;
}
ResponsiveImageCandidate::ResponsiveImageCandidate(const nsAString& aURLString,
double aDensity)
: mURLString(aURLString)
{
mType = eCandidateType_Density;
mValue.mDensity = aDensity;
}
void
ResponsiveImageCandidate::SetURLSpec(const nsAString& aURLString)
{
mURLString = aURLString;
}
void
ResponsiveImageCandidate::SetParameterAsComputedWidth(int32_t aWidth)
{
mType = eCandidateType_ComputedFromWidth;
mValue.mWidth = aWidth;
}
void
ResponsiveImageCandidate::SetParameterDefault()
{
MOZ_ASSERT(mType == eCandidateType_Invalid, "double setting candidate type");
mType = eCandidateType_Default;
// mValue shouldn't actually be used for this type, but set it to default
// anyway
mValue.mDensity = 1.0;
}
void
ResponsiveImageCandidate::SetParameterInvalid()
{
mType = eCandidateType_Invalid;
// mValue shouldn't actually be used for this type, but set it to default
// anyway
mValue.mDensity = 1.0;
}
void
ResponsiveImageCandidate::SetParameterAsDensity(double aDensity)
{
MOZ_ASSERT(mType == eCandidateType_Invalid, "double setting candidate type");
mType = eCandidateType_Density;
mValue.mDensity = aDensity;
}
// Represents all supported descriptors for a ResponsiveImageCandidate, though
// there is no candidate type that uses all of these. This should generally
// match the mValue union of ResponsiveImageCandidate.
struct ResponsiveImageDescriptors {
ResponsiveImageDescriptors()
: mInvalid(false) {};
Maybe<double> mDensity;
Maybe<int32_t> mWidth;
// We don't support "h" descriptors yet and they are not spec'd, but the
// current spec does specify that they can be silently ignored (whereas
// entirely unknown descriptors cause us to invalidate the candidate)
Maybe<int32_t> mFutureCompatHeight;
// If this descriptor set is bogus, e.g. a value was added twice (and thus
// dropped) or an unknown descriptor was added.
bool mInvalid;
void AddDescriptor(const nsAString& aDescriptor);
bool Valid();
// Use the current set of descriptors to configure a candidate
void FillCandidate(ResponsiveImageCandidate &aCandidate);
};
// Try to parse a single descriptor from a string. If value already set or
// unknown, sets invalid flag.
// This corresponds to the descriptor "Descriptor parser" step in:
// https://html.spec.whatwg.org/#parse-a-srcset-attribute
void
ResponsiveImageDescriptors::AddDescriptor(const nsAString& aDescriptor)
{
if (aDescriptor.IsEmpty()) {
return;
}
// All currently supported descriptors end with an identifying character.
nsAString::const_iterator descStart, descType;
aDescriptor.BeginReading(descStart);
aDescriptor.EndReading(descType);
descType--;
const nsDependentSubstring& valueStr = Substring(descStart, descType);
if (*descType == char16_t('w')) {
int32_t possibleWidth;
// If the value is not a valid non-negative integer, it doesn't match this
// descriptor, fall through.
if (ParseInteger(valueStr, possibleWidth) && possibleWidth >= 0) {
if (possibleWidth != 0 && HTMLPictureElement::IsPictureEnabled() &&
mWidth.isNothing() && mDensity.isNothing()) {
mWidth.emplace(possibleWidth);
} else {
// Valid width descriptor, but width or density were already seen, sizes
// support isn't enabled, or it parsed to 0, which is an error per spec
mInvalid = true;
}
return;
}
} else if (*descType == char16_t('h')) {
int32_t possibleHeight;
// If the value is not a valid non-negative integer, it doesn't match this
// descriptor, fall through.
if (ParseInteger(valueStr, possibleHeight) && possibleHeight >= 0) {
if (possibleHeight != 0 && mFutureCompatHeight.isNothing() &&
mDensity.isNothing()) {
mFutureCompatHeight.emplace(possibleHeight);
} else {
// Valid height descriptor, but height or density were already seen, or
// it parsed to zero, which is an error per spec
mInvalid = true;
}
return;
}
} else if (*descType == char16_t('x')) {
// If the value is not a valid floating point number, it doesn't match this
// descriptor, fall through.
nsresult rv;
double possibleDensity = PromiseFlatString(valueStr).ToDouble(&rv);
if (NS_SUCCEEDED(rv)) {
if (possibleDensity >= 0.0 &&
mWidth.isNothing() &&
mDensity.isNothing() &&
mFutureCompatHeight.isNothing()) {
mDensity.emplace(possibleDensity);
} else {
// Valid density descriptor, but height or width or density were already
// seen, or it parsed to less than zero, which is an error per spec
mInvalid = true;
}
return;
}
}
// Matched no known descriptor, mark this descriptor set invalid
mInvalid = true;
}
bool
ResponsiveImageDescriptors::Valid()
{
return !mInvalid && !(mFutureCompatHeight.isSome() && mWidth.isNothing());
}
void
ResponsiveImageDescriptors::FillCandidate(ResponsiveImageCandidate &aCandidate)
{
if (!Valid()) {
aCandidate.SetParameterInvalid();
} else if (mWidth.isSome()) {
MOZ_ASSERT(mDensity.isNothing()); // Shouldn't be valid
aCandidate.SetParameterAsComputedWidth(*mWidth);
} else if (mDensity.isSome()) {
MOZ_ASSERT(mWidth.isNothing()); // Shouldn't be valid
aCandidate.SetParameterAsDensity(*mDensity);
} else {
// A valid set of descriptors with no density nor width (e.g. an empty set)
// becomes 1.0 density, per spec
aCandidate.SetParameterAsDensity(1.0);
}
}
bool
ResponsiveImageCandidate::ConsumeDescriptors(nsAString::const_iterator& aIter,
const nsAString::const_iterator& aIterEnd)
{
nsAString::const_iterator &iter = aIter;
const nsAString::const_iterator &end = aIterEnd;
bool inParens = false;
ResponsiveImageDescriptors descriptors;
// Parse descriptor list.
// This corresponds to the descriptor parsing loop from:
// https://html.spec.whatwg.org/#parse-a-srcset-attribute
// Skip initial whitespace
for (; iter != end && nsContentUtils::IsHTMLWhitespace(*iter); ++iter);
nsAString::const_iterator currentDescriptor = iter;
for (;; iter++) {
if (iter == end) {
descriptors.AddDescriptor(Substring(currentDescriptor, iter));
break;
} else if (inParens) {
if (*iter == char16_t(')')) {
inParens = false;
}
} else {
if (*iter == char16_t(',')) {
// End of descriptors, flush current descriptor and advance past comma
// before breaking
descriptors.AddDescriptor(Substring(currentDescriptor, iter));
iter++;
break;
} else if (nsContentUtils::IsHTMLWhitespace(*iter)) {
// End of current descriptor, consume it, skip spaces
// ("After descriptor" state in spec) before continuing
descriptors.AddDescriptor(Substring(currentDescriptor, iter));
for (; iter != end && nsContentUtils::IsHTMLWhitespace(*iter); ++iter);
if (iter == end) {
break;
}
currentDescriptor = iter;
// Leave one whitespace so the loop advances to this position next iteration
iter--;
} else if (*iter == char16_t('(')) {
inParens = true;
}
}
}
descriptors.FillCandidate(*this);
return Type() != eCandidateType_Invalid;
}
bool
ResponsiveImageCandidate::HasSameParameter(const ResponsiveImageCandidate & aOther) const
{
if (aOther.mType != mType) {
return false;
}
if (mType == eCandidateType_Default) {
return true;
}
if (mType == eCandidateType_Density) {
return aOther.mValue.mDensity == mValue.mDensity;
}
if (mType == eCandidateType_Invalid) {
MOZ_ASSERT(false, "Comparing invalid candidates?");
return true;
} else if (mType == eCandidateType_ComputedFromWidth) {
return aOther.mValue.mWidth == mValue.mWidth;
}
MOZ_ASSERT(false, "Somebody forgot to check for all uses of this enum");
return false;
}
const nsAString&
ResponsiveImageCandidate::URLString() const
{
return mURLString;
}
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
{
if (mType == eCandidateType_Invalid) {
MOZ_ASSERT(false, "Getting density for uninitialized candidate");
return 1.0;
}
if (mType == eCandidateType_Default) {
return 1.0;
}
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, "Unknown candidate type");
return 1.0;
}
bool
ResponsiveImageCandidate::IsComputedFromWidth() const
{
if (mType == eCandidateType_ComputedFromWidth) {
return true;
}
MOZ_ASSERT(mType == eCandidateType_Default || mType == eCandidateType_Density,
"Unknown candidate type");
return false;
}
} // namespace dom
} // namespace mozilla