void
HTMLImageElement::PictureSourceSizesChanged(nsIContent *aSourceNode,
                                            const nsAString& aNewValue,
                                            bool aNotify)
{
  if (!HTMLPictureElement::IsPictureEnabled()) {
    return;
  }

  MOZ_ASSERT(aSourceNode == this ||
             IsPreviousSibling(aSourceNode, this),
             "Should not be getting notifications for non-previous-siblings");

  nsIContent *currentSrc =
    mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;

  if (aSourceNode == currentSrc) {
    // We're currently using this node as our responsive selector
    // source.
    mResponsiveSelector->SetSizesFromDescriptor(aNewValue);
  }

  // This always triggers the image update steps per the spec, even if
  // we are not using this source.
  QueueImageLoadTask(true);
}
void
HTMLImageElement::PictureSourceMediaOrTypeChanged(nsIContent *aSourceNode,
                                                  bool aNotify)
{
  if (!HTMLPictureElement::IsPictureEnabled()) {
    return;
  }

  MOZ_ASSERT(IsPreviousSibling(aSourceNode, this),
             "Should not be getting notifications for non-previous-siblings");

  // This always triggers the image update steps per the spec, even if
  // we are not switching to/from this source
  QueueImageLoadTask(true);
}
void
HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
                                             const nsAString& aNewValue,
                                             bool aNotify)
{
  bool isSelf = aSourceNode == this;

  if (!IsSrcsetEnabled() ||
      (!isSelf && !HTMLPictureElement::IsPictureEnabled())) {
    return;
  }

  MOZ_ASSERT(isSelf || IsPreviousSibling(aSourceNode, this),
             "Should not be getting notifications for non-previous-siblings");

  nsIContent *currentSrc =
    mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;

  if (aSourceNode == currentSrc) {
    // We're currently using this node as our responsive selector
    // source.
    mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
  }

  if (!mInDocResponsiveContent) {
    nsIDocument* doc = GetOurOwnerDoc();
    if (doc) {
      doc->AddResponsiveContent(this);
      mInDocResponsiveContent = true;
    }
  }

  // This always triggers the image update steps per the spec, even if
  // we are not using this source.
  QueueImageLoadTask(true);
}
bool
HTMLImageElement::SourceElementMatches(nsIContent* aSourceNode)
{
  MOZ_ASSERT(aSourceNode->IsHTMLElement(nsGkAtoms::source));

  DebugOnly<Element *> parent(nsINode::GetParentElement());
  MOZ_ASSERT(parent && parent->IsHTMLElement(nsGkAtoms::picture));
  MOZ_ASSERT(IsPreviousSibling(aSourceNode, this));
  MOZ_ASSERT(HTMLPictureElement::IsPictureEnabled());

  // Check media and type
  HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
  if (!src->MatchesCurrentMedia()) {
    return false;
  }

  nsAutoString type;
  if (aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
      !SupportedPictureSourceType(type)) {
    return false;
  }

  return true;
}
bool
HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
                                              const nsAString *aSrcset,
                                              const nsAString *aSizes)
{
  if (!IsSrcsetEnabled()) {
    return false;
  }

  bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
  // Skip if this is not a <source> with matching media query
  bool isSourceTag = aSourceNode->Tag() == nsGkAtoms::source;
  if (isSourceTag) {
    DebugOnly<nsINode *> parent(nsINode::GetParentNode());
    MOZ_ASSERT(parent && parent->Tag() == nsGkAtoms::picture);
    MOZ_ASSERT(IsPreviousSibling(aSourceNode, this));
    MOZ_ASSERT(pictureEnabled);

    // Check media and type
    HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
    if (!src->MatchesCurrentMedia()) {
      return false;
    }

    nsAutoString type;
    if (aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
        !imgLoader::SupportImageWithMimeType(
          NS_ConvertUTF16toUTF8(type).get(),
          AcceptedMimeTypes::IMAGES_AND_DOCUMENTS)
        ) {
      return false;
    }
  } else if (aSourceNode->Tag() == nsGkAtoms::img) {
    // Otherwise this is the <img> tag itself
    MOZ_ASSERT(aSourceNode == this);
  }

  // Skip if has no srcset or an empty srcset
  nsString srcset;
  if (aSrcset) {
    srcset = *aSrcset;
  } else if (!aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::srcset,
                                   srcset)) {
    return false;
  }

  if (srcset.IsEmpty()) {
    return false;
  }


  // Try to parse
  nsRefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(aSourceNode);
  if (!sel->SetCandidatesFromSourceSet(srcset)) {
    // No possible candidates, don't need to bother parsing sizes
    return false;
  }

  if (pictureEnabled && aSizes) {
    sel->SetSizesFromDescriptor(*aSizes);
  } else if (pictureEnabled) {
    nsAutoString sizes;
    aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::sizes, sizes);
    sel->SetSizesFromDescriptor(sizes);
  }

  // If this is the <img> tag, also pull in src as the default source
  if (!isSourceTag) {
    MOZ_ASSERT(aSourceNode == this);
    nsAutoString src;
    if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && !src.IsEmpty()) {
      sel->SetDefaultSource(src);
    }
  }

  mResponsiveSelector = sel;
  return true;
}