HTMLImageElement.cpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/dom/HTMLImageElement.h"
  6. #include "mozilla/dom/HTMLImageElementBinding.h"
  7. #include "nsGkAtoms.h"
  8. #include "nsStyleConsts.h"
  9. #include "nsPresContext.h"
  10. #include "nsMappedAttributes.h"
  11. #include "nsSize.h"
  12. #include "nsDocument.h"
  13. #include "nsIDocument.h"
  14. #include "nsIDOMMutationEvent.h"
  15. #include "nsIScriptContext.h"
  16. #include "nsIURL.h"
  17. #include "nsIIOService.h"
  18. #include "nsIServiceManager.h"
  19. #include "nsContentUtils.h"
  20. #include "nsContainerFrame.h"
  21. #include "nsNodeInfoManager.h"
  22. #include "mozilla/MouseEvents.h"
  23. #include "nsContentPolicyUtils.h"
  24. #include "nsIDOMWindow.h"
  25. #include "nsFocusManager.h"
  26. #include "mozilla/dom/HTMLFormElement.h"
  27. #include "nsAttrValueOrString.h"
  28. #include "imgLoader.h"
  29. #include "Image.h"
  30. // Responsive images!
  31. #include "mozilla/dom/HTMLSourceElement.h"
  32. #include "mozilla/dom/ResponsiveImageSelector.h"
  33. #include "imgIContainer.h"
  34. #include "imgILoader.h"
  35. #include "imgINotificationObserver.h"
  36. #include "imgRequestProxy.h"
  37. #include "nsILoadGroup.h"
  38. #include "nsRuleData.h"
  39. #include "nsIDOMHTMLMapElement.h"
  40. #include "mozilla/EventDispatcher.h"
  41. #include "mozilla/EventStates.h"
  42. #include "mozilla/net/ReferrerPolicy.h"
  43. #include "nsLayoutUtils.h"
  44. using namespace mozilla::net;
  45. NS_IMPL_NS_NEW_HTML_ELEMENT(Image)
  46. #ifdef DEBUG
  47. // Is aSubject a previous sibling of aNode.
  48. static bool IsPreviousSibling(nsINode *aSubject, nsINode *aNode)
  49. {
  50. if (aSubject == aNode) {
  51. return false;
  52. }
  53. nsINode *parent = aSubject->GetParentNode();
  54. if (parent && parent == aNode->GetParentNode()) {
  55. return parent->IndexOf(aSubject) < parent->IndexOf(aNode);
  56. }
  57. return false;
  58. }
  59. #endif
  60. namespace mozilla {
  61. namespace dom {
  62. // Calls LoadSelectedImage on host element unless it has been superseded or
  63. // canceled -- this is the synchronous section of "update the image data".
  64. // https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-image-data
  65. class ImageLoadTask : public Runnable
  66. {
  67. public:
  68. ImageLoadTask(HTMLImageElement *aElement, bool aAlwaysLoad)
  69. : mElement(aElement)
  70. , mAlwaysLoad(aAlwaysLoad)
  71. {
  72. mDocument = aElement->OwnerDoc();
  73. mDocument->BlockOnload();
  74. }
  75. NS_IMETHOD Run() override
  76. {
  77. if (mElement->mPendingImageLoadTask == this) {
  78. mElement->mPendingImageLoadTask = nullptr;
  79. mElement->LoadSelectedImage(true, true, mAlwaysLoad);
  80. }
  81. mDocument->UnblockOnload(false);
  82. return NS_OK;
  83. }
  84. bool AlwaysLoad() {
  85. return mAlwaysLoad;
  86. }
  87. private:
  88. ~ImageLoadTask() {}
  89. RefPtr<HTMLImageElement> mElement;
  90. nsCOMPtr<nsIDocument> mDocument;
  91. bool mAlwaysLoad;
  92. };
  93. HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
  94. : nsGenericHTMLElement(aNodeInfo)
  95. , mForm(nullptr)
  96. , mForceReload(false)
  97. , mInDocResponsiveContent(false)
  98. , mCurrentDensity(1.0)
  99. {
  100. // We start out broken
  101. AddStatesSilently(NS_EVENT_STATE_BROKEN);
  102. }
  103. HTMLImageElement::~HTMLImageElement()
  104. {
  105. DestroyImageLoadingContent();
  106. }
  107. NS_IMPL_ADDREF_INHERITED(HTMLImageElement, Element)
  108. NS_IMPL_RELEASE_INHERITED(HTMLImageElement, Element)
  109. NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLImageElement,
  110. nsGenericHTMLElement,
  111. mResponsiveSelector)
  112. // QueryInterface implementation for HTMLImageElement
  113. NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLImageElement)
  114. NS_INTERFACE_TABLE_INHERITED(HTMLImageElement,
  115. nsIDOMHTMLImageElement,
  116. nsIImageLoadingContent,
  117. imgIOnloadBlocker,
  118. imgINotificationObserver)
  119. NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
  120. NS_IMPL_ELEMENT_CLONE(HTMLImageElement)
  121. NS_IMPL_STRING_ATTR(HTMLImageElement, Name, name)
  122. NS_IMPL_STRING_ATTR(HTMLImageElement, Align, align)
  123. NS_IMPL_STRING_ATTR(HTMLImageElement, Alt, alt)
  124. NS_IMPL_STRING_ATTR(HTMLImageElement, Border, border)
  125. NS_IMPL_INT_ATTR(HTMLImageElement, Hspace, hspace)
  126. NS_IMPL_BOOL_ATTR(HTMLImageElement, IsMap, ismap)
  127. NS_IMPL_URI_ATTR(HTMLImageElement, LongDesc, longdesc)
  128. NS_IMPL_STRING_ATTR(HTMLImageElement, Sizes, sizes)
  129. NS_IMPL_URI_ATTR(HTMLImageElement, Lowsrc, lowsrc)
  130. NS_IMPL_URI_ATTR(HTMLImageElement, Src, src)
  131. NS_IMPL_STRING_ATTR(HTMLImageElement, Srcset, srcset)
  132. NS_IMPL_STRING_ATTR(HTMLImageElement, UseMap, usemap)
  133. NS_IMPL_INT_ATTR(HTMLImageElement, Vspace, vspace)
  134. bool
  135. HTMLImageElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
  136. {
  137. return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap) ||
  138. nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex);
  139. }
  140. void
  141. HTMLImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
  142. {
  143. nsImageLoadingContent::AsyncEventRunning(aEvent);
  144. }
  145. nsresult
  146. HTMLImageElement::GetCurrentSrc(nsAString& aValue)
  147. {
  148. nsCOMPtr<nsIURI> currentURI;
  149. GetCurrentURI(getter_AddRefs(currentURI));
  150. if (currentURI) {
  151. nsAutoCString spec;
  152. currentURI->GetSpec(spec);
  153. CopyUTF8toUTF16(spec, aValue);
  154. } else {
  155. SetDOMStringToNull(aValue);
  156. }
  157. return NS_OK;
  158. }
  159. bool
  160. HTMLImageElement::Draggable() const
  161. {
  162. // images may be dragged unless the draggable attribute is false
  163. return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
  164. nsGkAtoms::_false, eIgnoreCase);
  165. }
  166. bool
  167. HTMLImageElement::Complete()
  168. {
  169. if (!mCurrentRequest) {
  170. return true;
  171. }
  172. if (mPendingRequest) {
  173. return false;
  174. }
  175. uint32_t status;
  176. mCurrentRequest->GetImageStatus(&status);
  177. return
  178. (status &
  179. (imgIRequest::STATUS_LOAD_COMPLETE | imgIRequest::STATUS_ERROR)) != 0;
  180. }
  181. NS_IMETHODIMP
  182. HTMLImageElement::GetComplete(bool* aComplete)
  183. {
  184. NS_PRECONDITION(aComplete, "Null out param!");
  185. *aComplete = Complete();
  186. return NS_OK;
  187. }
  188. CSSIntPoint
  189. HTMLImageElement::GetXY()
  190. {
  191. nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
  192. if (!frame) {
  193. return CSSIntPoint(0, 0);
  194. }
  195. nsIFrame* layer = nsLayoutUtils::GetClosestLayer(frame->GetParent());
  196. return CSSIntPoint::FromAppUnitsRounded(frame->GetOffsetTo(layer));
  197. }
  198. int32_t
  199. HTMLImageElement::X()
  200. {
  201. return GetXY().x;
  202. }
  203. int32_t
  204. HTMLImageElement::Y()
  205. {
  206. return GetXY().y;
  207. }
  208. NS_IMETHODIMP
  209. HTMLImageElement::GetX(int32_t* aX)
  210. {
  211. *aX = X();
  212. return NS_OK;
  213. }
  214. NS_IMETHODIMP
  215. HTMLImageElement::GetY(int32_t* aY)
  216. {
  217. *aY = Y();
  218. return NS_OK;
  219. }
  220. NS_IMETHODIMP
  221. HTMLImageElement::GetHeight(uint32_t* aHeight)
  222. {
  223. *aHeight = Height();
  224. return NS_OK;
  225. }
  226. NS_IMETHODIMP
  227. HTMLImageElement::SetHeight(uint32_t aHeight)
  228. {
  229. ErrorResult rv;
  230. SetHeight(aHeight, rv);
  231. return rv.StealNSResult();
  232. }
  233. NS_IMETHODIMP
  234. HTMLImageElement::GetWidth(uint32_t* aWidth)
  235. {
  236. *aWidth = Width();
  237. return NS_OK;
  238. }
  239. NS_IMETHODIMP
  240. HTMLImageElement::SetWidth(uint32_t aWidth)
  241. {
  242. ErrorResult rv;
  243. SetWidth(aWidth, rv);
  244. return rv.StealNSResult();
  245. }
  246. bool
  247. HTMLImageElement::ParseAttribute(int32_t aNamespaceID,
  248. nsIAtom* aAttribute,
  249. const nsAString& aValue,
  250. nsAttrValue& aResult)
  251. {
  252. if (aNamespaceID == kNameSpaceID_None) {
  253. if (aAttribute == nsGkAtoms::align) {
  254. return ParseAlignValue(aValue, aResult);
  255. }
  256. if (aAttribute == nsGkAtoms::crossorigin) {
  257. ParseCORSValue(aValue, aResult);
  258. return true;
  259. }
  260. if (ParseImageAttribute(aAttribute, aValue, aResult)) {
  261. return true;
  262. }
  263. }
  264. return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
  265. aResult);
  266. }
  267. void
  268. HTMLImageElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
  269. nsRuleData* aData)
  270. {
  271. nsGenericHTMLElement::MapImageAlignAttributeInto(aAttributes, aData);
  272. nsGenericHTMLElement::MapImageBorderAttributeInto(aAttributes, aData);
  273. nsGenericHTMLElement::MapImageMarginAttributeInto(aAttributes, aData);
  274. nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData, true);
  275. nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
  276. }
  277. nsChangeHint
  278. HTMLImageElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
  279. int32_t aModType) const
  280. {
  281. nsChangeHint retval =
  282. nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
  283. if (aAttribute == nsGkAtoms::usemap ||
  284. aAttribute == nsGkAtoms::ismap) {
  285. retval |= nsChangeHint_ReconstructFrame;
  286. } else if (aAttribute == nsGkAtoms::alt) {
  287. if (aModType == nsIDOMMutationEvent::ADDITION ||
  288. aModType == nsIDOMMutationEvent::REMOVAL) {
  289. retval |= nsChangeHint_ReconstructFrame;
  290. }
  291. }
  292. return retval;
  293. }
  294. NS_IMETHODIMP_(bool)
  295. HTMLImageElement::IsAttributeMapped(const nsIAtom* aAttribute) const
  296. {
  297. static const MappedAttributeEntry* const map[] = {
  298. sCommonAttributeMap,
  299. sImageMarginSizeAttributeMap,
  300. sImageBorderAttributeMap,
  301. sImageAlignAttributeMap
  302. };
  303. return FindAttributeDependence(aAttribute, map);
  304. }
  305. nsMapRuleToAttributesFunc
  306. HTMLImageElement::GetAttributeMappingFunction() const
  307. {
  308. return &MapAttributesIntoRule;
  309. }
  310. nsresult
  311. HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
  312. const nsAttrValueOrString* aValue,
  313. bool aNotify)
  314. {
  315. if (aValue) {
  316. BeforeMaybeChangeAttr(aNameSpaceID, aName, *aValue, aNotify);
  317. }
  318. if (aNameSpaceID == kNameSpaceID_None && mForm &&
  319. (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
  320. // remove the image from the hashtable as needed
  321. nsAutoString tmp;
  322. GetAttr(kNameSpaceID_None, aName, tmp);
  323. if (!tmp.IsEmpty()) {
  324. mForm->RemoveImageElementFromTable(this, tmp,
  325. HTMLFormElement::AttributeUpdated);
  326. }
  327. }
  328. return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName,
  329. aValue, aNotify);
  330. }
  331. nsresult
  332. HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
  333. const nsAttrValue* aValue,
  334. const nsAttrValue* aOldValue, bool aNotify)
  335. {
  336. if (aValue) {
  337. AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
  338. }
  339. if (aNameSpaceID == kNameSpaceID_None && mForm &&
  340. (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
  341. aValue && !aValue->IsEmptyString()) {
  342. // add the image to the hashtable as needed
  343. MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom,
  344. "Expected atom value for name/id");
  345. mForm->AddImageElementToTable(this,
  346. nsDependentAtomString(aValue->GetAtomValue()));
  347. }
  348. // Handle src/srcset updates. If aNotify is false, we are coming from the
  349. // parser or some such place; we'll get bound after all the attributes have
  350. // been set, so we'll do the image load from BindToTree.
  351. nsAttrValueOrString attrVal(aValue);
  352. if (aName == nsGkAtoms::src &&
  353. aNameSpaceID == kNameSpaceID_None &&
  354. !aValue) {
  355. // SetAttr handles setting src since it needs to catch img.src =
  356. // img.src, so we only need to handle the unset case
  357. if (InResponsiveMode()) {
  358. if (mResponsiveSelector &&
  359. mResponsiveSelector->Content() == this) {
  360. mResponsiveSelector->SetDefaultSource(NullString());
  361. }
  362. QueueImageLoadTask(true);
  363. } else {
  364. // Bug 1076583 - We still behave synchronously in the non-responsive case
  365. CancelImageRequests(aNotify);
  366. }
  367. } else if (aName == nsGkAtoms::srcset &&
  368. aNameSpaceID == kNameSpaceID_None) {
  369. PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
  370. } else if (aName == nsGkAtoms::sizes &&
  371. aNameSpaceID == kNameSpaceID_None) {
  372. PictureSourceSizesChanged(this, attrVal.String(), aNotify);
  373. }
  374. return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
  375. aValue, aOldValue, aNotify);
  376. }
  377. nsresult
  378. HTMLImageElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
  379. const nsAttrValueOrString& aValue,
  380. bool aNotify)
  381. {
  382. BeforeMaybeChangeAttr(aNamespaceID, aName, aValue, aNotify);
  383. AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
  384. return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
  385. aValue, aNotify);
  386. }
  387. void
  388. HTMLImageElement::BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
  389. const nsAttrValueOrString& aValue,
  390. bool aNotify)
  391. {
  392. // We need to force our image to reload. This must be done here, not in
  393. // AfterSetAttr or BeforeSetAttr, because we want to do it even if the attr is
  394. // being set to its existing value, which is normally optimized away as a
  395. // no-op.
  396. //
  397. // If we are in responsive mode, we drop the forced reload behavior,
  398. // but still trigger a image load task for img.src = img.src per
  399. // spec.
  400. //
  401. // Both cases handle unsetting src in AfterSetAttr
  402. //
  403. // Much of this should probably happen in AfterMaybeChangeAttr.
  404. // See Bug 1370705
  405. if (aNamespaceID == kNameSpaceID_None &&
  406. aName == nsGkAtoms::src) {
  407. if (InResponsiveMode()) {
  408. if (mResponsiveSelector &&
  409. mResponsiveSelector->Content() == this) {
  410. mResponsiveSelector->SetDefaultSource(aValue.String());
  411. }
  412. QueueImageLoadTask(true);
  413. } else if (aNotify && OwnerDoc()->IsCurrentActiveDocument()) {
  414. // If aNotify is false, we are coming from the parser or some such place;
  415. // we'll get bound after all the attributes have been set, so we'll do the
  416. // sync image load from BindToTree. Skip the LoadImage call in that case.
  417. // Note that this sync behavior is partially removed from the spec, bug 1076583
  418. // A hack to get animations to reset. See bug 594771.
  419. mNewRequestsWillNeedAnimationReset = true;
  420. // Force image loading here, so that we'll try to load the image from
  421. // network if it's set to be not cacheable... If we change things so that
  422. // the state gets in Element's attr-setting happen around this
  423. // LoadImage call, we could start passing false instead of aNotify
  424. // here.
  425. LoadImage(aValue.String(), true, aNotify, eImageLoadType_Normal);
  426. mNewRequestsWillNeedAnimationReset = false;
  427. }
  428. } else if (aNamespaceID == kNameSpaceID_None &&
  429. aName == nsGkAtoms::crossorigin &&
  430. aNotify) {
  431. nsAttrValue attrValue;
  432. ParseCORSValue(aValue.String(), attrValue);
  433. if (GetCORSMode() != AttrValueToCORSMode(&attrValue)) {
  434. // Force a new load of the image with the new cross origin policy.
  435. mForceReload = true;
  436. }
  437. } else if (aName == nsGkAtoms::referrerpolicy &&
  438. aNamespaceID == kNameSpaceID_None &&
  439. aNotify) {
  440. ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue.String());
  441. if (!InResponsiveMode() &&
  442. referrerPolicy != RP_Unset &&
  443. referrerPolicy != GetImageReferrerPolicy()) {
  444. // XXX: Bug 1076583 - We still use the older synchronous algorithm
  445. // Because referrerPolicy is not treated as relevant mutations, setting
  446. // the attribute will neither trigger a reload nor update the referrer
  447. // policy of the loading channel (whether it has previously completed or
  448. // not). Force a new load of the image with the new referrerpolicy.
  449. mForceReload = true;
  450. }
  451. }
  452. return;
  453. }
  454. void
  455. HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
  456. bool aNotify)
  457. {
  458. // Because we load image synchronously in non-responsive-mode, we need to do
  459. // reload after the attribute has been set if the reload is triggerred by
  460. // cross origin changing.
  461. if (mForceReload) {
  462. mForceReload = false;
  463. if (InResponsiveMode()) {
  464. // per spec, full selection runs when this changes, even though
  465. // it doesn't directly affect the source selection
  466. QueueImageLoadTask(true);
  467. } else if (OwnerDoc()->IsCurrentActiveDocument()) {
  468. // Bug 1076583 - We still use the older synchronous algorithm in
  469. // non-responsive mode. Force a new load of the image with the
  470. // new cross origin policy
  471. ForceReload(aNotify);
  472. }
  473. }
  474. return;
  475. }
  476. nsresult
  477. HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
  478. {
  479. // We handle image element with attribute ismap in its corresponding frame
  480. // element. Set mMultipleActionsPrevented here to prevent the click event
  481. // trigger the behaviors in Element::PostHandleEventForLinks
  482. WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
  483. if (mouseEvent && mouseEvent->IsLeftClickEvent() && IsMap()) {
  484. mouseEvent->mFlags.mMultipleActionsPrevented = true;
  485. }
  486. return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
  487. }
  488. bool
  489. HTMLImageElement::IsHTMLFocusable(bool aWithMouse,
  490. bool *aIsFocusable, int32_t *aTabIndex)
  491. {
  492. int32_t tabIndex = TabIndex();
  493. if (IsInUncomposedDoc()) {
  494. nsAutoString usemap;
  495. GetUseMap(usemap);
  496. // XXXbz which document should this be using? sXBL/XBL2 issue! I
  497. // think that OwnerDoc() is right, since we don't want to
  498. // assume stuff about the document we're bound to.
  499. if (OwnerDoc()->FindImageMap(usemap)) {
  500. if (aTabIndex) {
  501. // Use tab index on individual map areas
  502. *aTabIndex = (sTabFocusModel & eTabFocus_linksMask)? 0 : -1;
  503. }
  504. // Image map is not focusable itself, but flag as tabbable
  505. // so that image map areas get walked into.
  506. *aIsFocusable = false;
  507. return false;
  508. }
  509. }
  510. if (aTabIndex) {
  511. // Can be in tab order if tabindex >=0 and form controls are tabbable.
  512. *aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask)? tabIndex : -1;
  513. }
  514. *aIsFocusable =
  515. (tabIndex >= 0 || HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex));
  516. return false;
  517. }
  518. nsresult
  519. HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
  520. nsIContent* aBindingParent,
  521. bool aCompileEventHandlers)
  522. {
  523. nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
  524. aBindingParent,
  525. aCompileEventHandlers);
  526. NS_ENSURE_SUCCESS(rv, rv);
  527. nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
  528. aCompileEventHandlers);
  529. if (aParent) {
  530. UpdateFormOwner();
  531. }
  532. if (HaveSrcsetOrInPicture()) {
  533. if (aDocument && !mInDocResponsiveContent) {
  534. aDocument->AddResponsiveContent(this);
  535. mInDocResponsiveContent = true;
  536. }
  537. // Run selection algorithm when an img element is inserted into a document
  538. // in order to react to changes in the environment. See note of
  539. // https://html.spec.whatwg.org/multipage/embedded-content.html#img-environment-changes
  540. QueueImageLoadTask(false);
  541. } else if (!InResponsiveMode() &&
  542. HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
  543. // We skip loading when our attributes were set from parser land,
  544. // so trigger a aForce=false load now to check if things changed.
  545. // This isn't necessary for responsive mode, since creating the
  546. // image load task is asynchronous we don't need to take special
  547. // care to avoid doing so when being filled by the parser.
  548. // FIXME: Bug 660963 it would be nice if we could just have
  549. // ClearBrokenState update our state and do it fast...
  550. ClearBrokenState();
  551. RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
  552. // We still act synchronously for the non-responsive case (Bug
  553. // 1076583), but still need to delay if it is unsafe to run
  554. // script.
  555. // If loading is temporarily disabled, don't even launch MaybeLoadImage.
  556. // Otherwise MaybeLoadImage may run later when someone has reenabled
  557. // loading.
  558. if (LoadingEnabled()) {
  559. nsContentUtils::AddScriptRunner(
  560. NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
  561. }
  562. }
  563. return rv;
  564. }
  565. void
  566. HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
  567. {
  568. if (mForm) {
  569. if (aNullParent || !FindAncestorForm(mForm)) {
  570. ClearForm(true);
  571. } else {
  572. UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
  573. }
  574. }
  575. if (mInDocResponsiveContent) {
  576. nsIDocument* doc = GetOurOwnerDoc();
  577. MOZ_ASSERT(doc);
  578. if (doc) {
  579. doc->RemoveResponsiveContent(this);
  580. mInDocResponsiveContent = false;
  581. }
  582. }
  583. mLastSelectedSource = nullptr;
  584. nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
  585. nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
  586. }
  587. void
  588. HTMLImageElement::UpdateFormOwner()
  589. {
  590. if (!mForm) {
  591. mForm = FindAncestorForm();
  592. }
  593. if (mForm && !HasFlag(ADDED_TO_FORM)) {
  594. // Now we need to add ourselves to the form
  595. nsAutoString nameVal, idVal;
  596. GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
  597. GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
  598. SetFlags(ADDED_TO_FORM);
  599. mForm->AddImageElement(this);
  600. if (!nameVal.IsEmpty()) {
  601. mForm->AddImageElementToTable(this, nameVal);
  602. }
  603. if (!idVal.IsEmpty()) {
  604. mForm->AddImageElementToTable(this, idVal);
  605. }
  606. }
  607. }
  608. void
  609. HTMLImageElement::MaybeLoadImage()
  610. {
  611. // Our base URI may have changed, or we may have had responsive parameters
  612. // change while not bound to the tree. Re-parse src/srcset and call LoadImage,
  613. // which is a no-op if it resolves to the same effective URI without aForce.
  614. // Note, check LoadingEnabled() after LoadImage call.
  615. LoadSelectedImage(false, true, false);
  616. if (!LoadingEnabled()) {
  617. CancelImageRequests(true);
  618. }
  619. }
  620. EventStates
  621. HTMLImageElement::IntrinsicState() const
  622. {
  623. return nsGenericHTMLElement::IntrinsicState() |
  624. nsImageLoadingContent::ImageState();
  625. }
  626. void
  627. HTMLImageElement::NodeInfoChanged()
  628. {
  629. // Resetting the last selected source if adoption steps are run.
  630. mLastSelectedSource = nullptr;
  631. }
  632. // static
  633. already_AddRefed<HTMLImageElement>
  634. HTMLImageElement::Image(const GlobalObject& aGlobal,
  635. const Optional<uint32_t>& aWidth,
  636. const Optional<uint32_t>& aHeight,
  637. ErrorResult& aError)
  638. {
  639. nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal.GetAsSupports());
  640. nsIDocument* doc;
  641. if (!win || !(doc = win->GetExtantDoc())) {
  642. aError.Throw(NS_ERROR_FAILURE);
  643. return nullptr;
  644. }
  645. already_AddRefed<mozilla::dom::NodeInfo> nodeInfo =
  646. doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::img, nullptr,
  647. kNameSpaceID_XHTML,
  648. nsIDOMNode::ELEMENT_NODE);
  649. RefPtr<HTMLImageElement> img = new HTMLImageElement(nodeInfo);
  650. if (aWidth.WasPassed()) {
  651. img->SetWidth(aWidth.Value(), aError);
  652. if (aError.Failed()) {
  653. return nullptr;
  654. }
  655. if (aHeight.WasPassed()) {
  656. img->SetHeight(aHeight.Value(), aError);
  657. if (aError.Failed()) {
  658. return nullptr;
  659. }
  660. }
  661. }
  662. return img.forget();
  663. }
  664. uint32_t
  665. HTMLImageElement::NaturalHeight()
  666. {
  667. uint32_t height;
  668. nsresult rv = nsImageLoadingContent::GetNaturalHeight(&height);
  669. if (NS_FAILED(rv)) {
  670. MOZ_ASSERT(false, "GetNaturalHeight should not fail");
  671. return 0;
  672. }
  673. if (mResponsiveSelector) {
  674. double density = mResponsiveSelector->GetSelectedImageDensity();
  675. MOZ_ASSERT(density >= 0.0);
  676. height = NSToIntRound(double(height) / density);
  677. height = std::max(height, 0u);
  678. }
  679. return height;
  680. }
  681. NS_IMETHODIMP
  682. HTMLImageElement::GetNaturalHeight(uint32_t* aNaturalHeight)
  683. {
  684. *aNaturalHeight = NaturalHeight();
  685. return NS_OK;
  686. }
  687. uint32_t
  688. HTMLImageElement::NaturalWidth()
  689. {
  690. uint32_t width;
  691. nsresult rv = nsImageLoadingContent::GetNaturalWidth(&width);
  692. if (NS_FAILED(rv)) {
  693. MOZ_ASSERT(false, "GetNaturalWidth should not fail");
  694. return 0;
  695. }
  696. if (mResponsiveSelector) {
  697. double density = mResponsiveSelector->GetSelectedImageDensity();
  698. MOZ_ASSERT(density >= 0.0);
  699. width = NSToIntRound(double(width) / density);
  700. width = std::max(width, 0u);
  701. }
  702. return width;
  703. }
  704. NS_IMETHODIMP
  705. HTMLImageElement::GetNaturalWidth(uint32_t* aNaturalWidth)
  706. {
  707. *aNaturalWidth = NaturalWidth();
  708. return NS_OK;
  709. }
  710. nsresult
  711. HTMLImageElement::CopyInnerTo(Element* aDest)
  712. {
  713. bool destIsStatic = aDest->OwnerDoc()->IsStaticDocument();
  714. auto dest = static_cast<HTMLImageElement*>(aDest);
  715. if (destIsStatic) {
  716. CreateStaticImageClone(dest);
  717. }
  718. nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
  719. if (NS_FAILED(rv)) {
  720. return rv;
  721. }
  722. if (!destIsStatic) {
  723. // In SetAttr (called from nsGenericHTMLElement::CopyInnerTo), dest skipped
  724. // doing the image load because we passed in false for aNotify. But we
  725. // really do want it to do the load, so set it up to happen once the cloning
  726. // reaches a stable state.
  727. if (!dest->InResponsiveMode() &&
  728. dest->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
  729. nsContentUtils::AddScriptRunner(
  730. NewRunnableMethod(dest, &HTMLImageElement::MaybeLoadImage));
  731. }
  732. }
  733. return NS_OK;
  734. }
  735. CORSMode
  736. HTMLImageElement::GetCORSMode()
  737. {
  738. return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
  739. }
  740. JSObject*
  741. HTMLImageElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  742. {
  743. return HTMLImageElementBinding::Wrap(aCx, this, aGivenProto);
  744. }
  745. #ifdef DEBUG
  746. nsIDOMHTMLFormElement*
  747. HTMLImageElement::GetForm() const
  748. {
  749. return mForm;
  750. }
  751. #endif
  752. void
  753. HTMLImageElement::SetForm(nsIDOMHTMLFormElement* aForm)
  754. {
  755. NS_PRECONDITION(aForm, "Don't pass null here");
  756. NS_ASSERTION(!mForm,
  757. "We don't support switching from one non-null form to another.");
  758. mForm = static_cast<HTMLFormElement*>(aForm);
  759. }
  760. void
  761. HTMLImageElement::ClearForm(bool aRemoveFromForm)
  762. {
  763. NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM),
  764. "Form control should have had flag set correctly");
  765. if (!mForm) {
  766. return;
  767. }
  768. if (aRemoveFromForm) {
  769. nsAutoString nameVal, idVal;
  770. GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
  771. GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
  772. mForm->RemoveImageElement(this);
  773. if (!nameVal.IsEmpty()) {
  774. mForm->RemoveImageElementFromTable(this, nameVal,
  775. HTMLFormElement::ElementRemoved);
  776. }
  777. if (!idVal.IsEmpty()) {
  778. mForm->RemoveImageElementFromTable(this, idVal,
  779. HTMLFormElement::ElementRemoved);
  780. }
  781. }
  782. UnsetFlags(ADDED_TO_FORM);
  783. mForm = nullptr;
  784. }
  785. void
  786. HTMLImageElement::QueueImageLoadTask(bool aAlwaysLoad)
  787. {
  788. // If loading is temporarily disabled, we don't want to queue tasks
  789. // that may then run when loading is re-enabled.
  790. if (!LoadingEnabled() || !this->OwnerDoc()->IsCurrentActiveDocument()) {
  791. return;
  792. }
  793. // Ensure that we don't overwrite a previous load request that requires
  794. // a complete load to occur.
  795. bool alwaysLoad = aAlwaysLoad;
  796. if (mPendingImageLoadTask) {
  797. alwaysLoad = alwaysLoad || mPendingImageLoadTask->AlwaysLoad();
  798. }
  799. RefPtr<ImageLoadTask> task = new ImageLoadTask(this, alwaysLoad);
  800. // The task checks this to determine if it was the last
  801. // queued event, and so earlier tasks are implicitly canceled.
  802. mPendingImageLoadTask = task;
  803. nsContentUtils::RunInStableState(task.forget());
  804. }
  805. bool
  806. HTMLImageElement::HaveSrcsetOrInPicture()
  807. {
  808. if (HasAttr(kNameSpaceID_None, nsGkAtoms::srcset)) {
  809. return true;
  810. }
  811. Element *parent = nsINode::GetParentElement();
  812. return (parent && parent->IsHTMLElement(nsGkAtoms::picture));
  813. }
  814. bool
  815. HTMLImageElement::InResponsiveMode()
  816. {
  817. // When we lose srcset or leave a <picture> element, the fallback to img.src
  818. // will happen from the microtask, and we should behave responsively in the
  819. // interim
  820. return mResponsiveSelector ||
  821. mPendingImageLoadTask ||
  822. HaveSrcsetOrInPicture();
  823. }
  824. bool
  825. HTMLImageElement::SelectedSourceMatchesLast(nsIURI* aSelectedSource, double aSelectedDensity)
  826. {
  827. // If there was no selected source previously, we don't want to short-circuit the load.
  828. // Similarly for if there is no newly selected source.
  829. if (!mLastSelectedSource || !aSelectedSource) {
  830. return false;
  831. }
  832. bool equal = false;
  833. return NS_SUCCEEDED(mLastSelectedSource->Equals(aSelectedSource, &equal)) && equal &&
  834. aSelectedDensity == mCurrentDensity;
  835. }
  836. nsresult
  837. HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
  838. {
  839. nsresult rv = NS_ERROR_FAILURE;
  840. if (aForce) {
  841. // In responsive mode we generally want to re-run the full
  842. // selection algorithm whenever starting a new load, per
  843. // spec. This also causes us to re-resolve the URI as appropriate.
  844. if (!UpdateResponsiveSource() && !aAlwaysLoad) {
  845. return NS_OK;
  846. }
  847. }
  848. nsCOMPtr<nsIURI> selectedSource;
  849. double currentDensity = 1.0; // default to 1.0 for the src attribute case
  850. if (mResponsiveSelector) {
  851. nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
  852. selectedSource = url;
  853. currentDensity = mResponsiveSelector->GetSelectedImageDensity();
  854. if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
  855. return NS_OK;
  856. }
  857. if (url) {
  858. rv = LoadImage(url, aForce, aNotify, eImageLoadType_Imageset);
  859. }
  860. } else {
  861. nsAutoString src;
  862. if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
  863. CancelImageRequests(aNotify);
  864. rv = NS_OK;
  865. } else {
  866. nsIDocument* doc = GetOurOwnerDoc();
  867. if (doc) {
  868. StringToURI(src, doc, getter_AddRefs(selectedSource));
  869. if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
  870. return NS_OK;
  871. }
  872. }
  873. // If we have a srcset attribute or are in a <picture> element,
  874. // we always use the Imageset load type, even if we parsed no
  875. // valid responsive sources from either, per spec.
  876. rv = LoadImage(src, aForce, aNotify,
  877. HaveSrcsetOrInPicture() ? eImageLoadType_Imageset
  878. : eImageLoadType_Normal);
  879. }
  880. }
  881. mLastSelectedSource = selectedSource;
  882. mCurrentDensity = currentDensity;
  883. if (NS_FAILED(rv)) {
  884. CancelImageRequests(aNotify);
  885. }
  886. return rv;
  887. }
  888. void
  889. HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
  890. const nsAString& aNewValue,
  891. bool aNotify)
  892. {
  893. MOZ_ASSERT(aSourceNode == this ||
  894. IsPreviousSibling(aSourceNode, this),
  895. "Should not be getting notifications for non-previous-siblings");
  896. nsIContent *currentSrc =
  897. mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
  898. if (aSourceNode == currentSrc) {
  899. // We're currently using this node as our responsive selector
  900. // source.
  901. mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
  902. }
  903. if (!mInDocResponsiveContent && IsInComposedDoc()) {
  904. nsIDocument* doc = GetOurOwnerDoc();
  905. if (doc) {
  906. doc->AddResponsiveContent(this);
  907. mInDocResponsiveContent = true;
  908. }
  909. }
  910. // This always triggers the image update steps per the spec, even if
  911. // we are not using this source.
  912. QueueImageLoadTask(true);
  913. }
  914. void
  915. HTMLImageElement::PictureSourceSizesChanged(nsIContent *aSourceNode,
  916. const nsAString& aNewValue,
  917. bool aNotify)
  918. {
  919. MOZ_ASSERT(aSourceNode == this ||
  920. IsPreviousSibling(aSourceNode, this),
  921. "Should not be getting notifications for non-previous-siblings");
  922. nsIContent *currentSrc =
  923. mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
  924. if (aSourceNode == currentSrc) {
  925. // We're currently using this node as our responsive selector
  926. // source.
  927. mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
  928. }
  929. // This always triggers the image update steps per the spec, even if
  930. // we are not using this source.
  931. QueueImageLoadTask(true);
  932. }
  933. void
  934. HTMLImageElement::PictureSourceMediaOrTypeChanged(nsIContent *aSourceNode,
  935. bool aNotify)
  936. {
  937. MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
  938. "Should not be getting notifications for non-previous-siblings");
  939. // This always triggers the image update steps per the spec, even if
  940. // we are not switching to/from this source
  941. QueueImageLoadTask(true);
  942. }
  943. void
  944. HTMLImageElement::PictureSourceAdded(nsIContent *aSourceNode)
  945. {
  946. MOZ_ASSERT(aSourceNode == this ||
  947. IsPreviousSibling(aSourceNode, this),
  948. "Should not be getting notifications for non-previous-siblings");
  949. QueueImageLoadTask(true);
  950. }
  951. void
  952. HTMLImageElement::PictureSourceRemoved(nsIContent *aSourceNode)
  953. {
  954. MOZ_ASSERT(aSourceNode == this ||
  955. IsPreviousSibling(aSourceNode, this),
  956. "Should not be getting notifications for non-previous-siblings");
  957. QueueImageLoadTask(true);
  958. }
  959. bool
  960. HTMLImageElement::UpdateResponsiveSource()
  961. {
  962. bool hadSelector = !!mResponsiveSelector;
  963. nsIContent *currentSource =
  964. mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
  965. Element *parent = nsINode::GetParentElement();
  966. nsINode *candidateSource = nullptr;
  967. if (parent && parent->IsHTMLElement(nsGkAtoms::picture)) {
  968. // Walk source nodes previous to ourselves
  969. candidateSource = parent->GetFirstChild();
  970. } else {
  971. candidateSource = this;
  972. }
  973. while (candidateSource) {
  974. if (candidateSource == currentSource) {
  975. // found no better source before current, re-run selection on
  976. // that and keep it if it's still usable.
  977. bool changed = mResponsiveSelector->SelectImage(true);
  978. if (mResponsiveSelector->NumCandidates()) {
  979. bool isUsableCandidate = true;
  980. // an otherwise-usable source element may still have a media query that may not
  981. // match any more.
  982. if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
  983. !SourceElementMatches(candidateSource->AsContent())) {
  984. isUsableCandidate = false;
  985. }
  986. if (isUsableCandidate) {
  987. return changed;
  988. }
  989. }
  990. // no longer valid
  991. mResponsiveSelector = nullptr;
  992. if (candidateSource == this) {
  993. // No further possibilities
  994. break;
  995. }
  996. } else if (candidateSource == this) {
  997. // We are the last possible source
  998. if (!TryCreateResponsiveSelector(candidateSource->AsContent())) {
  999. // Failed to find any source
  1000. mResponsiveSelector = nullptr;
  1001. }
  1002. break;
  1003. } else if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
  1004. TryCreateResponsiveSelector(candidateSource->AsContent())) {
  1005. // This led to a valid source, stop
  1006. break;
  1007. }
  1008. candidateSource = candidateSource->GetNextSibling();
  1009. }
  1010. if (!candidateSource) {
  1011. // Ran out of siblings without finding ourself, e.g. XBL magic.
  1012. mResponsiveSelector = nullptr;
  1013. }
  1014. // If we reach this point, either:
  1015. // - there was no selector originally, and there is not one now
  1016. // - there was no selector originally, and there is one now
  1017. // - there was a selector, and there is a different one now
  1018. // - there was a selector, and there is not one now
  1019. return hadSelector || mResponsiveSelector;
  1020. }
  1021. /*static */ bool
  1022. HTMLImageElement::SupportedPictureSourceType(const nsAString& aType)
  1023. {
  1024. nsAutoString type;
  1025. nsAutoString params;
  1026. nsContentUtils::SplitMimeType(aType, type, params);
  1027. if (type.IsEmpty()) {
  1028. return true;
  1029. }
  1030. return
  1031. imgLoader::SupportImageWithMimeType(NS_ConvertUTF16toUTF8(type).get(),
  1032. AcceptedMimeTypes::IMAGES_AND_DOCUMENTS);
  1033. }
  1034. bool
  1035. HTMLImageElement::SourceElementMatches(nsIContent* aSourceNode)
  1036. {
  1037. MOZ_ASSERT(aSourceNode->IsHTMLElement(nsGkAtoms::source));
  1038. DebugOnly<Element *> parent(nsINode::GetParentElement());
  1039. MOZ_ASSERT(parent && parent->IsHTMLElement(nsGkAtoms::picture));
  1040. MOZ_ASSERT(IsPreviousSibling(aSourceNode, this));
  1041. // Check media and type
  1042. HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
  1043. if (!src->MatchesCurrentMedia()) {
  1044. return false;
  1045. }
  1046. nsAutoString type;
  1047. if (aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
  1048. !SupportedPictureSourceType(type)) {
  1049. return false;
  1050. }
  1051. return true;
  1052. }
  1053. bool
  1054. HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode)
  1055. {
  1056. // Skip if this is not a <source> with matching media query
  1057. bool isSourceTag = aSourceNode->IsHTMLElement(nsGkAtoms::source);
  1058. if (isSourceTag) {
  1059. if (!SourceElementMatches(aSourceNode)) {
  1060. return false;
  1061. }
  1062. } else if (aSourceNode->IsHTMLElement(nsGkAtoms::img)) {
  1063. // Otherwise this is the <img> tag itself
  1064. MOZ_ASSERT(aSourceNode == this);
  1065. }
  1066. // Skip if has no srcset or an empty srcset
  1067. nsString srcset;
  1068. if (!aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::srcset, srcset)) {
  1069. return false;
  1070. }
  1071. if (srcset.IsEmpty()) {
  1072. return false;
  1073. }
  1074. // Try to parse
  1075. RefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(aSourceNode);
  1076. if (!sel->SetCandidatesFromSourceSet(srcset)) {
  1077. // No possible candidates, don't need to bother parsing sizes
  1078. return false;
  1079. }
  1080. nsAutoString sizes;
  1081. aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::sizes, sizes);
  1082. sel->SetSizesFromDescriptor(sizes);
  1083. // If this is the <img> tag, also pull in src as the default source
  1084. if (!isSourceTag) {
  1085. MOZ_ASSERT(aSourceNode == this);
  1086. nsAutoString src;
  1087. if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && !src.IsEmpty()) {
  1088. sel->SetDefaultSource(src);
  1089. }
  1090. }
  1091. mResponsiveSelector = sel;
  1092. return true;
  1093. }
  1094. /* static */ bool
  1095. HTMLImageElement::SelectSourceForTagWithAttrs(nsIDocument *aDocument,
  1096. bool aIsSourceTag,
  1097. const nsAString& aSrcAttr,
  1098. const nsAString& aSrcsetAttr,
  1099. const nsAString& aSizesAttr,
  1100. const nsAString& aTypeAttr,
  1101. const nsAString& aMediaAttr,
  1102. nsAString& aResult)
  1103. {
  1104. MOZ_ASSERT(aIsSourceTag || (aTypeAttr.IsEmpty() && aMediaAttr.IsEmpty()),
  1105. "Passing type or media attrs makes no sense without aIsSourceTag");
  1106. MOZ_ASSERT(!aIsSourceTag || aSrcAttr.IsEmpty(),
  1107. "Passing aSrcAttr makes no sense with aIsSourceTag set");
  1108. if (aSrcsetAttr.IsEmpty()) {
  1109. if (!aIsSourceTag) {
  1110. // For an <img> with no srcset, we would always select the src attr.
  1111. aResult.Assign(aSrcAttr);
  1112. return true;
  1113. }
  1114. // Otherwise, a <source> without srcset is never selected
  1115. return false;
  1116. }
  1117. // Would not consider source tags with unsupported media or type
  1118. if (aIsSourceTag &&
  1119. ((!aMediaAttr.IsVoid() &&
  1120. !HTMLSourceElement::WouldMatchMediaForDocument(aMediaAttr, aDocument)) ||
  1121. (!aTypeAttr.IsVoid() &&
  1122. !SupportedPictureSourceType(aTypeAttr)))) {
  1123. return false;
  1124. }
  1125. // Using srcset or picture <source>, build a responsive selector for this tag.
  1126. RefPtr<ResponsiveImageSelector> sel =
  1127. new ResponsiveImageSelector(aDocument);
  1128. sel->SetCandidatesFromSourceSet(aSrcsetAttr);
  1129. if (!aSizesAttr.IsEmpty()) {
  1130. sel->SetSizesFromDescriptor(aSizesAttr);
  1131. }
  1132. if (!aIsSourceTag) {
  1133. sel->SetDefaultSource(aSrcAttr);
  1134. }
  1135. if (sel->GetSelectedImageURLSpec(aResult)) {
  1136. return true;
  1137. }
  1138. if (!aIsSourceTag) {
  1139. // <img> tag with no match would definitively load nothing.
  1140. aResult.Truncate();
  1141. return true;
  1142. }
  1143. // <source> tags with no match would leave source yet-undetermined.
  1144. return false;
  1145. }
  1146. void
  1147. HTMLImageElement::DestroyContent()
  1148. {
  1149. mResponsiveSelector = nullptr;
  1150. nsGenericHTMLElement::DestroyContent();
  1151. }
  1152. void
  1153. HTMLImageElement::MediaFeatureValuesChanged()
  1154. {
  1155. QueueImageLoadTask(false);
  1156. }
  1157. void
  1158. HTMLImageElement::FlushUseCounters()
  1159. {
  1160. nsCOMPtr<imgIRequest> request;
  1161. GetRequest(CURRENT_REQUEST, getter_AddRefs(request));
  1162. nsCOMPtr<imgIContainer> container;
  1163. request->GetImage(getter_AddRefs(container));
  1164. }
  1165. } // namespace dom
  1166. } // namespace mozilla