예제 #1
0
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);
  }

  // This always triggers the image update steps per the spec, even if
  // we are not using this source.
  QueueImageLoadTask();
}
예제 #2
0
void
HTMLImageElement::UpdateResponsiveSource()
{
  if (!IsSrcsetEnabled()) {
    mResponsiveSelector = nullptr;
    return;
  }

  nsIContent *currentSource =
    mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
  bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
  nsINode *parent = pictureEnabled ? this->nsINode::GetParentNode() : nullptr;

  nsINode *candidateSource = nullptr;
  if (parent && parent->Tag() == nsGkAtoms::picture) {
    // Walk source nodes previous to ourselves
    candidateSource = parent->GetFirstChild();
  } else {
    candidateSource = this;
  }

  while (candidateSource) {
    if (candidateSource == currentSource) {
      // found no better source before current, re-run selection on
      // that and keep it if it's still usable.
      mResponsiveSelector->SelectImage(true);
      if (mResponsiveSelector->NumCandidates()) {
        break;
      }

      // no longer valid
      mResponsiveSelector = nullptr;
      if (candidateSource == this) {
        // No further possibilities
        break;
      }
    } else if (candidateSource == this) {
      // We are the last possible source
      if (!TryCreateResponsiveSelector(candidateSource->AsContent())) {
        // Failed to find any source
        mResponsiveSelector = nullptr;
      }
      break;
    } else if (candidateSource->Tag() == nsGkAtoms::source &&
               TryCreateResponsiveSelector(candidateSource->AsContent())) {
      // This led to a valid source, stop
      break;
    }
    candidateSource = candidateSource->GetNextSibling();
  }

  if (!candidateSource) {
    // Ran out of siblings without finding ourself, e.g. XBL magic.
    mResponsiveSelector = nullptr;
  }
}
예제 #3
0
bool
HTMLImageElement::HaveSrcsetOrInPicture()
{
  if (IsSrcsetEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::srcset)) {
    return true;
  }

  if (!HTMLPictureElement::IsPictureEnabled()) {
    return false;
  }

  Element *parent = nsINode::GetParentElement();
  return (parent && parent->IsHTMLElement(nsGkAtoms::picture));
}
예제 #4
0
nsresult
HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                               const nsAttrValue* aValue, bool aNotify)
{
  if (aNameSpaceID == kNameSpaceID_None && mForm &&
      (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
      aValue && !aValue->IsEmptyString()) {
    // add the image to the hashtable as needed
    NS_ABORT_IF_FALSE(aValue->Type() == nsAttrValue::eAtom,
      "Expected atom value for name/id");
    mForm->AddImageElementToTable(this,
      nsDependentAtomString(aValue->GetAtomValue()));
  }

  // Handle src/srcset/crossorigin updates. If aNotify is false, we are coming
  // from the parser or some such place; we'll get bound after all the
  // attributes have been set, so we'll do the image load from BindToTree.
  if (aName == nsGkAtoms::src &&
      aNameSpaceID == kNameSpaceID_None) {
    // SetAttr handles setting src in the non-responsive case, so only handle it
    // for responsive mode or unsetting
    if (!aValue) {
      CancelImageRequests(aNotify);
    } else if (mResponsiveSelector) {
      mResponsiveSelector->SetDefaultSource(aValue ? aValue->GetStringValue()
                                                   : EmptyString());
      LoadSelectedImage(false, aNotify);
    }
  } else if (aName == nsGkAtoms::srcset &&
             aNameSpaceID == kNameSpaceID_None &&
             aNotify &&
             AsContent()->IsInDoc() &&
             IsSrcsetEnabled()) {
    // We currently don't handle responsive mode until BindToTree
    UpdateSourceSet(aValue->GetStringValue());
    LoadSelectedImage(false, aNotify);
  } else if (aName == nsGkAtoms::crossorigin &&
             aNameSpaceID == kNameSpaceID_None &&
             aNotify) {
    // We want aForce == true in this LoadImage call, because we want to force
    // a new load of the image with the new cross origin policy.
    nsCOMPtr<nsIURI> currentURI;
    if (NS_SUCCEEDED(GetCurrentURI(getter_AddRefs(currentURI))) && currentURI) {
      LoadImage(currentURI, true, aNotify);
    }
  }

  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
                                            aValue, aNotify);
}
예제 #5
0
nsresult
HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
{
  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                 aBindingParent,
                                                 aCompileEventHandlers);
  NS_ENSURE_SUCCESS(rv, rv);

  nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
                                    aCompileEventHandlers);

  if (aParent) {
    UpdateFormOwner();
  }

  bool haveSrcset = IsSrcsetEnabled() &&
                    HasAttr(kNameSpaceID_None, nsGkAtoms::srcset);
  if (haveSrcset || HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
    // FIXME: Bug 660963 it would be nice if we could just have
    // ClearBrokenState update our state and do it fast...
    ClearBrokenState();
    RemoveStatesSilently(NS_EVENT_STATE_BROKEN);

    // We don't handle responsive changes when not bound to a tree, update them
    // now if necessary
    if (haveSrcset) {
      nsAutoString srcset;
      GetAttr(kNameSpaceID_None, nsGkAtoms::srcset, srcset);
      UpdateSourceSet(srcset);
      if (mResponsiveSelector) {
        nsAutoString src;
        GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
        mResponsiveSelector->SetDefaultSource(src);
      }
    }

    // If loading is temporarily disabled, don't even launch MaybeLoadImage.
    // Otherwise MaybeLoadImage may run later when someone has reenabled
    // loading.
    if (LoadingEnabled()) {
      nsContentUtils::AddScriptRunner(
        NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
    }
  }

  return rv;
}
예제 #6
0
void
HTMLImageElement::UpdateSourceSet(const nsAString & aSrcset)
{
  MOZ_ASSERT(IsSrcsetEnabled());

  bool haveSrcset = !aSrcset.IsEmpty();

  if (haveSrcset && !mResponsiveSelector) {
    mResponsiveSelector = new ResponsiveImageSelector(this);
    mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset);

    // src may have been set before we decided we were responsive
    nsAutoString src;
    if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && src.Length()) {
      mResponsiveSelector->SetDefaultSource(src);
    }

  } else if (haveSrcset) {
    mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset);
  } else if (mResponsiveSelector) {
    // Clearing srcset, don't need responsive selector anymore
    mResponsiveSelector = nullptr;
  }
}
예제 #7
0
nsresult
HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                               const nsAttrValue* aValue, bool aNotify)
{
  if (aNameSpaceID == kNameSpaceID_None && mForm &&
      (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
      aValue && !aValue->IsEmptyString()) {
    // add the image to the hashtable as needed
    MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom,
               "Expected atom value for name/id");
    mForm->AddImageElementToTable(this,
      nsDependentAtomString(aValue->GetAtomValue()));
  }

  // Handle src/srcset/crossorigin updates. If aNotify is false, we are coming
  // from the parser or some such place; we'll get bound after all the
  // attributes have been set, so we'll do the image load from BindToTree.

  nsAttrValueOrString attrVal(aValue);

  if (aName == nsGkAtoms::src &&
      aNameSpaceID == kNameSpaceID_None &&
      !aValue) {
    // SetAttr handles setting src since it needs to catch img.src =
    // img.src, so we only need to handle the unset case
    if (InResponsiveMode()) {
      if (mResponsiveSelector &&
          mResponsiveSelector->Content() == this) {
        mResponsiveSelector->SetDefaultSource(NullString());
      }
      QueueImageLoadTask(true);
    } else {
      // Bug 1076583 - We still behave synchronously in the non-responsive case
      CancelImageRequests(aNotify);
    }
  } else if (aName == nsGkAtoms::srcset &&
             aNameSpaceID == kNameSpaceID_None &&
             IsSrcsetEnabled()) {
    PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
  } else if (aName == nsGkAtoms::sizes &&
             aNameSpaceID == kNameSpaceID_None &&
             HTMLPictureElement::IsPictureEnabled()) {
    PictureSourceSizesChanged(this, attrVal.String(), aNotify);
  } else if (aName == nsGkAtoms::crossorigin &&
             aNameSpaceID == kNameSpaceID_None &&
             aNotify) {
    // Force a new load of the image with the new cross origin policy.
    if (InResponsiveMode()) {
      // per spec, full selection runs when this changes, even though
      // it doesn't directly affect the source selection
      QueueImageLoadTask(true);
    } else {
      // Bug 1076583 - We still use the older synchronous algorithm in
      // non-responsive mode.  Force a new load of the image with the
      // new cross origin policy.
      ForceReload(aNotify);
    }
  }

  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
                                            aValue, aNotify);
}
예제 #8
0
/* static */ bool
HTMLImageElement::SelectSourceForTagWithAttrs(nsIDocument *aDocument,
                                              bool aIsSourceTag,
                                              const nsAString& aSrcAttr,
                                              const nsAString& aSrcsetAttr,
                                              const nsAString& aSizesAttr,
                                              const nsAString& aTypeAttr,
                                              const nsAString& aMediaAttr,
                                              nsAString& aResult)
{
  MOZ_ASSERT(aIsSourceTag || (aTypeAttr.IsEmpty() && aMediaAttr.IsEmpty()),
             "Passing type or media attrs makes no sense without aIsSourceTag");
  MOZ_ASSERT(!aIsSourceTag || aSrcAttr.IsEmpty(),
             "Passing aSrcAttr makes no sense with aIsSourceTag set");

  bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
  if (aIsSourceTag && !pictureEnabled) {
    return false;
  }

  if (!IsSrcsetEnabled() || aSrcsetAttr.IsEmpty()) {
    if (!aIsSourceTag) {
      // For an <img> with no srcset, we would always select the src attr.
      aResult.Assign(aSrcAttr);
      return true;
    }
    // Otherwise, a <source> without srcset is never selected
    return false;
  }

  // Would not consider source tags with unsupported media or type
  if (aIsSourceTag &&
      ((!aMediaAttr.IsVoid() &&
       !HTMLSourceElement::WouldMatchMediaForDocument(aMediaAttr, aDocument)) ||
      (!aTypeAttr.IsVoid() &&
       !SupportedPictureSourceType(aTypeAttr)))) {
    return false;
  }

  // Using srcset or picture <source>, build a responsive selector for this tag.
  RefPtr<ResponsiveImageSelector> sel =
    new ResponsiveImageSelector(aDocument);

  sel->SetCandidatesFromSourceSet(aSrcsetAttr);
  if (pictureEnabled && !aSizesAttr.IsEmpty()) {
    sel->SetSizesFromDescriptor(aSizesAttr);
  }
  if (!aIsSourceTag) {
    sel->SetDefaultSource(aSrcAttr);
  }

  if (sel->GetSelectedImageURLSpec(aResult)) {
    return true;
  }

  if (!aIsSourceTag) {
    // <img> tag with no match would definitively load nothing.
    aResult.Truncate();
    return true;
  }

  // <source> tags with no match would leave source yet-undetermined.
  return false;
}
예제 #9
0
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->IsHTMLElement(nsGkAtoms::source);
  if (isSourceTag) {
    if (!SourceElementMatches(aSourceNode)) {
      return false;
    }
  } else if (aSourceNode->IsHTMLElement(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
  RefPtr<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;
}
예제 #10
0
bool
HTMLImageElement::UpdateResponsiveSource()
{
  bool hadSelector = !!mResponsiveSelector;

  if (!IsSrcsetEnabled()) {
    mResponsiveSelector = nullptr;
    return hadSelector;
  }

  nsIContent *currentSource =
    mResponsiveSelector ? mResponsiveSelector->Content() : nullptr;
  bool pictureEnabled = HTMLPictureElement::IsPictureEnabled();
  Element *parent = pictureEnabled ? nsINode::GetParentElement() : nullptr;

  nsINode *candidateSource = nullptr;
  if (parent && parent->IsHTMLElement(nsGkAtoms::picture)) {
    // Walk source nodes previous to ourselves
    candidateSource = parent->GetFirstChild();
  } else {
    candidateSource = this;
  }

  while (candidateSource) {
    if (candidateSource == currentSource) {
      // found no better source before current, re-run selection on
      // that and keep it if it's still usable.
      bool changed = mResponsiveSelector->SelectImage(true);
      if (mResponsiveSelector->NumCandidates()) {
        bool isUsableCandidate = true;

        // an otherwise-usable source element may still have a media query that may not
        // match any more.
        if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
            !SourceElementMatches(candidateSource->AsContent())) {
          isUsableCandidate = false;
        }

        if (isUsableCandidate) {
          return changed;
        }
      }

      // no longer valid
      mResponsiveSelector = nullptr;
      if (candidateSource == this) {
        // No further possibilities
        break;
      }
    } else if (candidateSource == this) {
      // We are the last possible source
      if (!TryCreateResponsiveSelector(candidateSource->AsContent())) {
        // Failed to find any source
        mResponsiveSelector = nullptr;
      }
      break;
    } else if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
               TryCreateResponsiveSelector(candidateSource->AsContent())) {
      // This led to a valid source, stop
      break;
    }
    candidateSource = candidateSource->GetNextSibling();
  }

  if (!candidateSource) {
    // Ran out of siblings without finding ourself, e.g. XBL magic.
    mResponsiveSelector = nullptr;
  }

  return !hadSelector || mResponsiveSelector;
}
예제 #11
0
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;
}