Bug 1627175 - part 15: Create `HTMLEditUtils::GetFirstLeafChild()` instead of `EditorBase::GetLeftmostChild()` r=m_kato

This is used by `TextEditor::UndoAsAction()` too, so, it might be better to be
in `EditorUtils`.  However, it is completely designed for `HTMLEditor` and
ideally, `UndoAsAction` should be overridden by `HTMLEditor` too and
`TextEditor` should use faster path because of its content is in anonymous
subtree. But we don't have any merit to do that instead of avoiding virtual
call.

Differential Revision: https://phabricator.services.mozilla.com/D74362
This commit is contained in:
Masayuki Nakano 2020-05-08 09:49:19 +00:00
Родитель 8317bb8abb
Коммит 8c859f8589
8 изменённых файлов: 71 добавлений и 71 удалений

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

@ -2581,7 +2581,8 @@ nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) {
MOZ_ASSERT(aRoot);
EditorType editorType = GetEditorType();
nsIContent* content = GetLeftmostChild(aRoot);
nsIContent* content =
HTMLEditUtils::GetFirstLeafChild(*aRoot, ChildBlockBoundary::TreatAsLeaf);
if (content && !EditorUtils::IsEditableContent(*content, editorType)) {
content = GetNextEditableNode(*content);
}
@ -2904,24 +2905,25 @@ nsIContent* EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint,
return point.GetChild();
}
nsIContent* leftMostContent =
GetLeftmostChild(point.GetChild(), aNoBlockCrossing);
if (!leftMostContent) {
nsIContent* firstLeafContent = HTMLEditUtils::GetFirstLeafChild(
*point.GetChild(), aNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf
: ChildBlockBoundary::Ignore);
if (!firstLeafContent) {
return point.GetChild();
}
if (!IsDescendantOfEditorRoot(leftMostContent)) {
if (!IsDescendantOfEditorRoot(firstLeafContent)) {
return nullptr;
}
if ((!aFindEditableNode ||
EditorUtils::IsEditableContent(*leftMostContent, GetEditorType())) &&
(aFindAnyDataNode || EditorUtils::IsElementOrText(*leftMostContent))) {
return leftMostContent;
EditorUtils::IsEditableContent(*firstLeafContent, GetEditorType())) &&
(aFindAnyDataNode || EditorUtils::IsElementOrText(*firstLeafContent))) {
return firstLeafContent;
}
// restart the search from the non-editable node we just found
return GetNextNodeInternal(*leftMostContent, aFindEditableNode,
return GetNextNodeInternal(*firstLeafContent, aFindEditableNode,
aFindAnyDataNode, aNoBlockCrossing);
}
@ -2955,17 +2957,14 @@ nsIContent* EditorBase::FindNextLeafNode(nsINode* aCurrentNode, bool aGoForward,
// don't look inside prevsib, since it is a block
return sibling;
}
nsIContent* leaf =
ChildBlockBoundary childBlockBoundary =
bNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf
: ChildBlockBoundary::Ignore;
nsIContent* leafContent =
aGoForward
? GetLeftmostChild(sibling, bNoBlockCrossing)
: HTMLEditUtils::GetLastLeafChild(
*sibling, bNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf
: ChildBlockBoundary::Ignore);
if (!leaf) {
return sibling;
}
return leaf;
? HTMLEditUtils::GetFirstLeafChild(*sibling, childBlockBoundary)
: HTMLEditUtils::GetLastLeafChild(*sibling, childBlockBoundary);
return leafContent ? leafContent : sibling;
}
nsINode* parent = cur->GetParentNode();
@ -3018,30 +3017,6 @@ nsIContent* EditorBase::FindNode(nsINode* aCurrentNode, bool aGoForward,
bNoBlockCrossing);
}
nsIContent* EditorBase::GetLeftmostChild(nsINode* aCurrentNode,
bool bNoBlockCrossing) const {
if (NS_WARN_IF(!aCurrentNode)) {
return nullptr;
}
nsIContent* content = aCurrentNode->GetFirstChild();
if (!content) {
return nullptr;
}
for (;;) {
if (bNoBlockCrossing && HTMLEditUtils::IsBlockElement(*content)) {
return content;
}
nsIContent* next = content->GetFirstChild();
if (!next) {
return content;
}
content = next;
}
MOZ_ASSERT_UNREACHABLE("What part of for(;;) do you not understand?");
return nullptr;
}
bool EditorBase::IsRoot(nsINode* inNode) const {
if (NS_WARN_IF(!inNode)) {
return false;

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

@ -1695,13 +1695,6 @@ class EditorBase : public nsIEditor,
return GetNextNodeInternal(aNode, true, true, true);
}
/**
* Get the leftmost child of aCurrentNode;
* return nullptr if aCurrentNode has no children.
*/
nsIContent* GetLeftmostChild(nsINode* aCurrentNode,
bool bNoBlockCrossing = false) const;
/**
* Returns true if aNode is our root node.
*/

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

@ -7859,8 +7859,9 @@ nsresult HTMLEditor::MaybeExtendSelectionToHardLineEdgesForBlockEditAction() {
// of going "down" into a block and "up" out of a block.
if (wsScannerAtStart.EndsByOtherBlockElement()) {
// startpoint is just before the start of a block.
nsINode* child = GetLeftmostChild(
wsScannerAtStart.EndReasonOtherBlockElementPtr(), true);
nsINode* child = HTMLEditUtils::GetFirstLeafChild(
*wsScannerAtStart.EndReasonOtherBlockElementPtr(),
ChildBlockBoundary::TreatAsLeaf);
if (child) {
newStartPoint.Set(child);
}
@ -9243,7 +9244,8 @@ nsresult HTMLEditor::SplitParagraph(
// selection to beginning of right hand para;
// look inside any containers that are up front.
nsCOMPtr<nsIContent> child = GetLeftmostChild(&aParentDivOrP, true);
nsCOMPtr<nsIContent> child = HTMLEditUtils::GetFirstLeafChild(
aParentDivOrP, ChildBlockBoundary::TreatAsLeaf);
if (child && (child->IsText() || HTMLEditUtils::IsContainerNode(*child))) {
nsresult rv = CollapseSelectionToStartOf(*child);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {

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

@ -220,6 +220,26 @@ class HTMLEditUtils final {
return nullptr;
}
/**
* GetFirstLeafChild() returns leftmost leaf content in aNode. It depends on
* aChildBlockBoundary whether this scans into a block child or treat
* block as a leaf.
*/
static nsIContent* GetFirstLeafChild(nsINode& aNode,
ChildBlockBoundary aChildBlockBoundary) {
for (nsIContent* content = aNode.GetFirstChild(); content;
content = content->GetFirstChild()) {
if (aChildBlockBoundary == ChildBlockBoundary::TreatAsLeaf &&
HTMLEditUtils::IsBlockElement(*content)) {
return content;
}
if (!content->HasChildren()) {
return content;
}
}
return nullptr;
}
/**
* GetAncestorBlockElement() returns parent or nearest ancestor of aContent
* which is a block element. If aAncestorLimiter is not nullptr,

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

@ -2745,7 +2745,8 @@ already_AddRefed<Element> HTMLEditor::GetSelectedElement(const nsAtom* aTagName,
if (nextSibling->IsHTMLElement(nsGkAtoms::br)) {
return nullptr;
}
nsIContent* firstEditableLeaf = GetLeftmostChild(nextSibling);
nsIContent* firstEditableLeaf = HTMLEditUtils::GetFirstLeafChild(
*nextSibling, ChildBlockBoundary::Ignore);
if (firstEditableLeaf &&
firstEditableLeaf->IsHTMLElement(nsGkAtoms::br)) {
return nullptr;
@ -4960,7 +4961,8 @@ nsIContent* HTMLEditor::GetLastEditableChild(nsINode& aNode) const {
}
nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const {
nsIContent* child = GetLeftmostChild(&aNode);
nsIContent* child =
HTMLEditUtils::GetFirstLeafChild(aNode, ChildBlockBoundary::Ignore);
while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) ||
child->HasChildren())) {
child = GetNextEditableHTMLNode(*child);

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

@ -46,6 +46,8 @@ namespace mozilla {
using namespace dom;
using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary;
nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty,
nsAtom* aAttribute,
const nsAString& aValue,
@ -880,10 +882,10 @@ EditResult HTMLEditor::ClearStyleAt(const EditorDOMPoint& aPoint,
// the next node. The first example should become
// `<p><b><i>a</i></b><b><i></i></b><b><i>bc</i></b></p>`.
// ^^^^^^^^^^^^^^
nsIContent* leftmostChildOfNextNode =
GetLeftmostChild(splitResult.GetNextNode());
EditorDOMPoint atStartOfNextNode(leftmostChildOfNextNode
? leftmostChildOfNextNode
nsIContent* firstLeafChildOfNextNode = HTMLEditUtils::GetFirstLeafChild(
*splitResult.GetNextNode(), ChildBlockBoundary::Ignore);
EditorDOMPoint atStartOfNextNode(firstLeafChildOfNextNode
? firstLeafChildOfNextNode
: splitResult.GetNextNode(),
0);
RefPtr<HTMLBRElement> brElement;
@ -938,10 +940,12 @@ EditResult HTMLEditor::ClearStyleAt(const EditorDOMPoint& aPoint,
// Now, we want to put `<br>` element into the empty split node if
// it was in next node of the first split.
// E.g., `<p><b><i>a</i></b><b><i><br></i></b><b><i>bc</i></b></p>`
nsIContent* leftmostChild =
GetLeftmostChild(splitResultAtStartOfNextNode.GetPreviousNode());
nsIContent* firstLeafChildOfPreviousNode = HTMLEditUtils::GetFirstLeafChild(
*splitResultAtStartOfNextNode.GetPreviousNode(),
ChildBlockBoundary::Ignore);
EditorDOMPoint pointToPutCaret(
leftmostChild ? leftmostChild
firstLeafChildOfPreviousNode
? firstLeafChildOfPreviousNode
: splitResultAtStartOfNextNode.GetPreviousNode(),
0);
// If the right node starts with a `<br>`, suck it out of right node and into

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

@ -8,6 +8,7 @@
#include <algorithm>
#include "EditAggregateTransaction.h"
#include "HTMLEditUtils.h"
#include "InternetCiter.h"
#include "PlaceholderTransaction.h"
#include "gfxFontUtils.h"
@ -69,6 +70,8 @@ namespace mozilla {
using namespace dom;
using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary;
TextEditor::TextEditor()
: mMaxTextLength(-1),
mUnmaskedStart(UINT32_MAX),
@ -1000,11 +1003,12 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
// at redo, or doing it everywhere else that might care. Since undo
// and redo are relatively rare, it makes sense to take the (small)
// performance hit here.
nsIContent* leftMostChild = GetLeftmostChild(mRootElement);
if (leftMostChild &&
EditorUtils::IsPaddingBRElementForEmptyEditor(*leftMostChild)) {
nsIContent* firstLeafChild = HTMLEditUtils::GetFirstLeafChild(
*mRootElement, ChildBlockBoundary::Ignore);
if (firstLeafChild &&
EditorUtils::IsPaddingBRElementForEmptyEditor(*firstLeafChild)) {
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(leftMostChild);
static_cast<HTMLBRElement*>(firstLeafChild);
} else {
mPaddingBRElementForEmptyEditor = nullptr;
}

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

@ -1238,8 +1238,8 @@ nsIContent* WSRunScanner::GetNextWSNodeInner(nsINode* aStartNode,
}
if (HTMLEditUtils::IsContainerNode(*nextContent)) {
// Else if it's a container, get deep leftmost child
nsCOMPtr<nsIContent> child = mHTMLEditor->GetLeftmostChild(nextContent);
if (child) {
if (nsIContent* child = HTMLEditUtils::GetFirstLeafChild(
*nextContent, ChildBlockBoundary::Ignore)) {
return child;
}
}
@ -1283,8 +1283,8 @@ nsIContent* WSRunScanner::GetNextWSNode(const EditorDOMPoint& aPoint,
}
if (HTMLEditUtils::IsContainerNode(*nextContent)) {
// else if it's a container, get deep leftmost child
nsCOMPtr<nsIContent> child = mHTMLEditor->GetLeftmostChild(nextContent);
if (child) {
if (nsIContent* child = HTMLEditUtils::GetFirstLeafChild(
*nextContent, ChildBlockBoundary::Ignore)) {
return child;
}
}