Bug 1627175 - part 55: Move `HTMLEditor::IsEmptyInlineNode()` and `HTMLEditor::IsEmptyOneHardLine()` to `HTMLEditUtils` r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D115164
This commit is contained in:
Masayuki Nakano 2021-05-17 08:38:55 +00:00
Родитель 38e52dd665
Коммит b6ea40871b
7 изменённых файлов: 80 добавлений и 80 удалений

Просмотреть файл

@ -85,8 +85,8 @@ class EditorDOMPointBase final {
: mParent(nullptr), mChild(nullptr), mIsChildInitialized(false) {} : mParent(nullptr), mChild(nullptr), mIsChildInitialized(false) {}
template <typename ContainerType> template <typename ContainerType>
EditorDOMPointBase(ContainerType* aContainer, int32_t aOffset) EditorDOMPointBase(const ContainerType* aContainer, int32_t aOffset)
: mParent(aContainer), : mParent(const_cast<ContainerType*>(aContainer)),
mChild(nullptr), mChild(nullptr),
mOffset(mozilla::Some(aOffset)), mOffset(mozilla::Some(aOffset)),
mIsChildInitialized(false) { mIsChildInitialized(false) {

Просмотреть файл

@ -2527,7 +2527,8 @@ EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
bool bOnlyBreaks = true; bool bOnlyBreaks = true;
for (auto& content : arrayOfContents) { for (auto& content : arrayOfContents) {
// if content is not a Break or empty inline, we're done // if content is not a Break or empty inline, we're done
if (!content->IsHTMLElement(nsGkAtoms::br) && !IsEmptyInlineNode(content)) { if (!content->IsHTMLElement(nsGkAtoms::br) &&
!HTMLEditUtils::IsEmptyInlineContent(content)) {
bOnlyBreaks = false; bOnlyBreaks = false;
break; break;
} }
@ -2647,7 +2648,8 @@ EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
// list item element. // list item element.
// If current node is an empty inline node, just delete it. // If current node is an empty inline node, just delete it.
if (EditorUtils::IsEditableContent(content, EditorType::HTML) && if (EditorUtils::IsEditableContent(content, EditorType::HTML) &&
(content->IsHTMLElement(nsGkAtoms::br) || IsEmptyInlineNode(content))) { (content->IsHTMLElement(nsGkAtoms::br) ||
HTMLEditUtils::IsEmptyInlineContent(content))) {
nsresult rv = DeleteNodeWithTransaction(*content); nsresult rv = DeleteNodeWithTransaction(*content);
if (NS_WARN_IF(Destroyed())) { if (NS_WARN_IF(Destroyed())) {
return EditActionResult(NS_ERROR_EDITOR_DESTROYED); return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
@ -3097,7 +3099,7 @@ nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
// If there is no visible and editable nodes in the edit targets, make an // If there is no visible and editable nodes in the edit targets, make an
// empty block. // empty block.
// XXX Isn't this odd if there are only non-editable visible nodes? // XXX Isn't this odd if there are only non-editable visible nodes?
if (IsEmptyOneHardLine(arrayOfContents)) { if (HTMLEditUtils::IsEmptyOneHardLine(arrayOfContents)) {
const nsRange* firstRange = SelectionRef().GetRangeAt(0); const nsRange* firstRange = SelectionRef().GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) { if (NS_WARN_IF(!firstRange)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -3555,7 +3557,7 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
// If there is no visible and editable nodes in the edit targets, make an // If there is no visible and editable nodes in the edit targets, make an
// empty block. // empty block.
// XXX Isn't this odd if there are only non-editable visible nodes? // XXX Isn't this odd if there are only non-editable visible nodes?
if (IsEmptyOneHardLine(arrayOfContents)) { if (HTMLEditUtils::IsEmptyOneHardLine(arrayOfContents)) {
// get selection location // get selection location
const nsRange* firstRange = SelectionRef().GetRangeAt(0); const nsRange* firstRange = SelectionRef().GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) { if (NS_WARN_IF(!firstRange)) {
@ -3776,7 +3778,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
// If there is no visible and editable nodes in the edit targets, make an // If there is no visible and editable nodes in the edit targets, make an
// empty block. // empty block.
// XXX Isn't this odd if there are only non-editable visible nodes? // XXX Isn't this odd if there are only non-editable visible nodes?
if (IsEmptyOneHardLine(arrayOfContents)) { if (HTMLEditUtils::IsEmptyOneHardLine(arrayOfContents)) {
const nsRange* firstRange = SelectionRef().GetRangeAt(0); const nsRange* firstRange = SelectionRef().GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) { if (NS_WARN_IF(!firstRange)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -9365,7 +9367,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
// If there is no visible and editable nodes in the edit targets, make an // If there is no visible and editable nodes in the edit targets, make an
// empty block. // empty block.
// XXX Isn't this odd if there are only non-editable visible nodes? // XXX Isn't this odd if there are only non-editable visible nodes?
if (IsEmptyOneHardLine(arrayOfContents)) { if (HTMLEditUtils::IsEmptyOneHardLine(arrayOfContents)) {
const nsRange* firstRange = SelectionRef().GetRangeAt(0); const nsRange* firstRange = SelectionRef().GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) { if (NS_WARN_IF(!firstRange)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;

Просмотреть файл

@ -204,7 +204,7 @@ bool HTMLEditUtils::IsHeader(nsINode& aNode) {
/** /**
* IsListItem() returns true if aNode is an html list item. * IsListItem() returns true if aNode is an html list item.
*/ */
bool HTMLEditUtils::IsListItem(nsINode* aNode) { bool HTMLEditUtils::IsListItem(const nsINode* aNode) {
MOZ_ASSERT(aNode); MOZ_ASSERT(aNode);
return aNode->IsAnyOfHTMLElements(nsGkAtoms::li, nsGkAtoms::dd, return aNode->IsAnyOfHTMLElements(nsGkAtoms::li, nsGkAtoms::dd,
nsGkAtoms::dt); nsGkAtoms::dt);
@ -248,7 +248,7 @@ bool HTMLEditUtils::IsTableRow(nsINode* aNode) {
/** /**
* IsTableCell() returns true if aNode is an html td or th. * IsTableCell() returns true if aNode is an html td or th.
*/ */
bool HTMLEditUtils::IsTableCell(nsINode* aNode) { bool HTMLEditUtils::IsTableCell(const nsINode* aNode) {
MOZ_ASSERT(aNode); MOZ_ASSERT(aNode);
return aNode->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th); return aNode->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th);
} }
@ -302,7 +302,7 @@ bool HTMLEditUtils::IsLink(nsINode* aNode) {
return !tmpText.IsEmpty(); return !tmpText.IsEmpty();
} }
bool HTMLEditUtils::IsNamedAnchor(nsINode* aNode) { bool HTMLEditUtils::IsNamedAnchor(const nsINode* aNode) {
MOZ_ASSERT(aNode); MOZ_ASSERT(aNode);
if (!aNode->IsHTMLElement(nsGkAtoms::a)) { if (!aNode->IsHTMLElement(nsGkAtoms::a)) {
return false; return false;
@ -350,7 +350,7 @@ bool HTMLEditUtils::IsMailCite(nsINode* aNode) {
/** /**
* IsFormWidget() returns true if aNode is a form widget of some kind. * IsFormWidget() returns true if aNode is a form widget of some kind.
*/ */
bool HTMLEditUtils::IsFormWidget(nsINode* aNode) { bool HTMLEditUtils::IsFormWidget(const nsINode* aNode) {
MOZ_ASSERT(aNode); MOZ_ASSERT(aNode);
return aNode->IsAnyOfHTMLElements(nsGkAtoms::textarea, nsGkAtoms::select, return aNode->IsAnyOfHTMLElements(nsGkAtoms::textarea, nsGkAtoms::select,
nsGkAtoms::button, nsGkAtoms::output, nsGkAtoms::button, nsGkAtoms::output,
@ -366,13 +366,13 @@ bool HTMLEditUtils::SupportsAlignAttr(nsINode& aNode) {
nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6); nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6);
} }
bool HTMLEditUtils::IsVisibleTextNode(Text& aText, bool HTMLEditUtils::IsVisibleTextNode(
Element* aEditingHost /* = nullptr */) { const Text& aText, const Element* aEditingHost /* = nullptr */) {
if (!aText.TextDataLength()) { if (!aText.TextDataLength()) {
return false; return false;
} }
if (!aText.TextIsOnlyWhitespace()) { if (!const_cast<Text&>(aText).TextIsOnlyWhitespace()) {
return true; return true;
} }
@ -388,7 +388,7 @@ bool HTMLEditUtils::IsVisibleTextNode(Text& aText,
} }
bool HTMLEditUtils::IsInVisibleTextFrames(nsPresContext* aPresContext, bool HTMLEditUtils::IsInVisibleTextFrames(nsPresContext* aPresContext,
Text& aText) { const Text& aText) {
MOZ_ASSERT(aPresContext); MOZ_ASSERT(aPresContext);
nsIFrame* frame = aText.GetPrimaryFrame(); nsIFrame* frame = aText.GetPrimaryFrame();
@ -417,7 +417,7 @@ bool HTMLEditUtils::IsInVisibleTextFrames(nsPresContext* aPresContext,
// bug 46209.) // bug 46209.)
bool isVisible = false; bool isVisible = false;
rv = selectionController->CheckVisibilityContent( rv = selectionController->CheckVisibilityContent(
&aText, 0, aText.TextDataLength(), &isVisible); const_cast<Text*>(&aText), 0, aText.TextDataLength(), &isVisible);
NS_WARNING_ASSERTION( NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv), NS_SUCCEEDED(rv),
"nsISelectionController::CheckVisibilityContent() failed"); "nsISelectionController::CheckVisibilityContent() failed");
@ -489,7 +489,8 @@ bool HTMLEditUtils::IsVisibleBRElement(
.ReachedBlockBoundary(); .ReachedBlockBoundary();
} }
bool HTMLEditUtils::IsEmptyNode(nsPresContext* aPresContext, nsINode& aNode, bool HTMLEditUtils::IsEmptyNode(nsPresContext* aPresContext,
const nsINode& aNode,
const EmptyCheckOptions& aOptions /* = {} */, const EmptyCheckOptions& aOptions /* = {} */,
bool* aSeenBR /* = nullptr */) { bool* aSeenBR /* = nullptr */) {
MOZ_ASSERT_IF(aOptions.contains(EmptyCheckOption::SafeToAskLayout), MOZ_ASSERT_IF(aOptions.contains(EmptyCheckOption::SafeToAskLayout),
@ -506,7 +507,7 @@ bool HTMLEditUtils::IsEmptyNode(nsPresContext* aPresContext, nsINode& aNode,
*aNode.AsContent()) *aNode.AsContent())
: nullptr; : nullptr;
if (Text* text = Text::FromNode(&aNode)) { if (const Text* text = Text::FromNode(&aNode)) {
return aOptions.contains(EmptyCheckOption::SafeToAskLayout) return aOptions.contains(EmptyCheckOption::SafeToAskLayout)
? !IsInVisibleTextFrames(aPresContext, *text) ? !IsInVisibleTextFrames(aPresContext, *text)
: !IsVisibleTextNode(*text, maybeParentBlockElement); : !IsVisibleTextNode(*text, maybeParentBlockElement);

Просмотреть файл

@ -120,21 +120,21 @@ class HTMLEditUtils final {
static bool IsFormatNode(nsINode* aNode); static bool IsFormatNode(nsINode* aNode);
static bool IsNodeThatCanOutdent(nsINode* aNode); static bool IsNodeThatCanOutdent(nsINode* aNode);
static bool IsHeader(nsINode& aNode); static bool IsHeader(nsINode& aNode);
static bool IsListItem(nsINode* aNode); static bool IsListItem(const nsINode* aNode);
static bool IsTable(nsINode* aNode); static bool IsTable(nsINode* aNode);
static bool IsTableRow(nsINode* aNode); static bool IsTableRow(nsINode* aNode);
static bool IsAnyTableElement(nsINode* aNode); static bool IsAnyTableElement(nsINode* aNode);
static bool IsAnyTableElementButNotTable(nsINode* aNode); static bool IsAnyTableElementButNotTable(nsINode* aNode);
static bool IsTableCell(nsINode* node); static bool IsTableCell(const nsINode* aNode);
static bool IsTableCellOrCaption(nsINode& aNode); static bool IsTableCellOrCaption(nsINode& aNode);
static bool IsAnyListElement(nsINode* aNode); static bool IsAnyListElement(nsINode* aNode);
static bool IsPre(nsINode* aNode); static bool IsPre(nsINode* aNode);
static bool IsImage(nsINode* aNode); static bool IsImage(nsINode* aNode);
static bool IsLink(nsINode* aNode); static bool IsLink(nsINode* aNode);
static bool IsNamedAnchor(nsINode* aNode); static bool IsNamedAnchor(const nsINode* aNode);
static bool IsMozDiv(nsINode* aNode); static bool IsMozDiv(nsINode* aNode);
static bool IsMailCite(nsINode* aNode); static bool IsMailCite(nsINode* aNode);
static bool IsFormWidget(nsINode* aNode); static bool IsFormWidget(const nsINode* aNode);
static bool SupportsAlignAttr(nsINode& aNode); static bool SupportsAlignAttr(nsINode& aNode);
static bool CanNodeContain(const nsINode& aParent, const nsIContent& aChild) { static bool CanNodeContain(const nsINode& aParent, const nsIContent& aChild) {
@ -252,15 +252,15 @@ class HTMLEditUtils final {
* But if you call this a lot, please specify proper editing host (or parent * But if you call this a lot, please specify proper editing host (or parent
* block) for the performance. * block) for the performance.
*/ */
static bool IsVisibleTextNode(dom::Text& aText, static bool IsVisibleTextNode(const dom::Text& aText,
Element* aEditingHost = nullptr); const Element* aEditingHost = nullptr);
/** /**
* IsInVisibleTextFrames() returns true if all text in aText is in visible * IsInVisibleTextFrames() returns true if all text in aText is in visible
* text frames. Callers have to guarantee that there is no pending reflow. * text frames. Callers have to guarantee that there is no pending reflow.
*/ */
static bool IsInVisibleTextFrames(nsPresContext* aPresContext, static bool IsInVisibleTextFrames(nsPresContext* aPresContext,
dom::Text& aText); const dom::Text& aText);
/** /**
* IsVisibleBRElement() and IsInvisibleBRElement() return true if aContent is * IsVisibleBRElement() and IsInvisibleBRElement() return true if aContent is
@ -298,16 +298,59 @@ class HTMLEditUtils final {
SafeToAskLayout, SafeToAskLayout,
}; };
using EmptyCheckOptions = EnumSet<EmptyCheckOption, uint32_t>; using EmptyCheckOptions = EnumSet<EmptyCheckOption, uint32_t>;
static bool IsEmptyNode(nsPresContext* aPresContext, nsINode& aNode, static bool IsEmptyNode(nsPresContext* aPresContext, const nsINode& aNode,
const EmptyCheckOptions& aOptions = {}, const EmptyCheckOptions& aOptions = {},
bool* aSeenBR = nullptr); bool* aSeenBR = nullptr);
static bool IsEmptyNode(nsINode& aNode, static bool IsEmptyNode(const nsINode& aNode,
const EmptyCheckOptions& aOptions = {}, const EmptyCheckOptions& aOptions = {},
bool* aSeenBR = nullptr) { bool* aSeenBR = nullptr) {
MOZ_ASSERT(!aOptions.contains(EmptyCheckOption::SafeToAskLayout)); MOZ_ASSERT(!aOptions.contains(EmptyCheckOption::SafeToAskLayout));
return IsEmptyNode(nullptr, aNode, aOptions, aSeenBR); return IsEmptyNode(nullptr, aNode, aOptions, aSeenBR);
} }
/**
* IsEmptyInlineContent() returns true if aContent is an inline node and it
* does not have meaningful content.
*/
static bool IsEmptyInlineContent(const nsIContent& aContent) {
return HTMLEditUtils::IsInlineElement(aContent) &&
HTMLEditUtils::IsContainerNode(aContent) &&
HTMLEditUtils::IsEmptyNode(
aContent, {EmptyCheckOption::TreatSingleBRElementAsVisible});
}
/**
* IsEmptyOneHardLine() returns true if aArrayOfContents does not represent
* 2 or more lines and have meaningful content.
*/
static bool IsEmptyOneHardLine(
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents) {
if (NS_WARN_IF(aArrayOfContents.IsEmpty())) {
return true;
}
bool brElementHasFound = false;
for (OwningNonNull<nsIContent>& content : aArrayOfContents) {
if (!EditorUtils::IsEditableContent(content,
EditorUtils::EditorType::HTML)) {
continue;
}
if (content->IsHTMLElement(nsGkAtoms::br)) {
// If there are 2 or more `<br>` elements, it's not empty line since
// there may be only one `<br>` element in a hard line.
if (brElementHasFound) {
return false;
}
brElementHasFound = true;
continue;
}
if (!HTMLEditUtils::IsEmptyInlineContent(content)) {
return false;
}
}
return true;
}
/** /**
* IsPointAtEdgeOfLink() returns true if aPoint is at start or end of a * IsPointAtEdgeOfLink() returns true if aPoint is at start or end of a
* link. * link.

Просмотреть файл

@ -899,17 +899,6 @@ NS_IMETHODIMP HTMLEditor::NodeIsBlock(nsINode* aNode, bool* aIsBlock) {
return NS_OK; return NS_OK;
} }
bool HTMLEditor::IsEmptyInlineNode(nsIContent& aContent) const {
MOZ_ASSERT(IsEditActionDataAvailable());
if (!HTMLEditUtils::IsInlineElement(aContent) ||
!HTMLEditUtils::IsContainerNode(aContent)) {
return false;
}
return HTMLEditUtils::IsEmptyNode(
aContent, {EmptyCheckOption::TreatSingleBRElementAsVisible});
}
/** /**
* IsNextCharInNodeWhiteSpace() checks the adjacent content in the same node to * IsNextCharInNodeWhiteSpace() checks the adjacent content in the same node to
* see if following selection is white-space or nbsp. * see if following selection is white-space or nbsp.

Просмотреть файл

@ -1442,43 +1442,6 @@ class HTMLEditor final : public TextEditor,
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeExtendSelectionToHardLineEdgesForBlockEditAction(); MaybeExtendSelectionToHardLineEdgesForBlockEditAction();
/**
* IsEmptyInlineNode() returns true if aContent is an inline node and it does
* not have meaningful content.
*/
bool IsEmptyInlineNode(nsIContent& aContent) const;
/**
* IsEmptyOneHardLine() returns true if aArrayOfContents does not represent
* 2 or more lines and have meaningful content.
*/
bool IsEmptyOneHardLine(
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents) const {
if (NS_WARN_IF(aArrayOfContents.IsEmpty())) {
return true;
}
bool brElementHasFound = false;
for (OwningNonNull<nsIContent>& content : aArrayOfContents) {
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue;
}
if (content->IsHTMLElement(nsGkAtoms::br)) {
// If there are 2 or more `<br>` elements, it's not empty line since
// there may be only one `<br>` element in a hard line.
if (brElementHasFound) {
return false;
}
brElementHasFound = true;
continue;
}
if (!IsEmptyInlineNode(content)) {
return false;
}
}
return true;
}
/** /**
* MaybeSplitAncestorsForInsertWithTransaction() does nothing if container of * MaybeSplitAncestorsForInsertWithTransaction() does nothing if container of
* aStartOfDeepestRightNode can have an element whose tag name is aTag. * aStartOfDeepestRightNode can have an element whose tag name is aTag.

Просмотреть файл

@ -283,10 +283,10 @@ class MOZ_STACK_CLASS WSRunScanner final {
using WSType = WSScanResult::WSType; using WSType = WSScanResult::WSType;
template <typename EditorDOMPointType> template <typename EditorDOMPointType>
WSRunScanner(dom::Element* aEditingHost, WSRunScanner(const dom::Element* aEditingHost,
const EditorDOMPointType& aScanStartPoint) const EditorDOMPointType& aScanStartPoint)
: mScanStartPoint(aScanStartPoint), : mScanStartPoint(aScanStartPoint),
mEditingHost(aEditingHost), mEditingHost(const_cast<dom::Element*>(aEditingHost)),
mTextFragmentDataAtStart(mScanStartPoint, mEditingHost) {} mTextFragmentDataAtStart(mScanStartPoint, mEditingHost) {}
// ScanNextVisibleNodeOrBlockBoundaryForwardFrom() returns the first visible // ScanNextVisibleNodeOrBlockBoundaryForwardFrom() returns the first visible
@ -298,7 +298,8 @@ class MOZ_STACK_CLASS WSRunScanner final {
const EditorDOMPointBase<PT, CT>& aPoint) const; const EditorDOMPointBase<PT, CT>& aPoint) const;
template <typename PT, typename CT> template <typename PT, typename CT>
static WSScanResult ScanNextVisibleNodeOrBlockBoundary( static WSScanResult ScanNextVisibleNodeOrBlockBoundary(
dom::Element* aEditingHost, const EditorDOMPointBase<PT, CT>& aPoint) { const dom::Element* aEditingHost,
const EditorDOMPointBase<PT, CT>& aPoint) {
return WSRunScanner(aEditingHost, aPoint) return WSRunScanner(aEditingHost, aPoint)
.ScanNextVisibleNodeOrBlockBoundaryFrom(aPoint); .ScanNextVisibleNodeOrBlockBoundaryFrom(aPoint);
} }
@ -312,7 +313,8 @@ class MOZ_STACK_CLASS WSRunScanner final {
const EditorDOMPointBase<PT, CT>& aPoint) const; const EditorDOMPointBase<PT, CT>& aPoint) const;
template <typename PT, typename CT> template <typename PT, typename CT>
static WSScanResult ScanPreviousVisibleNodeOrBlockBoundary( static WSScanResult ScanPreviousVisibleNodeOrBlockBoundary(
dom::Element* aEditingHost, const EditorDOMPointBase<PT, CT>& aPoint) { const dom::Element* aEditingHost,
const EditorDOMPointBase<PT, CT>& aPoint) {
return WSRunScanner(aEditingHost, aPoint) return WSRunScanner(aEditingHost, aPoint)
.ScanPreviousVisibleNodeOrBlockBoundaryFrom(aPoint); .ScanPreviousVisibleNodeOrBlockBoundaryFrom(aPoint);
} }