Commit 35e5ee10 authored by Masayuki Nakano's avatar Masayuki Nakano
Browse files

Bug 1803044 - part 8: Refactor the bottom half of `HTMLEditor::RemoveStyleInside` r=m_kato

Now, we can make there simpler because the part does one of them:
* Replaces element with new `<span>`
* Removes element
* Removes attribute
* Does nothing

However, there are duplicated code.  We should consider what there does with
lambdas.

Depends on D164002

Differential Revision: https://phabricator.services.mozilla.com/D164003
parent 3ed70dcc
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -122,6 +122,11 @@ bool EditorInlineStyle::IsRepresentedBy(const nsIContent& aContent) const {
       HTMLEditUtils::IsNamedAnchor(&element))) {
    return true;
  }
  // If the style is font size, it's also represented by <big> or <small>.
  if (mHTMLProperty == nsGkAtoms::font && mAttribute == nsGkAtoms::size &&
      aContent.IsAnyOfHTMLElements(nsGkAtoms::big, nsGkAtoms::small)) {
    return true;
  }
  return false;
}

+106 −107
Original line number Diff line number Diff line
@@ -1672,25 +1672,34 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::RemoveStyleInside(

  // Then, if we could and should remove or replace aElement, let's do it. Or
  // just remove attribute.
  auto ShouldRemoveHTMLStyle = [&]() {
    if (!aStyleToRemove.IsStyleToClearAllInlineStyles()) {
      return aStyleToRemove.IsRepresentedBy(aElement);
  if (aStyleToRemove.IsStyleToClearAllInlineStyles() &&
      !HTMLEditUtils::IsRemovableInlineStyleElement(aElement)) {
    return pointToPutCaret;
  }
    // or removing all styles and the element is a presentation element.
    return HTMLEditUtils::IsRemovableInlineStyleElement(aElement);
  };

  if (ShouldRemoveHTMLStyle()) {
    // If aStyleToRemove.mAttribute is nullptr, we want to remove any matching
    // inline styles entirely.
    if (!aStyleToRemove.mAttribute) {
  const bool isStyleRepresentedByElement =
      !aStyleToRemove.IsStyleToClearAllInlineStyles() &&
      aStyleToRemove.IsRepresentedBy(aElement);

  // If the element is not a <span> and still has some attributes, we should
  // replace it with new <span>.
  auto ReplaceWithNewSpan = [&]() {
    if (aStyleToRemove.IsStyleToClearAllInlineStyles()) {
      return false;  // Remove it even if it has attributes.
    }
    // If some style rules are specified to aElement, we need to keep them
    // as far as possible.
    // XXX Why don't we clone `id` attribute?
      if (!aStyleToRemove.IsStyleToClearAllInlineStyles() &&
          aSpecifiedStyle != SpecifiedStyle::Discard &&
          (aElement.HasAttr(kNameSpaceID_None, nsGkAtoms::style) ||
           aElement.HasAttr(kNameSpaceID_None, nsGkAtoms::_class))) {
    if (isStyleRepresentedByElement && !aStyleToRemove.mAttribute &&
        aSpecifiedStyle == SpecifiedStyle::Preserve &&
        (aElement.HasNonEmptyAttr(nsGkAtoms::style) ||
         aElement.HasNonEmptyAttr(nsGkAtoms::_class))) {
      return true;  // Preserve `style` and `class` attributes with new <span>
    }
    return false;
  };

  if (ReplaceWithNewSpan()) {
    // Move `style` attribute and `class` element to span element before
    // removing aElement from the tree.
    Result<CreateElementResult, nsresult> replaceWithSpanResult =
@@ -1708,15 +1717,14 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::RemoveStyleInside(
        pointToPutCaret, {SuggestCaret::OnlyIfHasSuggestion});
    const RefPtr<Element> spanElement =
        unwrappedReplaceWithSpanResult.UnwrapNewNode();
        nsresult rv = CloneAttributeWithTransaction(*nsGkAtoms::style,
                                                    *spanElement, aElement);
    nsresult rv = CloneAttributeWithTransaction(*nsGkAtoms::style, *spanElement,
                                                aElement);
    if (NS_WARN_IF(Destroyed())) {
      return Err(NS_ERROR_EDITOR_DESTROYED);
    }
    if (NS_FAILED(rv)) {
      NS_WARNING(
              "EditorBase::CloneAttributeWithTransaction(nsGkAtoms::style) "
              "failed");
          "EditorBase::CloneAttributeWithTransaction(nsGkAtoms::style) failed");
      return Err(rv);
    }
    rv = CloneAttributeWithTransaction(*nsGkAtoms::_class, *spanElement,
@@ -1730,71 +1738,62 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::RemoveStyleInside(
          "failed");
      return Err(rv);
    }
      } else {
        Result<EditorDOMPoint, nsresult> unwrapElementResult =
            RemoveContainerWithTransaction(aElement);
        if (MOZ_UNLIKELY(unwrapElementResult.isErr())) {
          NS_WARNING("HTMLEditor::RemoveContainerWithTransaction() failed");
          return unwrapElementResult.propagateErr();
        }
        if (AllowsTransactionsToChangeSelection() &&
            unwrapElementResult.inspect().IsSet()) {
          pointToPutCaret = unwrapElementResult.unwrap();
    return pointToPutCaret;
  }

  auto RemoveElement = [&]() {
    if (aStyleToRemove.IsStyleToClearAllInlineStyles()) {
      MOZ_ASSERT(HTMLEditUtils::IsRemovableInlineStyleElement(aElement));
      return true;
    }
    // If aStyleToRemove.mAttribute is nullptr, we want to remove any matching
    // inline styles entirely.
    if (isStyleRepresentedByElement && !aStyleToRemove.mAttribute) {
      return true;
    }
    // If aStyleToRemove.mAttribute is specified, we want to remove only the
    // attribute unless it's the last attribute of aElement.
    else if (aElement.HasAttr(kNameSpaceID_None, aStyleToRemove.mAttribute)) {
      if (!HTMLEditUtils::ElementHasAttributeExcept(
              aElement, *aStyleToRemove.mAttribute)) {
    if (isStyleRepresentedByElement &&
        aElement.HasAttr(kNameSpaceID_None, aStyleToRemove.mAttribute) &&
        !HTMLEditUtils::ElementHasAttributeExcept(aElement,
                                                  *aStyleToRemove.mAttribute)) {
      return true;
    }
    // If we've removed a CSS style and that made the <span> element have no
    // attributes, we can delete it.
    if (styleSpecified &&
        aElement.IsAnyOfHTMLElements(nsGkAtoms::span, nsGkAtoms::font) &&
        !HTMLEditUtils::ElementHasAttribute(aElement)) {
      return true;
    }
    return false;
  };

  if (RemoveElement()) {
    Result<EditorDOMPoint, nsresult> unwrapElementResult =
        RemoveContainerWithTransaction(aElement);
    if (MOZ_UNLIKELY(unwrapElementResult.isErr())) {
      NS_WARNING("HTMLEditor::RemoveContainerWithTransaction() failed");
      return unwrapElementResult.propagateErr();
    }
        if (unwrapElementResult.inspect().IsSet()) {
    if (AllowsTransactionsToChangeSelection() &&
        unwrapElementResult.inspect().IsSet()) {
      pointToPutCaret = unwrapElementResult.unwrap();
    }
      } else {
        nsresult rv = RemoveAttributeWithTransaction(
            aElement, *aStyleToRemove.mAttribute);
    return pointToPutCaret;
  }

  if (isStyleRepresentedByElement && aStyleToRemove.mAttribute) {
    nsresult rv =
        RemoveAttributeWithTransaction(aElement, *aStyleToRemove.mAttribute);
    if (NS_FAILED(rv)) {
      NS_WARNING("EditorBase::RemoveAttributeWithTransaction() failed");
      return Err(rv);
    }
  }
    }
  }

  // If we've removed a CSS style and that made the <span> element have no
  // attributes, we can delete it.
  if (styleSpecified &&
      aElement.IsAnyOfHTMLElements(nsGkAtoms::span, nsGkAtoms::font) &&
      !HTMLEditUtils::ElementHasAttribute(aElement)) {
    Result<EditorDOMPoint, nsresult> unwrapSpanElement =
        RemoveContainerWithTransaction(aElement);
    NS_WARNING_ASSERTION(unwrapSpanElement.isOk(),
                         "HTMLEditor::RemoveContainerWithTransaction() failed");
    return unwrapSpanElement;
  }

  if (aStyleToRemove.mHTMLProperty != nsGkAtoms::font ||
      aStyleToRemove.mAttribute != nsGkAtoms::size ||
      !aElement.IsAnyOfHTMLElements(nsGkAtoms::big, nsGkAtoms::small)) {
  return pointToPutCaret;
}

  // Finally, remove aElement if it's a `<big>` or `<small>` element and
  // we're removing `<font size>`.
  Result<EditorDOMPoint, nsresult> unwrapBigOrSmallElementResult =
      RemoveContainerWithTransaction(aElement);
  NS_WARNING_ASSERTION(unwrapBigOrSmallElementResult.isOk(),
                       "HTMLEditor::RemoveContainerWithTransaction() failed");
  return unwrapBigOrSmallElementResult;
}

nsresult HTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange) {
  // We assume that <a> is not nested.
  // XXX Shouldn't ignore the editing host.