tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLImageElement.cpp (40776B)


      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 
      9 #include "imgLoader.h"
     10 #include "mozilla/FocusModel.h"
     11 #include "mozilla/MouseEvents.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/StaticPrefs_image.h"
     14 #include "mozilla/dom/BindContext.h"
     15 #include "mozilla/dom/BindingUtils.h"
     16 #include "mozilla/dom/DOMIntersectionObserver.h"
     17 #include "mozilla/dom/Document.h"
     18 #include "mozilla/dom/HTMLFormElement.h"
     19 #include "mozilla/dom/HTMLImageElementBinding.h"
     20 #include "mozilla/dom/NameSpaceConstants.h"
     21 #include "mozilla/dom/UnbindContext.h"
     22 #include "mozilla/dom/UserActivation.h"
     23 #include "nsAttrValueOrString.h"
     24 #include "nsContainerFrame.h"
     25 #include "nsContentUtils.h"
     26 #include "nsFocusManager.h"
     27 #include "nsGenericHTMLElement.h"
     28 #include "nsGkAtoms.h"
     29 #include "nsIMutationObserver.h"
     30 #include "nsImageFrame.h"
     31 #include "nsNodeInfoManager.h"
     32 #include "nsPresContext.h"
     33 #include "nsSize.h"
     34 
     35 // Responsive images!
     36 #include "imgINotificationObserver.h"
     37 #include "imgRequestProxy.h"
     38 #include "mozilla/EventDispatcher.h"
     39 #include "mozilla/MappedDeclarationsBuilder.h"
     40 #include "mozilla/Maybe.h"
     41 #include "mozilla/RestyleManager.h"
     42 #include "mozilla/dom/HTMLSourceElement.h"
     43 #include "mozilla/dom/ResponsiveImageSelector.h"
     44 #include "nsLayoutUtils.h"
     45 
     46 using namespace mozilla::net;
     47 using mozilla::Maybe;
     48 
     49 NS_IMPL_NS_NEW_HTML_ELEMENT(Image)
     50 
     51 #ifdef DEBUG
     52 // Is aSubject a previous sibling of aNode.
     53 static bool IsPreviousSibling(const nsINode* aSubject, const nsINode* aNode) {
     54  if (aSubject == aNode) {
     55    return false;
     56  }
     57 
     58  nsINode* parent = aSubject->GetParentNode();
     59  if (parent && parent == aNode->GetParentNode()) {
     60    const Maybe<uint32_t> indexOfSubject = parent->ComputeIndexOf(aSubject);
     61    const Maybe<uint32_t> indexOfNode = parent->ComputeIndexOf(aNode);
     62    if (MOZ_LIKELY(indexOfSubject.isSome() && indexOfNode.isSome())) {
     63      return *indexOfSubject < *indexOfNode;
     64    }
     65    // XXX Keep the odd traditional behavior for now.
     66    return indexOfSubject.isNothing() && indexOfNode.isSome();
     67  }
     68 
     69  return false;
     70 }
     71 #endif
     72 
     73 namespace mozilla::dom {
     74 
     75 HTMLImageElement::HTMLImageElement(
     76    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     77    : nsGenericHTMLElement(std::move(aNodeInfo)) {
     78  // We start out broken
     79  AddStatesSilently(ElementState::BROKEN);
     80 }
     81 
     82 HTMLImageElement::~HTMLImageElement() {
     83  nsImageLoadingContent::Destroy();
     84  if (mInDocResponsiveContent) {
     85    OwnerDoc()->RemoveResponsiveContent(this);
     86    mInDocResponsiveContent = false;
     87  }
     88 }
     89 
     90 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLImageElement, nsGenericHTMLElement,
     91                                   mResponsiveSelector)
     92 
     93 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLImageElement,
     94                                             nsGenericHTMLElement,
     95                                             nsIImageLoadingContent,
     96                                             imgINotificationObserver)
     97 
     98 NS_IMPL_ELEMENT_CLONE(HTMLImageElement)
     99 
    100 bool HTMLImageElement::IsInteractiveHTMLContent() const {
    101  return HasAttr(nsGkAtoms::usemap) ||
    102         nsGenericHTMLElement::IsInteractiveHTMLContent();
    103 }
    104 
    105 void HTMLImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
    106  nsImageLoadingContent::AsyncEventRunning(aEvent);
    107 }
    108 
    109 void HTMLImageElement::GetCurrentSrc(nsAString& aValue) {
    110  nsCOMPtr<nsIURI> currentURI;
    111  GetCurrentURI(getter_AddRefs(currentURI));
    112  if (currentURI) {
    113    nsAutoCString spec;
    114    currentURI->GetSpec(spec);
    115    CopyUTF8toUTF16(spec, aValue);
    116  } else {
    117    SetDOMStringToNull(aValue);
    118  }
    119 }
    120 
    121 bool HTMLImageElement::Draggable() const {
    122  // images may be dragged unless the draggable attribute is false
    123  return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
    124                      nsGkAtoms::_false, eIgnoreCase);
    125 }
    126 
    127 bool HTMLImageElement::Complete() {
    128  // It is still not clear what value should img.complete return in various
    129  // cases, see https://github.com/whatwg/html/issues/4884
    130  if (!HasAttr(nsGkAtoms::srcset) && !HasNonEmptyAttr(nsGkAtoms::src)) {
    131    return true;
    132  }
    133 
    134  if (!mCurrentRequest || mPendingRequest || mPendingImageLoadTask) {
    135    return false;
    136  }
    137 
    138  uint32_t status;
    139  mCurrentRequest->GetImageStatus(&status);
    140  return (status &
    141          (imgIRequest::STATUS_LOAD_COMPLETE | imgIRequest::STATUS_ERROR)) != 0;
    142 }
    143 
    144 CSSIntPoint HTMLImageElement::GetXY() {
    145  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
    146  if (!frame) {
    147    return CSSIntPoint(0, 0);
    148  }
    149  return CSSIntPoint::FromAppUnitsRounded(
    150      frame->GetOffsetTo(frame->PresShell()->GetRootFrame()));
    151 }
    152 
    153 int32_t HTMLImageElement::X() { return GetXY().x; }
    154 
    155 int32_t HTMLImageElement::Y() { return GetXY().y; }
    156 
    157 void HTMLImageElement::GetDecoding(nsAString& aValue) {
    158  GetEnumAttr(nsGkAtoms::decoding, kDecodingTableDefault->tag, aValue);
    159 }
    160 
    161 already_AddRefed<Promise> HTMLImageElement::Decode(ErrorResult& aRv) {
    162  return nsImageLoadingContent::QueueDecodeAsync(aRv);
    163 }
    164 
    165 bool HTMLImageElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
    166                                      const nsAString& aValue,
    167                                      nsIPrincipal* aMaybeScriptedPrincipal,
    168                                      nsAttrValue& aResult) {
    169  if (aNamespaceID == kNameSpaceID_None) {
    170    if (aAttribute == nsGkAtoms::align) {
    171      return ParseAlignValue(aValue, aResult);
    172    }
    173    if (aAttribute == nsGkAtoms::crossorigin) {
    174      ParseCORSValue(aValue, aResult);
    175      return true;
    176    }
    177    if (aAttribute == nsGkAtoms::decoding) {
    178      return aResult.ParseEnumValue(aValue, kDecodingTable,
    179                                    /* aCaseSensitive = */ false,
    180                                    kDecodingTableDefault);
    181    }
    182    if (aAttribute == nsGkAtoms::loading) {
    183      return ParseLoadingAttribute(aValue, aResult);
    184    }
    185    if (aAttribute == nsGkAtoms::fetchpriority) {
    186      ParseFetchPriority(aValue, aResult);
    187      return true;
    188    }
    189    if (ParseImageAttribute(aAttribute, aValue, aResult)) {
    190      return true;
    191    }
    192  }
    193 
    194  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
    195                                              aMaybeScriptedPrincipal, aResult);
    196 }
    197 
    198 void HTMLImageElement::MapAttributesIntoRule(
    199    MappedDeclarationsBuilder& aBuilder) {
    200  MapImageAlignAttributeInto(aBuilder);
    201  MapImageBorderAttributeInto(aBuilder);
    202  MapImageMarginAttributeInto(aBuilder);
    203  MapImageSizeAttributesInto(aBuilder, MapAspectRatio::Yes);
    204  MapCommonAttributesInto(aBuilder);
    205 }
    206 
    207 nsChangeHint HTMLImageElement::GetAttributeChangeHint(
    208    const nsAtom* aAttribute, AttrModType aModType) const {
    209  nsChangeHint retval =
    210      nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
    211  if (aAttribute == nsGkAtoms::usemap || aAttribute == nsGkAtoms::ismap) {
    212    retval |= nsChangeHint_ReconstructFrame;
    213  } else if (aAttribute == nsGkAtoms::alt) {
    214    if (IsAdditionOrRemoval(aModType)) {
    215      retval |= nsChangeHint_ReconstructFrame;
    216    }
    217  }
    218  return retval;
    219 }
    220 
    221 NS_IMETHODIMP_(bool)
    222 HTMLImageElement::IsAttributeMapped(const nsAtom* aAttribute) const {
    223  static const MappedAttributeEntry* const map[] = {
    224      sCommonAttributeMap, sImageMarginSizeAttributeMap,
    225      sImageBorderAttributeMap, sImageAlignAttributeMap};
    226 
    227  return FindAttributeDependence(aAttribute, map);
    228 }
    229 
    230 nsMapRuleToAttributesFunc HTMLImageElement::GetAttributeMappingFunction()
    231    const {
    232  return &MapAttributesIntoRule;
    233 }
    234 
    235 void HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
    236                                     const nsAttrValue* aValue, bool aNotify) {
    237  if (aNameSpaceID == kNameSpaceID_None && mForm &&
    238      (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
    239    // remove the image from the hashtable as needed
    240    if (const auto* old = GetParsedAttr(aName); old && !old->IsEmptyString()) {
    241      mForm->RemoveImageElementFromTable(
    242          this, nsDependentAtomString(old->GetAtomValue()));
    243    }
    244  }
    245 
    246  return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
    247                                             aNotify);
    248 }
    249 
    250 void HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
    251                                    const nsAttrValue* aValue,
    252                                    const nsAttrValue* aOldValue,
    253                                    nsIPrincipal* aMaybeScriptedPrincipal,
    254                                    bool aNotify) {
    255  if (aNameSpaceID != kNameSpaceID_None) {
    256    return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
    257                                              aOldValue,
    258                                              aMaybeScriptedPrincipal, aNotify);
    259  }
    260 
    261  nsAttrValueOrString attrVal(aValue);
    262  if (aName == nsGkAtoms::src) {
    263    mSrcURI = nullptr;
    264    if (aValue && !aValue->IsEmptyString()) {
    265      StringToURI(attrVal.String(), OwnerDoc(), getter_AddRefs(mSrcURI));
    266    }
    267  }
    268 
    269  if (aValue) {
    270    AfterMaybeChangeAttr(aNameSpaceID, aName, attrVal, aOldValue,
    271                         aMaybeScriptedPrincipal, aNotify);
    272  }
    273 
    274  if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) && aValue &&
    275      !aValue->IsEmptyString()) {
    276    // add the image to the hashtable as needed
    277    MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom,
    278               "Expected atom value for name/id");
    279    mForm->AddImageElementToTable(
    280        this, nsDependentAtomString(aValue->GetAtomValue()));
    281  }
    282 
    283  bool forceReload = false;
    284  if (aName == nsGkAtoms::loading) {
    285    if (aValue && Loading(aValue->GetEnumValue()) == Loading::Lazy) {
    286      SetLazyLoading();
    287    } else if (aOldValue &&
    288               Loading(aOldValue->GetEnumValue()) == Loading::Lazy) {
    289      StopLazyLoading(StartLoad(aNotify));
    290    }
    291  } else if (aName == nsGkAtoms::src && !aValue) {
    292    // AfterMaybeChangeAttr handles setting src since it needs to catch
    293    // img.src = img.src, so we only need to handle the unset case
    294    // NOTE: regular src value changes are handled in AfterMaybeChangeAttr, so
    295    // this only needs to handle unsetting the src attribute.
    296    // Mark channel as urgent-start before load image if the image load is
    297    // initiated by a user interaction.
    298    if (mResponsiveSelector && mResponsiveSelector->Content() == this) {
    299      mResponsiveSelector->SetDefaultSource(VoidString());
    300    }
    301    forceReload = true;
    302  } else if (aName == nsGkAtoms::srcset) {
    303    // Mark channel as urgent-start before load image if the image load is
    304    // initaiated by a user interaction.
    305    mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
    306 
    307    mSrcsetTriggeringPrincipal = aMaybeScriptedPrincipal;
    308 
    309    if (aValue) {
    310      if (!mInDocResponsiveContent) {
    311        OwnerDoc()->AddResponsiveContent(this);
    312        mInDocResponsiveContent = true;
    313      }
    314    } else if (mInDocResponsiveContent && !IsInPicture()) {
    315      OwnerDoc()->RemoveResponsiveContent(this);
    316      mInDocResponsiveContent = false;
    317    }
    318 
    319    PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
    320  } else if (aName == nsGkAtoms::sizes) {
    321    // Mark channel as urgent-start before load image if the image load is
    322    // initiated by a user interaction.
    323    mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
    324 
    325    PictureSourceSizesChanged(this, attrVal.String(), aNotify);
    326  } else if (aName == nsGkAtoms::decoding) {
    327    // Request sync or async image decoding.
    328    SetSyncDecodingHint(
    329        aValue && static_cast<ImageDecodingType>(aValue->GetEnumValue()) ==
    330                      ImageDecodingType::Sync);
    331  } else if (aName == nsGkAtoms::referrerpolicy) {
    332    ReferrerPolicy referrerPolicy = GetReferrerPolicyAsEnum();
    333    forceReload = referrerPolicy != ReferrerPolicy::_empty &&
    334                  referrerPolicy != ReferrerPolicyFromAttr(aOldValue);
    335  } else if (aName == nsGkAtoms::crossorigin) {
    336    forceReload = GetCORSMode() != AttrValueToCORSMode(aOldValue);
    337  }
    338 
    339  // NOTE(emilio): When not notifying, we come from the parser or some other
    340  // internal caller, in which cases we can skip the load since we are about to
    341  // get bound to a tree.
    342  if (forceReload) {
    343    mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
    344    UpdateSourceSyncAndQueueImageTask(true, aNotify);
    345  }
    346 
    347  return nsGenericHTMLElement::AfterSetAttr(
    348      aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
    349 }
    350 
    351 void HTMLImageElement::OnAttrSetButNotChanged(int32_t aNamespaceID,
    352                                              nsAtom* aName,
    353                                              const nsAttrValueOrString& aValue,
    354                                              bool aNotify) {
    355  AfterMaybeChangeAttr(aNamespaceID, aName, aValue, nullptr, nullptr, aNotify);
    356  return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
    357                                                      aValue, aNotify);
    358 }
    359 
    360 void HTMLImageElement::AfterMaybeChangeAttr(
    361    int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue,
    362    const nsAttrValue* aOldValue, nsIPrincipal* aMaybeScriptedPrincipal,
    363    bool aNotify) {
    364  if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::src) {
    365    return;
    366  }
    367 
    368  // We need to force our image to reload.  This must be done here, not in
    369  // AfterSetAttr or BeforeSetAttr, because we want to do it even if the attr is
    370  // being set to its existing value, which is normally optimized away as a
    371  // no-op.
    372  //
    373  // If we are in responsive mode, we drop the forced reload behavior, but still
    374  // trigger a image load task for img.src = img.src per spec.
    375  //
    376  // Both cases handle unsetting src in AfterSetAttr
    377  mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
    378      this, aValue.String(), aMaybeScriptedPrincipal);
    379 
    380  if (mResponsiveSelector && mResponsiveSelector->Content() == this) {
    381    mResponsiveSelector->SetDefaultSource(mSrcURI, mSrcTriggeringPrincipal);
    382  }
    383  mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
    384  UpdateSourceSyncAndQueueImageTask(true, aNotify);
    385 }
    386 
    387 void HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
    388  // We handle image element with attribute ismap in its corresponding frame
    389  // element. Set mMultipleActionsPrevented here to prevent the click event
    390  // trigger the behaviors in Element::PostHandleEventForLinks
    391  WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
    392  if (mouseEvent && mouseEvent->IsLeftClickEvent() && IsMap()) {
    393    mouseEvent->mFlags.mMultipleActionsPrevented = true;
    394  }
    395  nsGenericHTMLElement::GetEventTargetParent(aVisitor);
    396 }
    397 
    398 nsINode* HTMLImageElement::GetScopeChainParent() const {
    399  if (mForm) {
    400    return mForm;
    401  }
    402  return nsGenericHTMLElement::GetScopeChainParent();
    403 }
    404 
    405 bool HTMLImageElement::IsHTMLFocusable(IsFocusableFlags aFlags,
    406                                       bool* aIsFocusable, int32_t* aTabIndex) {
    407  int32_t tabIndex = TabIndex();
    408 
    409  if (IsInComposedDoc() && FindImageMap()) {
    410    // Use tab index on individual map areas.
    411    *aTabIndex = FocusModel::IsTabFocusable(TabFocusableType::Links) ? 0 : -1;
    412    // Image map is not focusable itself, but flag as tabbable
    413    // so that image map areas get walked into.
    414    *aIsFocusable = false;
    415    return false;
    416  }
    417 
    418  // Can be in tab order if tabindex >=0 and form controls are tabbable.
    419  *aTabIndex = FocusModel::IsTabFocusable(TabFocusableType::FormElements)
    420                   ? tabIndex
    421                   : -1;
    422  *aIsFocusable = IsFormControlDefaultFocusable(aFlags) &&
    423                  (tabIndex >= 0 || GetTabIndexAttrValue().isSome());
    424 
    425  return false;
    426 }
    427 
    428 nsresult HTMLImageElement::BindToTree(BindContext& aContext, nsINode& aParent) {
    429  MOZ_TRY(nsGenericHTMLElement::BindToTree(aContext, aParent));
    430 
    431  nsImageLoadingContent::BindToTree(aContext, aParent);
    432 
    433  UpdateFormOwner();
    434 
    435  // Mark channel as urgent-start before load image if the image load is
    436  // initiated by a user interaction.
    437  if (IsInPicture()) {
    438    if (!mInDocResponsiveContent) {
    439      OwnerDoc()->AddResponsiveContent(this);
    440      mInDocResponsiveContent = true;
    441    }
    442    mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
    443    UpdateSourceSyncAndQueueImageTask(false, /* aNotify = */ false);
    444  }
    445  return NS_OK;
    446 }
    447 
    448 void HTMLImageElement::UnbindFromTree(UnbindContext& aContext) {
    449  if (mForm) {
    450    if (aContext.IsUnbindRoot(this) || !FindAncestorForm(mForm)) {
    451      ClearForm(true);
    452    } else {
    453      UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
    454    }
    455  }
    456  // Our in-pictureness can only change if we're the unbind root.
    457  const bool wasInPicture = IsInPicture();
    458 
    459  nsImageLoadingContent::UnbindFromTree();
    460  nsGenericHTMLElement::UnbindFromTree(aContext);
    461 
    462  if (wasInPicture != IsInPicture()) {
    463    MOZ_ASSERT(wasInPicture);
    464    MOZ_ASSERT(aContext.IsUnbindRoot(this));
    465    MOZ_ASSERT(mInDocResponsiveContent);
    466    if (!HasAttr(nsGkAtoms::srcset)) {
    467      OwnerDoc()->RemoveResponsiveContent(this);
    468      mInDocResponsiveContent = false;
    469    }
    470    UpdateSourceSyncAndQueueImageTask(false, /* aNotify = */ false);
    471  }
    472 }
    473 
    474 void HTMLImageElement::UpdateFormOwner() {
    475  if (!mForm) {
    476    mForm = FindAncestorForm();
    477  }
    478 
    479  if (mForm && !HasFlag(ADDED_TO_FORM)) {
    480    // Now we need to add ourselves to the form
    481    nsAutoString nameVal, idVal;
    482    GetAttr(nsGkAtoms::name, nameVal);
    483    GetAttr(nsGkAtoms::id, idVal);
    484 
    485    SetFlags(ADDED_TO_FORM);
    486 
    487    mForm->AddImageElement(this);
    488 
    489    if (!nameVal.IsEmpty()) {
    490      mForm->AddImageElementToTable(this, nameVal);
    491    }
    492 
    493    if (!idVal.IsEmpty()) {
    494      mForm->AddImageElementToTable(this, idVal);
    495    }
    496  }
    497 }
    498 
    499 void HTMLImageElement::NodeInfoChanged(Document* aOldDoc) {
    500  nsGenericHTMLElement::NodeInfoChanged(aOldDoc);
    501 
    502  if (mInDocResponsiveContent) {
    503    aOldDoc->RemoveResponsiveContent(this);
    504    OwnerDoc()->AddResponsiveContent(this);
    505  }
    506 
    507  // Reparse the URI if needed. Note that we can't check whether we already have
    508  // a parsed URI, because it might be null even if we have a valid src
    509  // attribute, if we tried to parse with a different base.
    510  mSrcURI = nullptr;
    511  nsAutoString src;
    512  if (GetAttr(nsGkAtoms::src, src) && !src.IsEmpty()) {
    513    StringToURI(src, OwnerDoc(), getter_AddRefs(mSrcURI));
    514  }
    515 
    516  if (mLazyLoading) {
    517    aOldDoc->GetLazyLoadObserver()->Unobserve(*this);
    518    mLazyLoading = false;
    519    SetLazyLoading();
    520  }
    521 
    522  // Run selection algorithm synchronously and reload when an img element's
    523  // adopting steps are run, in order to react to changes in the environment,
    524  // per spec,
    525  // https://html.spec.whatwg.org/#reacting-to-dom-mutations, and
    526  // https://html.spec.whatwg.org/#reacting-to-environment-changes.
    527  UpdateSourceSyncAndQueueImageTask(true, /* aNotify = */ false);
    528 }
    529 
    530 // static
    531 already_AddRefed<HTMLImageElement> HTMLImageElement::Image(
    532    const GlobalObject& aGlobal, const Optional<uint32_t>& aWidth,
    533    const Optional<uint32_t>& aHeight, ErrorResult& aError) {
    534  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal.GetAsSupports());
    535  Document* doc;
    536  if (!win || !(doc = win->GetExtantDoc())) {
    537    aError.Throw(NS_ERROR_FAILURE);
    538    return nullptr;
    539  }
    540 
    541  RefPtr<mozilla::dom::NodeInfo> nodeInfo = doc->NodeInfoManager()->GetNodeInfo(
    542      nsGkAtoms::img, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
    543 
    544  auto* nim = nodeInfo->NodeInfoManager();
    545  RefPtr<HTMLImageElement> img = new (nim) HTMLImageElement(nodeInfo.forget());
    546 
    547  if (aWidth.WasPassed()) {
    548    img->SetWidth(aWidth.Value(), aError);
    549    if (aError.Failed()) {
    550      return nullptr;
    551    }
    552 
    553    if (aHeight.WasPassed()) {
    554      img->SetHeight(aHeight.Value(), aError);
    555      if (aError.Failed()) {
    556        return nullptr;
    557      }
    558    }
    559  }
    560 
    561  return img.forget();
    562 }
    563 
    564 uint32_t HTMLImageElement::Height() { return GetWidthHeightForImage().height; }
    565 
    566 uint32_t HTMLImageElement::Width() { return GetWidthHeightForImage().width; }
    567 
    568 nsresult HTMLImageElement::CopyInnerTo(HTMLImageElement* aDest) {
    569  MOZ_TRY(nsGenericHTMLElement::CopyInnerTo(aDest));
    570 
    571  // In SetAttr (called from nsGenericHTMLElement::CopyInnerTo), aDest skipped
    572  // doing the image load because we passed in false for aNotify.  But we
    573  // really do want it to do the load, so set it up to happen once the cloning
    574  // reaches a stable state.
    575  aDest->UpdateSourceSyncAndQueueImageTask(false, /* aNotify = */ false);
    576  return NS_OK;
    577 }
    578 
    579 CORSMode HTMLImageElement::GetCORSMode() {
    580  return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
    581 }
    582 
    583 JSObject* HTMLImageElement::WrapNode(JSContext* aCx,
    584                                     JS::Handle<JSObject*> aGivenProto) {
    585  return HTMLImageElement_Binding::Wrap(aCx, this, aGivenProto);
    586 }
    587 
    588 #ifdef DEBUG
    589 HTMLFormElement* HTMLImageElement::GetForm() const { return mForm; }
    590 #endif
    591 
    592 void HTMLImageElement::SetForm(HTMLFormElement* aForm) {
    593  MOZ_ASSERT(aForm, "Don't pass null here");
    594  NS_ASSERTION(!mForm,
    595               "We don't support switching from one non-null form to another.");
    596 
    597  mForm = aForm;
    598 }
    599 
    600 void HTMLImageElement::ClearForm(bool aRemoveFromForm) {
    601  NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM),
    602               "Form control should have had flag set correctly");
    603 
    604  if (!mForm) {
    605    return;
    606  }
    607 
    608  if (aRemoveFromForm) {
    609    nsAutoString nameVal, idVal;
    610    GetAttr(nsGkAtoms::name, nameVal);
    611    GetAttr(nsGkAtoms::id, idVal);
    612 
    613    mForm->RemoveImageElement(this);
    614 
    615    if (!nameVal.IsEmpty()) {
    616      mForm->RemoveImageElementFromTable(this, nameVal);
    617    }
    618 
    619    if (!idVal.IsEmpty()) {
    620      mForm->RemoveImageElementFromTable(this, idVal);
    621    }
    622  }
    623 
    624  UnsetFlags(ADDED_TO_FORM);
    625  mForm = nullptr;
    626 }
    627 
    628 // Roughly corresponds to https://html.spec.whatwg.org/#update-the-image-data
    629 void HTMLImageElement::UpdateSourceSyncAndQueueImageTask(
    630    bool aAlwaysLoad, bool aNotify, const HTMLSourceElement* aSkippedSource) {
    631  // Per spec, when updating the image data or reacting to environment
    632  // changes, we always run the full selection (including selecting the source
    633  // element and the best fit image from srcset) even if it doesn't directly
    634  // affect the source selection.
    635  //
    636  // However, in the spec of updating the image data, the selection of image
    637  // source URL is in the asynchronous part (i.e. in a microtask), and so this
    638  // doesn't guarantee that the image style is correct after we flush the style
    639  // synchronously. So here we update the responsive source synchronously always
    640  // to make sure the image source is always up-to-date after each DOM mutation.
    641  // Spec issue: https://github.com/whatwg/html/issues/8207.
    642  UpdateResponsiveSource(aSkippedSource);
    643 
    644  nsImageLoadingContent::QueueImageTask(mSrcURI, mSrcTriggeringPrincipal,
    645                                        HaveSrcsetOrInPicture(), aAlwaysLoad,
    646                                        aNotify);
    647 }
    648 
    649 bool HTMLImageElement::HaveSrcsetOrInPicture() const {
    650  return HasAttr(nsGkAtoms::srcset) || IsInPicture();
    651 }
    652 
    653 bool HTMLImageElement::SelectedSourceMatchesLast(nsIURI* aSelectedSource) {
    654  // If there was no selected source previously, we don't want to short-circuit
    655  // the load. Similarly for if there is no newly selected source.
    656  if (!mLastSelectedSource || !aSelectedSource) {
    657    return false;
    658  }
    659  bool equal = false;
    660  return NS_SUCCEEDED(mLastSelectedSource->Equals(aSelectedSource, &equal)) &&
    661         equal;
    662 }
    663 
    664 void HTMLImageElement::LoadSelectedImage(bool aAlwaysLoad,
    665                                         bool aStopLazyLoading) {
    666  // In responsive mode, we have to make sure we ran the full selection
    667  // algorithm before loading the selected image.
    668  // Use this assertion to catch any cases we missed.
    669  MOZ_ASSERT(!UpdateResponsiveSource(),
    670             "The image source should be the same because we update the "
    671             "responsive source synchronously");
    672 
    673  if (aStopLazyLoading) {
    674    StopLazyLoading(StartLoad::No);
    675  }
    676 
    677  // The density is default to 1.0 for the src attribute case.
    678  double currentDensity = mResponsiveSelector
    679                              ? mResponsiveSelector->GetSelectedImageDensity()
    680                              : 1.0;
    681 
    682  nsCOMPtr<nsIURI> selectedSource;
    683  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
    684  ImageLoadType type = eImageLoadType_Normal;
    685  bool hasSrc = false;
    686  if (mResponsiveSelector) {
    687    selectedSource = mResponsiveSelector->GetSelectedImageURL();
    688    triggeringPrincipal =
    689        mResponsiveSelector->GetSelectedImageTriggeringPrincipal();
    690    type = eImageLoadType_Imageset;
    691  } else if (mSrcURI || HasAttr(nsGkAtoms::src)) {
    692    hasSrc = true;
    693    if (mSrcURI) {
    694      selectedSource = mSrcURI;
    695      if (HaveSrcsetOrInPicture()) {
    696        // If we have a srcset attribute or are in a <picture> element, we
    697        // always use the Imageset load type, even if we parsed no valid
    698        // responsive sources from either, per spec.
    699        type = eImageLoadType_Imageset;
    700      }
    701      triggeringPrincipal = mSrcTriggeringPrincipal;
    702    }
    703  }
    704 
    705  if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource)) {
    706    // Update state when only density may have changed (i.e., the source to load
    707    // hasn't changed, and we don't do any request at all). We need (apart from
    708    // updating our internal state) to tell the image frame because its
    709    // intrinsic size may have changed.
    710    //
    711    // In the case we actually trigger a new load, that load will trigger a call
    712    // to nsImageFrame::NotifyNewCurrentRequest, which takes care of that for
    713    // us.
    714    SetDensity(currentDensity);
    715    // If we're (re-)loading a broken image, we might have just become broken
    716    // again.
    717    UpdateImageState(true);
    718    return;
    719  }
    720 
    721  if (mLazyLoading) {
    722    return;
    723  }
    724 
    725  nsresult rv = NS_ERROR_FAILURE;
    726 
    727  const bool kNotify = true;
    728  // src triggers an error event on invalid URI, unlike other loads.
    729  if (selectedSource || hasSrc) {
    730    // We can pass true for aForce because we already do a manual check for
    731    // SelectedSourceMatchesLast.
    732    rv = LoadImage(selectedSource, /* aForce = */ true, kNotify, type,
    733                   triggeringPrincipal);
    734  }
    735 
    736  mLastSelectedSource = selectedSource;
    737  mCurrentDensity = currentDensity;
    738 
    739  if (NS_FAILED(rv)) {
    740    CancelImageRequests(kNotify);
    741  }
    742 }
    743 
    744 void HTMLImageElement::PictureSourceSrcsetChanged(nsIContent* aSourceNode,
    745                                                  const nsAString& aNewValue,
    746                                                  bool aNotify) {
    747  MOZ_ASSERT(aSourceNode == this || IsPreviousSibling(aSourceNode, this),
    748             "Should not be getting notifications for non-previous-siblings");
    749 
    750  nsIContent* currentSrc =
    751      mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
    752 
    753  if (aSourceNode == currentSrc) {
    754    // We're currently using this node as our responsive selector
    755    // source.
    756    nsCOMPtr<nsIPrincipal> principal;
    757    if (aSourceNode == this) {
    758      principal = mSrcsetTriggeringPrincipal;
    759    } else if (auto* source = HTMLSourceElement::FromNode(aSourceNode)) {
    760      principal = source->GetSrcsetTriggeringPrincipal();
    761    }
    762    mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue, principal);
    763  }
    764 
    765  // This always triggers the image update steps per the spec, even if we are
    766  // not using this source.
    767  UpdateSourceSyncAndQueueImageTask(true, aNotify);
    768 }
    769 
    770 void HTMLImageElement::PictureSourceSizesChanged(nsIContent* aSourceNode,
    771                                                 const nsAString& aNewValue,
    772                                                 bool aNotify) {
    773  MOZ_ASSERT(aSourceNode == this || IsPreviousSibling(aSourceNode, this),
    774             "Should not be getting notifications for non-previous-siblings");
    775 
    776  nsIContent* currentSrc =
    777      mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
    778 
    779  if (aSourceNode == currentSrc) {
    780    // We're currently using this node as our responsive selector
    781    // source.
    782    mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
    783  }
    784 
    785  // This always triggers the image update steps per the spec, even if
    786  // we are not using this source.
    787  UpdateSourceSyncAndQueueImageTask(true, aNotify);
    788 }
    789 
    790 void HTMLImageElement::PictureSourceMediaOrTypeChanged(nsIContent* aSourceNode,
    791                                                       bool aNotify) {
    792  MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
    793             "Should not be getting notifications for non-previous-siblings");
    794 
    795  // This always triggers the image update steps per the spec, even if
    796  // we are not switching to/from this source
    797  UpdateSourceSyncAndQueueImageTask(true, aNotify);
    798 }
    799 
    800 void HTMLImageElement::PictureSourceDimensionChanged(
    801    HTMLSourceElement* aSourceNode, bool aNotify) {
    802  MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
    803             "Should not be getting notifications for non-previous-siblings");
    804 
    805  // "width" and "height" affect the dimension of images, but they don't have
    806  // impact on the selection of <source> elements. In other words,
    807  // UpdateResponsiveSource doesn't change the source, so all we need to do is
    808  // just request restyle.
    809  if (mResponsiveSelector && mResponsiveSelector->Content() == aSourceNode) {
    810    InvalidateAttributeMapping();
    811  }
    812 }
    813 
    814 void HTMLImageElement::PictureSourceAdded(bool aNotify,
    815                                          HTMLSourceElement* aSourceNode) {
    816  MOZ_ASSERT(!aSourceNode || IsPreviousSibling(aSourceNode, this),
    817             "Should not be getting notifications for non-previous-siblings");
    818 
    819  UpdateSourceSyncAndQueueImageTask(true, aNotify);
    820 }
    821 
    822 void HTMLImageElement::PictureSourceRemoved(bool aNotify,
    823                                            HTMLSourceElement* aSourceNode) {
    824  MOZ_ASSERT(!aSourceNode || IsPreviousSibling(aSourceNode, this),
    825             "Should not be getting notifications for non-previous-siblings");
    826  UpdateSourceSyncAndQueueImageTask(true, aNotify, aSourceNode);
    827 }
    828 
    829 bool HTMLImageElement::UpdateResponsiveSource(
    830    const HTMLSourceElement* aSkippedSource) {
    831  bool hadSelector = !!mResponsiveSelector;
    832 
    833  nsIContent* currentSource =
    834      mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
    835 
    836  // Walk source nodes previous to ourselves if IsInPicture().
    837  nsINode* candidateSource =
    838      IsInPicture() ? GetParentElement()->GetFirstChild() : this;
    839 
    840  // Initialize this as nullptr so we don't have to nullify it when runing out
    841  // of siblings without finding ourself, e.g. XBL magic.
    842  RefPtr<ResponsiveImageSelector> newResponsiveSelector = nullptr;
    843 
    844  for (; candidateSource; candidateSource = candidateSource->GetNextSibling()) {
    845    if (aSkippedSource == candidateSource) {
    846      continue;
    847    }
    848 
    849    if (candidateSource == currentSource) {
    850      // found no better source before current, re-run selection on
    851      // that and keep it if it's still usable.
    852      bool changed = mResponsiveSelector->SelectImage(true);
    853      if (mResponsiveSelector->NumCandidates()) {
    854        bool isUsableCandidate = true;
    855 
    856        // an otherwise-usable source element may still have a media query that
    857        // may not match any more.
    858        if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
    859            !SourceElementMatches(candidateSource->AsElement())) {
    860          isUsableCandidate = false;
    861        }
    862 
    863        if (isUsableCandidate) {
    864          // We are still using the current source, but the selected image may
    865          // be changed, so always set the density from the selected image.
    866          SetDensity(mResponsiveSelector->GetSelectedImageDensity());
    867          return changed;
    868        }
    869      }
    870 
    871      // no longer valid
    872      newResponsiveSelector = nullptr;
    873      if (candidateSource == this) {
    874        // No further possibilities
    875        break;
    876      }
    877    } else if (candidateSource == this) {
    878      // We are the last possible source
    879      newResponsiveSelector =
    880          TryCreateResponsiveSelector(candidateSource->AsElement());
    881      break;
    882    } else if (auto* source = HTMLSourceElement::FromNode(candidateSource)) {
    883      if (RefPtr<ResponsiveImageSelector> selector =
    884              TryCreateResponsiveSelector(source)) {
    885        newResponsiveSelector = selector.forget();
    886        // This led to a valid source, stop
    887        break;
    888      }
    889    }
    890  }
    891 
    892  // If we reach this point, either:
    893  // - there was no selector originally, and there is not one now
    894  // - there was no selector originally, and there is one now
    895  // - there was a selector, and there is a different one now
    896  // - there was a selector, and there is not one now
    897  SetResponsiveSelector(std::move(newResponsiveSelector));
    898  return hadSelector || mResponsiveSelector;
    899 }
    900 
    901 /*static */
    902 bool HTMLImageElement::SupportedPictureSourceType(const nsAString& aType) {
    903  nsAutoString type;
    904  nsAutoString params;
    905 
    906  nsContentUtils::SplitMimeType(aType, type, params);
    907  if (type.IsEmpty()) {
    908    return true;
    909  }
    910 
    911  return imgLoader::SupportImageWithMimeType(
    912      NS_ConvertUTF16toUTF8(type), AcceptedMimeTypes::IMAGES_AND_DOCUMENTS);
    913 }
    914 
    915 bool HTMLImageElement::SourceElementMatches(Element* aSourceElement) {
    916  MOZ_ASSERT(aSourceElement->IsHTMLElement(nsGkAtoms::source));
    917 
    918  MOZ_ASSERT(IsInPicture());
    919  MOZ_ASSERT(IsPreviousSibling(aSourceElement, this));
    920 
    921  // Check media and type
    922  auto* src = static_cast<HTMLSourceElement*>(aSourceElement);
    923  if (!src->MatchesCurrentMedia()) {
    924    return false;
    925  }
    926 
    927  nsAutoString type;
    928  return !src->GetAttr(nsGkAtoms::type, type) ||
    929         SupportedPictureSourceType(type);
    930 }
    931 
    932 already_AddRefed<ResponsiveImageSelector>
    933 HTMLImageElement::TryCreateResponsiveSelector(Element* aSourceElement) {
    934  nsCOMPtr<nsIPrincipal> principal;
    935 
    936  // Skip if this is not a <source> with matching media query
    937  bool isSourceTag = aSourceElement->IsHTMLElement(nsGkAtoms::source);
    938  if (isSourceTag) {
    939    if (!SourceElementMatches(aSourceElement)) {
    940      return nullptr;
    941    }
    942    auto* source = HTMLSourceElement::FromNode(aSourceElement);
    943    principal = source->GetSrcsetTriggeringPrincipal();
    944  } else if (aSourceElement->IsHTMLElement(nsGkAtoms::img)) {
    945    // Otherwise this is the <img> tag itself
    946    MOZ_ASSERT(aSourceElement == this);
    947    principal = mSrcsetTriggeringPrincipal;
    948  }
    949 
    950  // Skip if has no srcset or an empty srcset
    951  nsString srcset;
    952  if (!aSourceElement->GetAttr(nsGkAtoms::srcset, srcset)) {
    953    return nullptr;
    954  }
    955 
    956  if (srcset.IsEmpty()) {
    957    return nullptr;
    958  }
    959 
    960  // Try to parse
    961  RefPtr<ResponsiveImageSelector> sel =
    962      new ResponsiveImageSelector(aSourceElement);
    963  if (!sel->SetCandidatesFromSourceSet(srcset, principal)) {
    964    // No possible candidates, don't need to bother parsing sizes
    965    return nullptr;
    966  }
    967 
    968  nsAutoString sizes;
    969  aSourceElement->GetAttr(nsGkAtoms::sizes, sizes);
    970  sel->SetSizesFromDescriptor(sizes);
    971 
    972  // If this is the <img> tag, also pull in src as the default source
    973  if (!isSourceTag) {
    974    MOZ_ASSERT(aSourceElement == this);
    975    if (mSrcURI) {
    976      sel->SetDefaultSource(mSrcURI, mSrcTriggeringPrincipal);
    977    }
    978  }
    979 
    980  return sel.forget();
    981 }
    982 
    983 /* static */
    984 bool HTMLImageElement::SelectSourceForTagWithAttrs(
    985    Document* aDocument, bool aIsSourceTag, const nsAString& aSrcAttr,
    986    const nsAString& aSrcsetAttr, const nsAString& aSizesAttr,
    987    const nsAString& aTypeAttr, const nsAString& aMediaAttr,
    988    nsAString& aResult) {
    989  MOZ_ASSERT(aIsSourceTag || (aTypeAttr.IsEmpty() && aMediaAttr.IsEmpty()),
    990             "Passing type or media attrs makes no sense without aIsSourceTag");
    991  MOZ_ASSERT(!aIsSourceTag || aSrcAttr.IsEmpty(),
    992             "Passing aSrcAttr makes no sense with aIsSourceTag set");
    993 
    994  if (aSrcsetAttr.IsEmpty()) {
    995    if (!aIsSourceTag) {
    996      // For an <img> with no srcset, we would always select the src attr.
    997      aResult.Assign(aSrcAttr);
    998      return true;
    999    }
   1000    // Otherwise, a <source> without srcset is never selected
   1001    return false;
   1002  }
   1003 
   1004  // Would not consider source tags with unsupported media or type
   1005  if (aIsSourceTag &&
   1006      ((!aMediaAttr.IsVoid() && !HTMLSourceElement::WouldMatchMediaForDocument(
   1007                                    aMediaAttr, aDocument)) ||
   1008       (!aTypeAttr.IsVoid() && !SupportedPictureSourceType(aTypeAttr)))) {
   1009    return false;
   1010  }
   1011 
   1012  // Using srcset or picture <source>, build a responsive selector for this
   1013  // tag.
   1014  RefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(aDocument);
   1015 
   1016  sel->SetCandidatesFromSourceSet(aSrcsetAttr);
   1017  if (!aSizesAttr.IsEmpty()) {
   1018    sel->SetSizesFromDescriptor(aSizesAttr);
   1019  }
   1020  if (!aIsSourceTag) {
   1021    sel->SetDefaultSource(aSrcAttr);
   1022  }
   1023 
   1024  if (sel->GetSelectedImageURLSpec(aResult)) {
   1025    return true;
   1026  }
   1027 
   1028  if (!aIsSourceTag) {
   1029    // <img> tag with no match would definitively load nothing.
   1030    aResult.Truncate();
   1031    return true;
   1032  }
   1033 
   1034  // <source> tags with no match would leave source yet-undetermined.
   1035  return false;
   1036 }
   1037 
   1038 void HTMLImageElement::DestroyContent() {
   1039  // Clear the load task to avoid running LoadSelectedImage() after getting
   1040  // destroyed.
   1041  ClearImageLoadTask();
   1042 
   1043  mResponsiveSelector = nullptr;
   1044 
   1045  nsImageLoadingContent::Destroy();
   1046  nsGenericHTMLElement::DestroyContent();
   1047 }
   1048 
   1049 void HTMLImageElement::MediaFeatureValuesChanged() {
   1050  UpdateSourceSyncAndQueueImageTask(false, /* aNotify = */ true);
   1051 }
   1052 
   1053 void HTMLImageElement::SetLazyLoading() {
   1054  if (mLazyLoading) {
   1055    return;
   1056  }
   1057 
   1058  // If scripting is disabled don't do lazy load.
   1059  // https://whatpr.org/html/3752/images.html#updating-the-image-data
   1060  //
   1061  // Same for printing.
   1062  Document* doc = OwnerDoc();
   1063  if (!doc->IsScriptEnabled() || doc->IsStaticDocument()) {
   1064    return;
   1065  }
   1066 
   1067  doc->EnsureLazyLoadObserver().Observe(*this);
   1068  mLazyLoading = true;
   1069  UpdateImageState(true);
   1070 }
   1071 
   1072 void HTMLImageElement::StopLazyLoading(StartLoad aStartLoad) {
   1073  if (!mLazyLoading) {
   1074    return;
   1075  }
   1076  mLazyLoading = false;
   1077  Document* doc = OwnerDoc();
   1078  if (auto* obs = doc->GetLazyLoadObserver()) {
   1079    obs->Unobserve(*this);
   1080  }
   1081 
   1082  if (aStartLoad == StartLoad::Yes) {
   1083    UpdateSourceSyncAndQueueImageTask(true, /* aNotify = */ true);
   1084  }
   1085 }
   1086 
   1087 const StyleLockedDeclarationBlock*
   1088 HTMLImageElement::GetMappedAttributesFromSource() const {
   1089  if (!IsInPicture() || !mResponsiveSelector) {
   1090    return nullptr;
   1091  }
   1092 
   1093  const auto* source =
   1094      HTMLSourceElement::FromNodeOrNull(mResponsiveSelector->Content());
   1095  if (!source) {
   1096    return nullptr;
   1097  }
   1098 
   1099  MOZ_ASSERT(IsPreviousSibling(source, this),
   1100             "Incorrect or out-of-date source");
   1101  return source->GetAttributesMappedForImage();
   1102 }
   1103 
   1104 void HTMLImageElement::InvalidateAttributeMapping() {
   1105  if (!IsInPicture()) {
   1106    return;
   1107  }
   1108 
   1109  nsPresContext* presContext = nsContentUtils::GetContextForContent(this);
   1110  if (!presContext) {
   1111    return;
   1112  }
   1113 
   1114  // Note: Unfortunately, we have to use RESTYLE_SELF, instead of using
   1115  // RESTYLE_STYLE_ATTRIBUTE or other ways, to avoid re-selector-match because
   1116  // we are using Gecko_GetExtraContentStyleDeclarations() to retrieve the
   1117  // extra declaration block from |this|'s width and height attributes, and
   1118  // other restyle hints seems not enough.
   1119  // FIXME: We may refine this together with the restyle for presentation
   1120  // attributes in RestyleManger::AttributeChagned()
   1121  presContext->RestyleManager()->PostRestyleEvent(
   1122      this, RestyleHint::RESTYLE_SELF, nsChangeHint(0));
   1123 }
   1124 
   1125 void HTMLImageElement::SetResponsiveSelector(
   1126    RefPtr<ResponsiveImageSelector>&& aSource) {
   1127  if (mResponsiveSelector == aSource) {
   1128    return;
   1129  }
   1130 
   1131  mResponsiveSelector = std::move(aSource);
   1132 
   1133  // Invalidate the style if needed.
   1134  InvalidateAttributeMapping();
   1135 
   1136  // Update density.
   1137  SetDensity(mResponsiveSelector
   1138                 ? mResponsiveSelector->GetSelectedImageDensity()
   1139                 : 1.0);
   1140 }
   1141 
   1142 void HTMLImageElement::SetDensity(double aDensity) {
   1143  if (mCurrentDensity == aDensity) {
   1144    return;
   1145  }
   1146 
   1147  mCurrentDensity = aDensity;
   1148 
   1149  // Invalidate the reflow.
   1150  if (nsImageFrame* f = do_QueryFrame(GetPrimaryFrame())) {
   1151    f->ResponsiveContentDensityChanged();
   1152  }
   1153 }
   1154 
   1155 FetchPriority HTMLImageElement::GetFetchPriorityForImage() const {
   1156  return Element::GetFetchPriority();
   1157 }
   1158 
   1159 void HTMLImageElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
   1160                                              size_t* aNodeSize) const {
   1161  nsGenericHTMLElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
   1162 
   1163  // It is okay to include the size of mSrcURI here even though it might have
   1164  // strong references from elsewhere because the URI was created for this
   1165  // object, in nsImageLoadingContent::StringToURI(). Only objects that created
   1166  // their own URI will call nsIURI::SizeOfIncludingThis().
   1167  if (mSrcURI) {
   1168    *aNodeSize += mSrcURI->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
   1169  }
   1170 }
   1171 
   1172 }  // namespace mozilla::dom