1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_HTMLImageElement_h
8 #define mozilla_dom_HTMLImageElement_h
10 #include "mozilla/Attributes.h"
11 #include "nsGenericHTMLElement.h"
12 #include "nsImageLoadingContent.h"
14 #include "nsCycleCollectionParticipant.h"
17 class EventChainPreVisitor
;
22 class ResponsiveImageSelector
;
23 class HTMLImageElement final
: public nsGenericHTMLElement
,
24 public nsImageLoadingContent
{
25 friend class HTMLSourceElement
;
26 friend class HTMLPictureElement
;
27 friend class ImageLoadTask
;
30 explicit HTMLImageElement(
31 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
);
33 static already_AddRefed
<HTMLImageElement
> Image(
34 const GlobalObject
& aGlobal
, const Optional
<uint32_t>& aWidth
,
35 const Optional
<uint32_t>& aHeight
, ErrorResult
& aError
);
37 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLImageElement
,
41 NS_DECL_ISUPPORTS_INHERITED
43 bool Draggable() const override
;
45 ResponsiveImageSelector
* GetResponsiveImageSelector() {
46 return mResponsiveSelector
.get();
50 bool IsInteractiveHTMLContent() const override
;
53 void AsyncEventRunning(AsyncEventDispatcher
* aEvent
) override
;
55 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLImageElement
, img
)
57 // override from nsImageLoadingContent
58 CORSMode
GetCORSMode() override
;
61 bool ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
62 const nsAString
& aValue
,
63 nsIPrincipal
* aMaybeScriptedPrincipal
,
64 nsAttrValue
& aResult
) override
;
65 nsChangeHint
GetAttributeChangeHint(const nsAtom
* aAttribute
,
66 int32_t aModType
) const override
;
67 NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom
* aAttribute
) const override
;
68 nsMapRuleToAttributesFunc
GetAttributeMappingFunction() const override
;
70 void GetEventTargetParent(EventChainPreVisitor
& aVisitor
) override
;
71 nsINode
* GetScopeChainParent() const override
;
73 bool IsHTMLFocusable(bool aWithMouse
, bool* aIsFocusable
,
74 int32_t* aTabIndex
) override
;
76 nsresult
BindToTree(BindContext
&, nsINode
& aParent
) override
;
77 void UnbindFromTree(UnbindContext
&) override
;
79 nsresult
Clone(dom::NodeInfo
*, nsINode
** aResult
) const override
;
81 void NodeInfoChanged(Document
* aOldDoc
) override
;
83 nsresult
CopyInnerTo(HTMLImageElement
* aDest
);
85 void MaybeLoadImage(bool aAlwaysForceLoad
);
87 bool IsMap() { return GetBoolAttr(nsGkAtoms::ismap
); }
88 void SetIsMap(bool aIsMap
, ErrorResult
& aError
) {
89 SetHTMLBoolAttr(nsGkAtoms::ismap
, aIsMap
, aError
);
91 MOZ_CAN_RUN_SCRIPT
uint32_t Width();
92 void SetWidth(uint32_t aWidth
, ErrorResult
& aError
) {
93 SetUnsignedIntAttr(nsGkAtoms::width
, aWidth
, 0, aError
);
95 MOZ_CAN_RUN_SCRIPT
uint32_t Height();
96 void SetHeight(uint32_t aHeight
, ErrorResult
& aError
) {
97 SetUnsignedIntAttr(nsGkAtoms::height
, aHeight
, 0, aError
);
100 nsIntSize
NaturalSize();
101 uint32_t NaturalHeight() { return NaturalSize().height
; }
102 uint32_t NaturalWidth() { return NaturalSize().width
; }
106 return GetDimensionAttrAsUnsignedInt(nsGkAtoms::hspace
, 0);
108 void SetHspace(uint32_t aHspace
, ErrorResult
& aError
) {
109 SetUnsignedIntAttr(nsGkAtoms::hspace
, aHspace
, 0, aError
);
112 return GetDimensionAttrAsUnsignedInt(nsGkAtoms::vspace
, 0);
114 void SetVspace(uint32_t aVspace
, ErrorResult
& aError
) {
115 SetUnsignedIntAttr(nsGkAtoms::vspace
, aVspace
, 0, aError
);
118 void GetAlt(nsAString
& aAlt
) { GetHTMLAttr(nsGkAtoms::alt
, aAlt
); }
119 void SetAlt(const nsAString
& aAlt
, ErrorResult
& aError
) {
120 SetHTMLAttr(nsGkAtoms::alt
, aAlt
, aError
);
122 void GetSrc(nsAString
& aSrc
) { GetURIAttr(nsGkAtoms::src
, nullptr, aSrc
); }
123 void SetSrc(const nsAString
& aSrc
, ErrorResult
& aError
) {
124 SetHTMLAttr(nsGkAtoms::src
, aSrc
, aError
);
126 void SetSrc(const nsAString
& aSrc
, nsIPrincipal
* aTriggeringPrincipal
,
127 ErrorResult
& aError
) {
128 SetHTMLAttr(nsGkAtoms::src
, aSrc
, aTriggeringPrincipal
, aError
);
130 void GetSrcset(nsAString
& aSrcset
) {
131 GetHTMLAttr(nsGkAtoms::srcset
, aSrcset
);
133 void SetSrcset(const nsAString
& aSrcset
, nsIPrincipal
* aTriggeringPrincipal
,
134 ErrorResult
& aError
) {
135 SetHTMLAttr(nsGkAtoms::srcset
, aSrcset
, aTriggeringPrincipal
, aError
);
137 void GetCrossOrigin(nsAString
& aResult
) {
138 // Null for both missing and invalid defaults is ok, since we
139 // always parse to an enum value, so we don't need an invalid
140 // default, and we _want_ the missing default to be null.
141 GetEnumAttr(nsGkAtoms::crossorigin
, nullptr, aResult
);
143 void SetCrossOrigin(const nsAString
& aCrossOrigin
, ErrorResult
& aError
) {
144 SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin
, aCrossOrigin
, aError
);
146 void GetUseMap(nsAString
& aUseMap
) {
147 GetHTMLAttr(nsGkAtoms::usemap
, aUseMap
);
149 void SetUseMap(const nsAString
& aUseMap
, ErrorResult
& aError
) {
150 SetHTMLAttr(nsGkAtoms::usemap
, aUseMap
, aError
);
152 void GetName(nsAString
& aName
) { GetHTMLAttr(nsGkAtoms::name
, aName
); }
153 void SetName(const nsAString
& aName
, ErrorResult
& aError
) {
154 SetHTMLAttr(nsGkAtoms::name
, aName
, aError
);
156 void GetAlign(nsAString
& aAlign
) { GetHTMLAttr(nsGkAtoms::align
, aAlign
); }
157 void SetAlign(const nsAString
& aAlign
, ErrorResult
& aError
) {
158 SetHTMLAttr(nsGkAtoms::align
, aAlign
, aError
);
160 void GetLongDesc(nsAString
& aLongDesc
) {
161 GetURIAttr(nsGkAtoms::longdesc
, nullptr, aLongDesc
);
163 void SetLongDesc(const nsAString
& aLongDesc
, ErrorResult
& aError
) {
164 SetHTMLAttr(nsGkAtoms::longdesc
, aLongDesc
, aError
);
166 void GetSizes(nsAString
& aSizes
) { GetHTMLAttr(nsGkAtoms::sizes
, aSizes
); }
167 void SetSizes(const nsAString
& aSizes
, ErrorResult
& aError
) {
168 SetHTMLAttr(nsGkAtoms::sizes
, aSizes
, aError
);
170 void GetCurrentSrc(nsAString
& aValue
);
171 void GetBorder(nsAString
& aBorder
) {
172 GetHTMLAttr(nsGkAtoms::border
, aBorder
);
174 void SetBorder(const nsAString
& aBorder
, ErrorResult
& aError
) {
175 SetHTMLAttr(nsGkAtoms::border
, aBorder
, aError
);
177 void SetReferrerPolicy(const nsAString
& aReferrer
, ErrorResult
& aError
) {
178 SetHTMLAttr(nsGkAtoms::referrerpolicy
, aReferrer
, aError
);
180 void GetReferrerPolicy(nsAString
& aReferrer
) {
181 GetEnumAttr(nsGkAtoms::referrerpolicy
, "", aReferrer
);
183 void SetDecoding(const nsAString
& aDecoding
, ErrorResult
& aError
) {
184 SetHTMLAttr(nsGkAtoms::decoding
, aDecoding
, aError
);
186 void GetDecoding(nsAString
& aValue
);
188 void SetLoading(const nsAString
& aLoading
, ErrorResult
& aError
) {
189 SetHTMLAttr(nsGkAtoms::loading
, aLoading
, aError
);
192 bool IsAwaitingLoadOrLazyLoading() const {
193 return mLazyLoading
|| mPendingImageLoadTask
;
196 bool IsLazyLoading() const { return mLazyLoading
; }
198 already_AddRefed
<Promise
> Decode(ErrorResult
& aRv
);
200 MOZ_CAN_RUN_SCRIPT
int32_t X();
201 MOZ_CAN_RUN_SCRIPT
int32_t Y();
202 void GetLowsrc(nsAString
& aLowsrc
) {
203 GetURIAttr(nsGkAtoms::lowsrc
, nullptr, aLowsrc
);
205 void SetLowsrc(const nsAString
& aLowsrc
, ErrorResult
& aError
) {
206 SetHTMLAttr(nsGkAtoms::lowsrc
, aLowsrc
, aError
);
210 HTMLFormElement
* GetForm() const;
212 void SetForm(HTMLFormElement
* aForm
);
213 void ClearForm(bool aRemoveFromForm
);
215 void DestroyContent() override
;
217 void MediaFeatureValuesChanged();
220 * Given a hypothetical <img> or <source> tag with the given parameters,
221 * return what URI we would attempt to use, if any. Used by the preloader to
222 * resolve sources prior to DOM creation.
224 * @param aDocument The document this image would be for, for referencing
225 * viewport width and DPI/zoom
226 * @param aIsSourceTag If these parameters are for a <source> tag (as in a
227 * <picture>) rather than an <img> tag. Note that some attrs are unused
228 * when this is true an vice versa
229 * @param aSrcAttr [ignored if aIsSourceTag] The src attr for this image.
230 * @param aSrcsetAttr The srcset attr for this image/source
231 * @param aSizesAttr The sizes attr for this image/source
232 * @param aTypeAttr [ignored if !aIsSourceTag] The type attr for this source.
233 * Should be a void string to differentiate no type attribute
235 * @param aMediaAttr [ignored if !aIsSourceTag] The media attr for this
236 * source. Should be a void string to differentiate no
237 * media attribute from an empty one.
238 * @param aResult A reference to store the resulting URL spec in if we
239 * selected a source. This value is not guaranteed to parse to
240 * a valid URL, merely the URL that the tag would attempt to
241 * resolve and load (which may be the empty string). This
242 * parameter is not modified if return value is false.
243 * @return True if we were able to select a final source, false if further
244 * sources would be considered. It follows that this always returns
245 * true if !aIsSourceTag.
247 * Note that the return value may be true with an empty string as the result,
248 * which implies that the parameters provided describe a tag that would select
249 * no source. This is distinct from a return of false which implies that
250 * further <source> or <img> tags would be considered.
252 static bool SelectSourceForTagWithAttrs(
253 Document
* aDocument
, bool aIsSourceTag
, const nsAString
& aSrcAttr
,
254 const nsAString
& aSrcsetAttr
, const nsAString
& aSizesAttr
,
255 const nsAString
& aTypeAttr
, const nsAString
& aMediaAttr
,
258 enum class FromIntersectionObserver
: bool { No
, Yes
};
259 enum class StartLoading
: bool { No
, Yes
};
260 void StopLazyLoading(StartLoading
);
262 // This is used when restyling, for retrieving the extra style from the source
264 const StyleLockedDeclarationBlock
* GetMappedAttributesFromSource() const;
267 virtual ~HTMLImageElement();
269 // Update the responsive source synchronously and queues a task to run
270 // LoadSelectedImage pending stable state.
272 // Pending Bug 1076583 this is only used by the responsive image
273 // algorithm (InResponsiveMode()) -- synchronous actions when just
274 // using img.src will bypass this, and update source and kick off
275 // image load synchronously.
276 void UpdateSourceSyncAndQueueImageTask(
277 bool aAlwaysLoad
, const HTMLSourceElement
* aSkippedSource
= nullptr);
279 // True if we have a srcset attribute or a <picture> parent, regardless of if
280 // any valid responsive sources were parsed from either.
281 bool HaveSrcsetOrInPicture();
283 // True if we are using the newer image loading algorithm. This will be the
284 // only mode after Bug 1076583
285 bool InResponsiveMode();
287 // True if the given URL equals the last URL that was loaded by this element.
288 bool SelectedSourceMatchesLast(nsIURI
* aSelectedSource
);
290 // Load the current mResponsiveSelector (responsive mode) or src attr image.
291 // Note: This doesn't run the full selection for the responsive selector.
292 nsresult
LoadSelectedImage(bool aForce
, bool aNotify
, bool aAlwaysLoad
);
294 // True if this string represents a type we would support on <source type>
295 static bool SupportedPictureSourceType(const nsAString
& aType
);
297 // Update/create/destroy mResponsiveSelector
298 void PictureSourceSrcsetChanged(nsIContent
* aSourceNode
,
299 const nsAString
& aNewValue
, bool aNotify
);
300 void PictureSourceSizesChanged(nsIContent
* aSourceNode
,
301 const nsAString
& aNewValue
, bool aNotify
);
302 // As we re-run the source selection on these mutations regardless,
303 // we don't actually care which changed or to what
304 void PictureSourceMediaOrTypeChanged(nsIContent
* aSourceNode
, bool aNotify
);
306 // This is called when we update "width" or "height" attribute of source
308 void PictureSourceDimensionChanged(HTMLSourceElement
* aSourceNode
,
311 void PictureSourceAdded(HTMLSourceElement
* aSourceNode
= nullptr);
312 // This should be called prior to the unbind, such that nextsibling works
313 void PictureSourceRemoved(HTMLSourceElement
* aSourceNode
= nullptr);
315 // Re-evaluates all source nodes (picture <source>,<img>) and finds
316 // the best source set for mResponsiveSelector. If a better source
317 // is found, creates a new selector and feeds the source to it. If
318 // the current ResponsiveSelector is not changed, runs
319 // SelectImage(true) to re-evaluate its candidates.
321 // Because keeping the existing selector is the common case (and we
322 // often do no-op reselections), this does not re-parse values for
323 // the existing mResponsiveSelector, meaning you need to update its
324 // parameters as appropriate before calling (or null it out to force
327 // if |aSkippedSource| is non-null, we will skip it when running the
328 // algorithm. This is used when we need to update the source when we are
329 // removing the source element.
331 // Returns true if the source has changed, and false otherwise.
332 bool UpdateResponsiveSource(
333 const HTMLSourceElement
* aSkippedSource
= nullptr);
335 // Given a <source> node that is a previous sibling *or* ourselves, try to
336 // create a ResponsiveSelector.
338 // If the node's srcset/sizes make for an invalid selector, returns
339 // nullptr. This does not guarantee the resulting selector matches an image,
340 // only that it is valid.
341 already_AddRefed
<ResponsiveImageSelector
> TryCreateResponsiveSelector(
342 Element
* aSourceElement
);
344 MOZ_CAN_RUN_SCRIPT CSSIntPoint
GetXY();
345 JSObject
* WrapNode(JSContext
*, JS::Handle
<JSObject
*> aGivenProto
) override
;
346 void UpdateFormOwner();
348 void BeforeSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
349 const nsAttrValue
* aValue
, bool aNotify
) override
;
351 void AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
352 const nsAttrValue
* aValue
, const nsAttrValue
* aOldValue
,
353 nsIPrincipal
* aMaybeScriptedPrincipal
,
354 bool aNotify
) override
;
355 void OnAttrSetButNotChanged(int32_t aNamespaceID
, nsAtom
* aName
,
356 const nsAttrValueOrString
& aValue
,
357 bool aNotify
) override
;
359 // Override for nsImageLoadingContent.
360 nsIContent
* AsContent() override
{ return this; }
362 // Created when we're tracking responsive image state
363 RefPtr
<ResponsiveImageSelector
> mResponsiveSelector
;
365 // This is a weak reference that this element and the HTMLFormElement
366 // cooperate in maintaining.
367 HTMLFormElement
* mForm
= nullptr;
370 bool SourceElementMatches(Element
* aSourceElement
);
372 static void MapAttributesIntoRule(MappedDeclarationsBuilder
&);
374 * This function is called by AfterSetAttr and OnAttrSetButNotChanged.
375 * It will not be called if the value is being unset.
377 * @param aNamespaceID the namespace of the attr being set
378 * @param aName the localname of the attribute being set
379 * @param aValue the value it's being set to represented as either a string or
380 * a parsed nsAttrValue.
381 * @param aOldValue the value previously set. Will be null if no value was
382 * previously set. This value should only be used when
383 * aValueMaybeChanged is true; when aValueMaybeChanged is false,
384 * aOldValue should be considered unreliable.
385 * @param aNotify Whether we plan to notify document observers.
387 void AfterMaybeChangeAttr(int32_t aNamespaceID
, nsAtom
* aName
,
388 const nsAttrValueOrString
& aValue
,
389 const nsAttrValue
* aOldValue
,
390 nsIPrincipal
* aMaybeScriptedPrincipal
,
393 bool ShouldLoadImage() const;
395 // Set this image as a lazy load image due to loading="lazy".
396 void SetLazyLoading();
398 void StartLoadingIfNeeded();
400 bool IsInPicture() const {
401 return GetParentElement() &&
402 GetParentElement()->IsHTMLElement(nsGkAtoms::picture
);
405 void InvalidateAttributeMapping();
407 void SetResponsiveSelector(RefPtr
<ResponsiveImageSelector
>&& aSource
);
408 void SetDensity(double aDensity
);
410 // Queue an image load task (via microtask).
411 void QueueImageLoadTask(bool aAlwaysLoad
);
413 RefPtr
<ImageLoadTask
> mPendingImageLoadTask
;
414 nsCOMPtr
<nsIURI
> mSrcURI
;
415 nsCOMPtr
<nsIPrincipal
> mSrcTriggeringPrincipal
;
416 nsCOMPtr
<nsIPrincipal
> mSrcsetTriggeringPrincipal
;
418 // Last URL that was attempted to load by this element.
419 nsCOMPtr
<nsIURI
> mLastSelectedSource
;
420 // Last pixel density that was selected.
421 double mCurrentDensity
= 1.0;
422 bool mInDocResponsiveContent
= false;
426 } // namespace mozilla
428 #endif /* mozilla_dom_HTMLImageElement_h */