LCOV - code coverage report
Current view: top level - dom/html - HTMLImageElement.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 119 593 20.1 %
Date: 2017-07-14 16:53:18 Functions: 21 103 20.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13