mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1037643 - Part 2 - Line up with new spec for <img srcset> and <picture> mutations. r=bz
This commit is contained in:
parent
d421d27d96
commit
75413ef1f5
@ -16,6 +16,8 @@
|
||||
#include "nsIURL.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsContainerFrame.h"
|
||||
@ -49,6 +51,8 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
static const char *kPrefSrcsetEnabled = "dom.image.srcset.enabled";
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Image)
|
||||
|
||||
// Is aSubject a previous sibling of aNode.
|
||||
@ -69,6 +73,30 @@ static bool IsPreviousSibling(nsINode *aSubject, nsINode *aNode)
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Calls LoadSelectedImage on host element unless it has been superseded or
|
||||
// canceled -- this is the synchronous section of "update the image data".
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-image-data
|
||||
class ImageLoadTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit ImageLoadTask(HTMLImageElement *aElement) :
|
||||
mElement(aElement)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mElement->mPendingImageLoadTask == this) {
|
||||
mElement->mPendingImageLoadTask = nullptr;
|
||||
mElement->LoadSelectedImage(true, true);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~ImageLoadTask() {}
|
||||
nsRefPtr<HTMLImageElement> mElement;
|
||||
};
|
||||
|
||||
HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo)
|
||||
, mForm(nullptr)
|
||||
@ -367,39 +395,47 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
// from the parser or some such place; we'll get bound after all the
|
||||
// attributes have been set, so we'll do the image load from BindToTree.
|
||||
|
||||
nsCOMPtr<nsIContent> thisContent = AsContent();
|
||||
nsAttrValueOrString attrVal(aValue);
|
||||
|
||||
if (aName == nsGkAtoms::src &&
|
||||
aNameSpaceID == kNameSpaceID_None) {
|
||||
// SetAttr handles setting src in the non-responsive case, so only handle it
|
||||
// for responsive mode or unsetting
|
||||
if (!aValue) {
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
!aValue) {
|
||||
// SetAttr handles setting src since it needs to catch img.src =
|
||||
// img.src, so we only need to handle the unset case
|
||||
if (mResponsiveSelector) {
|
||||
if (mResponsiveSelector->Content() == this) {
|
||||
mResponsiveSelector->SetDefaultSource(nullptr);
|
||||
}
|
||||
QueueImageLoadTask();
|
||||
} else {
|
||||
// Bug 1076583 - We still behave synchronously in the non-responsive case
|
||||
CancelImageRequests(aNotify);
|
||||
} else if (mResponsiveSelector) {
|
||||
mResponsiveSelector->SetDefaultSource(attrVal.String());
|
||||
LoadSelectedImage(false, aNotify);
|
||||
}
|
||||
} else if (aName == nsGkAtoms::srcset &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
aNotify &&
|
||||
AsContent()->IsInDoc() &&
|
||||
IsSrcsetEnabled()) {
|
||||
// We currently don't handle responsive mode until BindToTree
|
||||
PictureSourceSrcsetChanged(thisContent, attrVal.String(), aNotify);
|
||||
PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
|
||||
} else if (aName == nsGkAtoms::sizes &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
thisContent->IsInDoc() &&
|
||||
HTMLPictureElement::IsPictureEnabled()) {
|
||||
PictureSourceSizesChanged(thisContent, attrVal.String(), aNotify);
|
||||
PictureSourceSizesChanged(this, attrVal.String(), aNotify);
|
||||
} else if (aName == nsGkAtoms::crossorigin &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
aNotify) {
|
||||
// We want aForce == true in this LoadImage call, because we want to force
|
||||
// a new load of the image with the new cross origin policy.
|
||||
nsCOMPtr<nsIURI> currentURI;
|
||||
if (NS_SUCCEEDED(GetCurrentURI(getter_AddRefs(currentURI))) && currentURI) {
|
||||
LoadImage(currentURI, true, aNotify);
|
||||
// Force a new load of the image with the new cross origin policy.
|
||||
if (mResponsiveSelector) {
|
||||
// per spec, full selection runs when this changes, even though
|
||||
// it doesn't directly affect the source selection
|
||||
QueueImageLoadTask();
|
||||
} else {
|
||||
// Bug 1076583 - We still use the older synchronous algorithm in
|
||||
// non-responsive mode. We want aForce == true in this LoadImage
|
||||
// call, because we want to force a new load of the image with
|
||||
// the new cross origin policy.
|
||||
nsCOMPtr<nsIURI> currentURI;
|
||||
if (NS_SUCCEEDED(GetCurrentURI(getter_AddRefs(currentURI))) && currentURI) {
|
||||
LoadImage(currentURI, true, aNotify);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,32 +511,45 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
// being set to its existing value, which is normally optimized away as a
|
||||
// no-op.
|
||||
//
|
||||
// If aNotify is false, we are coming from the parser or some such place;
|
||||
// we'll get bound after all the attributes have been set, so we'll do the
|
||||
// image load from BindToTree. Skip the LoadImage call in that case.
|
||||
// If we are in responsive mode, we drop the forced reload behavior,
|
||||
// but still trigger a image load task for img.src = img.src per
|
||||
// spec.
|
||||
//
|
||||
// If we are in responsive mode, we drop the forced reload behavior, and
|
||||
// handle updates in AfterSetAttr
|
||||
if (aNotify && !mResponsiveSelector &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
// Both cases handle unsetting src in AfterSetAttr
|
||||
if (aNameSpaceID == kNameSpaceID_None &&
|
||||
aName == nsGkAtoms::src) {
|
||||
|
||||
// Prevent setting image.src by exiting early
|
||||
// This is for dom.disable_image_src_set, which predates "srcset"
|
||||
// as an attribute. See Bug 773429
|
||||
if (nsContentUtils::IsImageSrcSetDisabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// A hack to get animations to reset. See bug 594771.
|
||||
mNewRequestsWillNeedAnimationReset = true;
|
||||
if (mResponsiveSelector || mPendingImageLoadTask) {
|
||||
if (mResponsiveSelector &&
|
||||
mResponsiveSelector->Content() == this) {
|
||||
mResponsiveSelector->SetDefaultSource(aValue);
|
||||
}
|
||||
QueueImageLoadTask();
|
||||
} else if (aNotify) {
|
||||
// If aNotify is false, we are coming from the parser or some such place;
|
||||
// we'll get bound after all the attributes have been set, so we'll do the
|
||||
// sync image load from BindToTree. Skip the LoadImage call in that case.
|
||||
|
||||
// Force image loading here, so that we'll try to load the image from
|
||||
// network if it's set to be not cacheable... If we change things so that
|
||||
// the state gets in Element's attr-setting happen around this
|
||||
// LoadImage call, we could start passing false instead of aNotify
|
||||
// here.
|
||||
LoadImage(aValue, true, aNotify);
|
||||
// Note that this sync behavior is partially removed from the spec, bug 1076583
|
||||
|
||||
mNewRequestsWillNeedAnimationReset = false;
|
||||
// A hack to get animations to reset. See bug 594771.
|
||||
mNewRequestsWillNeedAnimationReset = true;
|
||||
|
||||
// Force image loading here, so that we'll try to load the image from
|
||||
// network if it's set to be not cacheable... If we change things so that
|
||||
// the state gets in Element's attr-setting happen around this
|
||||
// LoadImage call, we could start passing false instead of aNotify
|
||||
// here.
|
||||
LoadImage(aValue, true, aNotify);
|
||||
|
||||
mNewRequestsWillNeedAnimationReset = false;
|
||||
}
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
|
||||
@ -526,27 +575,31 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
|
||||
bool addedToPicture = aParent && aParent->Tag() == nsGkAtoms::picture &&
|
||||
HTMLPictureElement::IsPictureEnabled();
|
||||
bool haveSrcset = IsSrcsetEnabled() &&
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::srcset);
|
||||
if (addedToPicture || haveSrcset ||
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
|
||||
if (addedToPicture) {
|
||||
QueueImageLoadTask();
|
||||
} else if (!mResponsiveSelector &&
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
|
||||
// We skip loading when our attributes were set from parser land,
|
||||
// so trigger a aForce=false load now to check if things changed.
|
||||
// This isn't necessary for responsive mode, since creating the
|
||||
// image load task is asynchronous we don't need to take special
|
||||
// care to avoid doing so when being filled by the parser.
|
||||
|
||||
// FIXME: Bug 660963 it would be nice if we could just have
|
||||
// ClearBrokenState update our state and do it fast...
|
||||
ClearBrokenState();
|
||||
RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
|
||||
|
||||
// We don't handle responsive changes when not bound to a tree, update them
|
||||
// now if necessary
|
||||
if (addedToPicture || haveSrcset) {
|
||||
MaybeUpdateResponsiveSelector();
|
||||
}
|
||||
// We still act synchronously for the non-responsive case (Bug
|
||||
// 1076583), but still need to delay if it is unsafe to run
|
||||
// script.
|
||||
|
||||
// If loading is temporarily disabled, don't even launch MaybeLoadImage.
|
||||
// Otherwise MaybeLoadImage may run later when someone has reenabled
|
||||
// loading.
|
||||
if (LoadingEnabled()) {
|
||||
nsContentUtils::AddScriptRunner(
|
||||
NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
|
||||
NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,7 +617,13 @@ HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
}
|
||||
}
|
||||
|
||||
mResponsiveSelector = nullptr;
|
||||
if (aNullParent &&
|
||||
nsINode::GetParentNode()->Tag() == nsGkAtoms::picture &&
|
||||
HTMLPictureElement::IsPictureEnabled()) {
|
||||
// Being removed from picture re-triggers selection, even if we
|
||||
// weren't using a <source> peer
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
|
||||
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
@ -606,9 +665,9 @@ HTMLImageElement::MaybeLoadImage()
|
||||
|
||||
// Note, check LoadingEnabled() after LoadImage call.
|
||||
|
||||
nsresult rv = LoadSelectedImage(false, true);
|
||||
LoadSelectedImage(false, true);
|
||||
|
||||
if (NS_FAILED(rv) || !LoadingEnabled()) {
|
||||
if (!LoadingEnabled()) {
|
||||
CancelImageRequests(true);
|
||||
}
|
||||
}
|
||||
@ -785,19 +844,41 @@ HTMLImageElement::ClearForm(bool aRemoveFromForm)
|
||||
mForm = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::QueueImageLoadTask()
|
||||
{
|
||||
// If loading is temporarily disabled, we don't want to queue tasks
|
||||
// that may then run when loading is re-enabled.
|
||||
if (!LoadingEnabled() || !this->OwnerDoc()->IsCurrentActiveDocument()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The task checks this to determine if it was the last queued event, so this
|
||||
// implicitly cancels earlier tasks
|
||||
mPendingImageLoadTask = new ImageLoadTask(this);
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
appShell->RunInStableState(mPendingImageLoadTask);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "expect appshell for HTMLImageElement");
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
||||
if (aForce) {
|
||||
// In responsive mode we generally want to re-run the full
|
||||
// selection algorithm whenever starting a new load, per
|
||||
// spec. This also causes us to re-resolve the URI as appropriate.
|
||||
UpdateResponsiveSource();
|
||||
}
|
||||
|
||||
if (mResponsiveSelector) {
|
||||
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
|
||||
if (url) {
|
||||
rv = LoadImage(url, aForce, aNotify);
|
||||
} else {
|
||||
CancelImageRequests(aNotify);
|
||||
rv = NS_OK;
|
||||
}
|
||||
rv = LoadImage(url, aForce, aNotify);
|
||||
} else {
|
||||
nsAutoString src;
|
||||
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
|
||||
@ -805,12 +886,12 @@ HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify)
|
||||
rv = NS_OK;
|
||||
} else {
|
||||
rv = LoadImage(src, aForce, aNotify);
|
||||
if (NS_FAILED(rv)) {
|
||||
CancelImageRequests(aNotify);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
CancelImageRequests(aNotify);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -819,31 +900,28 @@ HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
|
||||
const nsAString& aNewValue,
|
||||
bool aNotify)
|
||||
{
|
||||
if (aSourceNode != AsContent() && !HTMLPictureElement::IsPictureEnabled()) {
|
||||
// Don't consider <source> nodes if picture is pref'd off
|
||||
bool isSelf = aSourceNode == this;
|
||||
|
||||
if (!IsSrcsetEnabled() ||
|
||||
(!isSelf && !HTMLPictureElement::IsPictureEnabled())) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
|
||||
: nullptr;
|
||||
MOZ_ASSERT(isSelf || IsPreviousSibling(aSourceNode, this),
|
||||
"Should not be getting notifications for non-previous-siblings");
|
||||
|
||||
nsIContent *currentSrc =
|
||||
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
|
||||
|
||||
if (aSourceNode == currentSrc) {
|
||||
// We're currently using this node as our responsive selector source.
|
||||
// We're currently using this node as our responsive selector
|
||||
// source.
|
||||
mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
|
||||
// Search for a new source if we are no longer valid.
|
||||
MaybeUpdateResponsiveSelector(currentSrc);
|
||||
|
||||
LoadSelectedImage(false, aNotify);
|
||||
} else if (currentSrc && IsPreviousSibling(currentSrc, aSourceNode)) {
|
||||
// If we have a source and it is previous to the one being updated, ignore
|
||||
return;
|
||||
} else {
|
||||
// This is previous to our current source or we don't have a current source,
|
||||
// use it if valid.
|
||||
if (TryCreateResponsiveSelector(aSourceNode, &aNewValue, nullptr)) {
|
||||
LoadSelectedImage(false, aNotify);
|
||||
}
|
||||
}
|
||||
|
||||
// This always triggers the image update steps per the spec, even if
|
||||
// we are not using this source.
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
|
||||
void
|
||||
@ -852,109 +930,119 @@ HTMLImageElement::PictureSourceSizesChanged(nsIContent *aSourceNode,
|
||||
bool aNotify)
|
||||
{
|
||||
if (!HTMLPictureElement::IsPictureEnabled()) {
|
||||
// Don't consider sizes at all if picture support is disabled
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
|
||||
: nullptr;
|
||||
MOZ_ASSERT(aSourceNode == this ||
|
||||
IsPreviousSibling(aSourceNode, this),
|
||||
"Should not be getting notifications for non-previous-siblings");
|
||||
|
||||
nsIContent *currentSrc =
|
||||
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
|
||||
|
||||
if (aSourceNode == currentSrc) {
|
||||
// We're currently using this node as our responsive selector source.
|
||||
// We're currently using this node as our responsive selector
|
||||
// source.
|
||||
mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
|
||||
LoadSelectedImage(false, aNotify);
|
||||
}
|
||||
|
||||
// This always triggers the image update steps per the spec, even if
|
||||
// we are not using this source.
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::PictureSourceMediaChanged(nsIContent *aSourceNode,
|
||||
const nsAString& aNewValue,
|
||||
bool aNotify)
|
||||
{
|
||||
if (!HTMLPictureElement::IsPictureEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
|
||||
"Should not be getting notifications for non-previous-siblings");
|
||||
|
||||
// This always triggers the image update steps per the spec, even if
|
||||
// we are not switching to/from this source
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::PictureSourceAdded(nsIContent *aSourceNode)
|
||||
{
|
||||
// If the source node is previous to our current one, or ourselves if we have
|
||||
// no responsive source, try to use it as a responsive source.
|
||||
nsIContent *currentSrc = mResponsiveSelector ? mResponsiveSelector->Content()
|
||||
: AsContent();
|
||||
|
||||
if (HTMLPictureElement::IsPictureEnabled() &&
|
||||
IsPreviousSibling(aSourceNode, currentSrc) &&
|
||||
TryCreateResponsiveSelector(aSourceNode, nullptr, nullptr)) {
|
||||
LoadSelectedImage(false, true);
|
||||
if (!HTMLPictureElement::IsPictureEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::PictureSourceRemoved(nsIContent *aSourceNode)
|
||||
{
|
||||
// If this is our current source, we'll need to find another one or leave
|
||||
// responsive mode.
|
||||
if (mResponsiveSelector && mResponsiveSelector->Content() == aSourceNode) {
|
||||
MaybeUpdateResponsiveSelector(aSourceNode, true);
|
||||
LoadSelectedImage(false, true);
|
||||
if (!HTMLPictureElement::IsPictureEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::MaybeUpdateResponsiveSelector(nsIContent *aCurrentSource,
|
||||
bool aSourceRemoved)
|
||||
void
|
||||
HTMLImageElement::UpdateResponsiveSource()
|
||||
{
|
||||
nsIContent *thisContent = AsContent();
|
||||
|
||||
if (!aCurrentSource && mResponsiveSelector) {
|
||||
aCurrentSource = mResponsiveSelector->Content();
|
||||
}
|
||||
|
||||
// If we have a source with candidates, no update is needed unless it is being
|
||||
// removed
|
||||
if (aCurrentSource && !aSourceRemoved &&
|
||||
mResponsiveSelector->NumCandidates()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, invalidate
|
||||
bool hadSelector = !!mResponsiveSelector;
|
||||
mResponsiveSelector = nullptr;
|
||||
|
||||
if (!IsSrcsetEnabled()) {
|
||||
return hadSelector;
|
||||
mResponsiveSelector = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// See if there's another source node we could use.
|
||||
nsIContent *currentSource =
|
||||
mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
|
||||
bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
|
||||
nsIContent *nextSource = nullptr;
|
||||
if (pictureEnabled && aCurrentSource && aCurrentSource != thisContent) {
|
||||
// If current source is the <img> tag, there is no next candidate. Otherwise,
|
||||
// it's the next sibling of the current source.
|
||||
MOZ_ASSERT(IsPreviousSibling(aCurrentSource, thisContent) &&
|
||||
thisContent->GetParentNode()->Tag() == nsGkAtoms::picture);
|
||||
nextSource = aCurrentSource->GetNextSibling();
|
||||
} else if (!aCurrentSource) {
|
||||
// If no current source at all, start from the first possible source, which
|
||||
// is the first node of the <picture> element or ourselves if we're not a
|
||||
// picture
|
||||
nsINode *parent = pictureEnabled ? thisContent->GetParentNode() : nullptr;
|
||||
if (parent && parent->Tag() == nsGkAtoms::picture) {
|
||||
nextSource = parent->GetFirstChild();
|
||||
} else {
|
||||
nextSource = thisContent;
|
||||
}
|
||||
nsINode *parent = pictureEnabled ? this->nsINode::GetParentNode() : nullptr;
|
||||
|
||||
nsINode *candidateSource = nullptr;
|
||||
if (parent && parent->Tag() == nsGkAtoms::picture) {
|
||||
// Walk source nodes previous to ourselves
|
||||
candidateSource = parent->GetFirstChild();
|
||||
} else {
|
||||
candidateSource = this;
|
||||
}
|
||||
|
||||
while (nextSource) {
|
||||
if (nextSource == thisContent) {
|
||||
// We are the last possible source, so stop searching if we match or
|
||||
// not
|
||||
TryCreateResponsiveSelector(nextSource);
|
||||
while (candidateSource) {
|
||||
if (candidateSource == currentSource) {
|
||||
// found no better source before current, re-run selection on
|
||||
// that and keep it if it's still usable.
|
||||
mResponsiveSelector->SelectImage(true);
|
||||
if (mResponsiveSelector->NumCandidates()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// no longer valid
|
||||
mResponsiveSelector = nullptr;
|
||||
if (candidateSource == this) {
|
||||
// No further possibilities
|
||||
break;
|
||||
}
|
||||
} else if (candidateSource == this) {
|
||||
// We are the last possible source
|
||||
if (!TryCreateResponsiveSelector(candidateSource->AsContent())) {
|
||||
// Failed to find any source
|
||||
mResponsiveSelector = nullptr;
|
||||
}
|
||||
break;
|
||||
} else if (nextSource->Tag() == nsGkAtoms::source &&
|
||||
TryCreateResponsiveSelector(nextSource)) {
|
||||
// If this led to a valid source, stop
|
||||
} else if (candidateSource->Tag() == nsGkAtoms::source &&
|
||||
TryCreateResponsiveSelector(candidateSource->AsContent())) {
|
||||
// This led to a valid source, stop
|
||||
break;
|
||||
}
|
||||
|
||||
nextSource = nextSource->GetNextSibling();
|
||||
candidateSource = candidateSource->GetNextSibling();
|
||||
}
|
||||
|
||||
// State changed unless we didn't make a selector and didn't start with one
|
||||
return mResponsiveSelector || hadSelector;
|
||||
if (!candidateSource) {
|
||||
// Ran out of siblings without finding ourself, e.g. XBL magic.
|
||||
mResponsiveSelector = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@ -972,7 +1060,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
|
||||
if (isSourceTag) {
|
||||
DebugOnly<nsINode *> parent(nsINode::GetParentNode());
|
||||
MOZ_ASSERT(parent && parent->Tag() == nsGkAtoms::picture);
|
||||
MOZ_ASSERT(IsPreviousSibling(aSourceNode, AsContent()));
|
||||
MOZ_ASSERT(IsPreviousSibling(aSourceNode, this));
|
||||
MOZ_ASSERT(pictureEnabled);
|
||||
|
||||
HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
|
||||
@ -981,7 +1069,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
|
||||
}
|
||||
} else if (aSourceNode->Tag() == nsGkAtoms::img) {
|
||||
// Otherwise this is the <img> tag itself
|
||||
MOZ_ASSERT(aSourceNode == AsContent());
|
||||
MOZ_ASSERT(aSourceNode == this);
|
||||
}
|
||||
|
||||
// Skip if has no srcset or an empty srcset
|
||||
@ -999,7 +1087,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
|
||||
|
||||
|
||||
// Try to parse
|
||||
nsRefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(this);
|
||||
nsRefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(aSourceNode);
|
||||
if (!sel->SetCandidatesFromSourceSet(srcset)) {
|
||||
// No possible candidates, don't need to bother parsing sizes
|
||||
return false;
|
||||
@ -1015,7 +1103,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
|
||||
|
||||
// If this is the <img> tag, also pull in src as the default source
|
||||
if (!isSourceTag) {
|
||||
MOZ_ASSERT(aSourceNode == AsContent());
|
||||
MOZ_ASSERT(aSourceNode == this);
|
||||
nsAutoString src;
|
||||
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && !src.IsEmpty()) {
|
||||
sel->SetDefaultSource(src);
|
||||
|
@ -26,6 +26,8 @@ class HTMLImageElement MOZ_FINAL : public nsGenericHTMLElement,
|
||||
public nsIDOMHTMLImageElement
|
||||
{
|
||||
friend class HTMLSourceElement;
|
||||
friend class HTMLPictureElement;
|
||||
friend class ImageLoadTask;
|
||||
public:
|
||||
explicit HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
|
||||
|
||||
@ -43,6 +45,8 @@ public:
|
||||
// nsIDOMHTMLImageElement
|
||||
NS_DECL_NSIDOMHTMLIMAGEELEMENT
|
||||
|
||||
NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLImageElement, img)
|
||||
|
||||
// override from nsImageLoadingContent
|
||||
CORSMode GetCORSMode();
|
||||
|
||||
@ -197,6 +201,9 @@ public:
|
||||
protected:
|
||||
virtual ~HTMLImageElement();
|
||||
|
||||
// Queues a task to run LoadSelectedImage pending stable state
|
||||
void QueueImageLoadTask();
|
||||
|
||||
// Resolve and load the current mResponsiveSelector (responsive mode) or src
|
||||
// attr image.
|
||||
nsresult LoadSelectedImage(bool aForce, bool aNotify);
|
||||
@ -206,13 +213,25 @@ protected:
|
||||
const nsAString& aNewValue, bool aNotify);
|
||||
void PictureSourceSizesChanged(nsIContent *aSourceNode,
|
||||
const nsAString& aNewValue, bool aNotify);
|
||||
void PictureSourceMediaChanged(nsIContent *aSourceNode,
|
||||
const nsAString& aNewValue, bool aNotify);
|
||||
|
||||
void PictureSourceAdded(nsIContent *aSourceNode);
|
||||
// This should be called prior to the unbind, such that nextsibling works
|
||||
void PictureSourceRemoved(nsIContent *aSourceNode);
|
||||
|
||||
bool MaybeUpdateResponsiveSelector(nsIContent *aCurrentSource = nullptr,
|
||||
bool aSourceRemoved = false);
|
||||
// Re-evaluates all source nodes (picture <source>,<img>) and finds
|
||||
// the best source set for mResponsiveSelector. If a better source
|
||||
// is found, creates a new selector and feeds the source to it. If
|
||||
// the current ResponsiveSelector is not changed, runs
|
||||
// SelectImage(true) to re-evaluate its candidates.
|
||||
//
|
||||
// Because keeping the existing selector is the common case (and we
|
||||
// often do no-op reselections), this does not re-parse values for
|
||||
// the existing mResponsiveSelector, meaning you need to update its
|
||||
// parameters as appropriate before calling (or null it out to force
|
||||
// recreation)
|
||||
void UpdateResponsiveSource();
|
||||
|
||||
// Given a <source> node that is a previous sibling *or* ourselves, try to
|
||||
// create a ResponsiveSelector.
|
||||
@ -247,6 +266,8 @@ protected:
|
||||
private:
|
||||
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
nsRuleData* aData);
|
||||
|
||||
nsCOMPtr<nsIRunnable> mPendingImageLoadTask;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -40,6 +40,28 @@ NS_IMPL_ISUPPORTS_INHERITED(HTMLPictureElement, nsGenericHTMLElement,
|
||||
|
||||
NS_IMPL_ELEMENT_CLONE(HTMLPictureElement)
|
||||
|
||||
void
|
||||
HTMLPictureElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
|
||||
{
|
||||
// Find all img siblings after this <source> to notify them of its demise
|
||||
nsCOMPtr<nsINode> child = GetChildAt(aIndex);
|
||||
nsCOMPtr<nsIContent> nextSibling;
|
||||
if (child && child->Tag() == nsGkAtoms::source) {
|
||||
nextSibling = child->GetNextSibling();
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify);
|
||||
|
||||
if (nextSibling && nextSibling->GetParentNode() == this) {
|
||||
do {
|
||||
HTMLImageElement* img = HTMLImageElement::FromContent(nextSibling);
|
||||
if (img) {
|
||||
img->PictureSourceRemoved(child->AsContent());
|
||||
}
|
||||
} while ( (nextSibling = nextSibling->GetNextSibling()) );
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLPictureElement::IsPictureEnabled()
|
||||
{
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
NS_DECL_NSIDOMHTMLPICTUREELEMENT
|
||||
|
||||
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const MOZ_OVERRIDE;
|
||||
virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) MOZ_OVERRIDE;
|
||||
|
||||
static bool IsPictureEnabled();
|
||||
|
||||
|
@ -63,9 +63,10 @@ HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
// responsive parameter changes
|
||||
nsINode *parent = nsINode::GetParentNode();
|
||||
if (aNameSpaceID == kNameSpaceID_None &&
|
||||
(aName == nsGkAtoms::srcset || aName == nsGkAtoms::sizes) &&
|
||||
parent && parent->Tag() == nsGkAtoms::picture && MatchesCurrentMedia()) {
|
||||
|
||||
(aName == nsGkAtoms::srcset ||
|
||||
aName == nsGkAtoms::sizes ||
|
||||
aName == nsGkAtoms::media) &&
|
||||
parent && parent->Tag() == nsGkAtoms::picture) {
|
||||
nsString strVal = aValue ? aValue->GetStringValue() : EmptyString();
|
||||
// Find all img siblings after this <source> and notify them of the change
|
||||
nsCOMPtr<nsINode> sibling = AsContent();
|
||||
@ -76,6 +77,8 @@ HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
img->PictureSourceSrcsetChanged(AsContent(), strVal, aNotify);
|
||||
} else if (aName == nsGkAtoms::sizes) {
|
||||
img->PictureSourceSizesChanged(AsContent(), strVal, aNotify);
|
||||
} else if (aName == nsGkAtoms::media) {
|
||||
img->PictureSourceMediaChanged(AsContent(), strVal, aNotify);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,24 +140,6 @@ HTMLSourceElement::BindToTree(nsIDocument *aDocument,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLSourceElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
nsINode *parent = nsINode::GetParentNode();
|
||||
if (parent && parent->Tag() == nsGkAtoms::picture) {
|
||||
// Find all img siblings after this <source> and notify them of our demise
|
||||
nsCOMPtr<nsINode> sibling = AsContent();
|
||||
while ( (sibling = sibling->GetNextSibling()) ) {
|
||||
if (sibling->Tag() == nsGkAtoms::img) {
|
||||
HTMLImageElement *img = static_cast<HTMLImageElement*>(sibling.get());
|
||||
img->PictureSourceRemoved(AsContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
HTMLSourceElement::WrapNode(JSContext* aCx)
|
||||
{
|
||||
|
@ -39,7 +39,6 @@ public:
|
||||
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers) MOZ_OVERRIDE;
|
||||
virtual void UnbindFromTree(bool aDeep, bool aNullParent) MOZ_OVERRIDE;
|
||||
|
||||
// If this element's media attr matches for its owner document. Returns true
|
||||
// if no media attr was set.
|
||||
|
Loading…
Reference in New Issue
Block a user