Bug 1359404 - Redesign HTMLEditor::IsVisTextNode() r?m_kato
Despite of its name, HTMLEditor::IsVisTextNode() returns true with its out
argument when the given text node is empty. And although it returns nsresult,
it's almost always NS_OK because it returns error only when the editor isn't
usual condition.
So, it should return bool and true when the given text node is visible.
This patch separates the method. One is for checking the node with frames,
called IsInVisibleTextFrames(). The other is for checking it only with
its text, called IsVisibleTextNode().
MozReview-Commit-ID: EdQmkOxfNxO
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -2743,19 +2743,19 @@ HTMLEditRules::WillDeleteSelection(Selec
// If something visible is deleted, no need to join. Visible means
// all nodes except non-visible textnodes and breaks.
if (join && origCollapsed) {
if (!somenode->IsContent()) {
join = false;
continue;
}
nsCOMPtr<nsIContent> content = somenode->AsContent();
- if (content->NodeType() == nsIDOMNode::TEXT_NODE) {
+ if (Text* text = content->GetAsText()) {
NS_ENSURE_STATE(mHTMLEditor);
- mHTMLEditor->IsVisTextNode(content, &join, true);
+ join = !mHTMLEditor->IsInVisibleTextFrames(*text);
} else {
NS_ENSURE_STATE(mHTMLEditor);
join = content->IsHTMLElement(nsGkAtoms::br) &&
!mHTMLEditor->IsVisibleBRElement(somenode);
}
}
}
}
@@ -2834,24 +2834,28 @@ HTMLEditRules::WillDeleteSelection(Selec
* that doesn't correctly skip over it.
*
* If deleting the node fails (like if it's not editable), the caller should
* proceed as usual, so don't return any errors.
*/
void
HTMLEditRules::DeleteNodeIfCollapsedText(nsINode& aNode)
{
- if (!aNode.GetAsText()) {
+ Text* text = aNode.GetAsText();
+ if (!text) {
return;
}
- bool empty;
- nsresult rv = mHTMLEditor->IsVisTextNode(aNode.AsContent(), &empty, false);
- NS_ENSURE_SUCCESS_VOID(rv);
- if (empty) {
- mHTMLEditor->DeleteNode(&aNode);
+
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return;
+ }
+
+ if (!mHTMLEditor->IsVisibleTextNode(*text)) {
+ RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+ htmlEditor->DeleteNode(&aNode);
}
}
/**
* InsertBRIfNeeded() determines if a br is needed for current selection to not
* be spastic. If so, it inserts one. Callers responsibility to only call
* with collapsed selection.
@@ -6232,22 +6236,19 @@ HTMLEditRules::GetNodesForOperation(
if (HTMLEditUtils::IsListItem(node)) {
int32_t j = i;
aOutArrayOfNodes.RemoveElementAt(i);
GetInnerContent(*node, aOutArrayOfNodes, &j);
}
}
// Empty text node shouldn't be selected if unnecessary
for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
- OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
- if (EditorBase::IsTextNode(node)) {
+ if (Text* text = aOutArrayOfNodes[i]->GetAsText()) {
// Don't select empty text except to empty block
- bool isEmpty = false;
- htmlEditor->IsVisTextNode(node->AsContent(), &isEmpty, false);
- if (isEmpty) {
+ if (!htmlEditor->IsVisibleTextNode(*text)) {
aOutArrayOfNodes.RemoveElementAt(i);
}
}
}
}
// Indent/outdent already do something special for list items, but we still
// need to make sure we don't act on table elements
else if (aOperationType == EditAction::outdent ||
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3933,65 +3933,65 @@ HTMLEditor::GetLastEditableLeaf(nsINode&
if (!aNode.Contains(child)) {
return nullptr;
}
}
return child;
}
-/**
- * IsVisTextNode() figures out if textnode aTextNode has any visible content.
- */
-nsresult
-HTMLEditor::IsVisTextNode(nsIContent* aNode,
- bool* outIsEmptyNode,
- bool aSafeToAskFrames)
+bool
+HTMLEditor::IsInVisibleTextFrames(Text& aText)
{
- MOZ_ASSERT(aNode);
- MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::TEXT_NODE);
- MOZ_ASSERT(outIsEmptyNode);
-
- *outIsEmptyNode = true;
-
- uint32_t length = aNode->TextLength();
- if (aSafeToAskFrames) {
- nsISelectionController* selectionController = GetSelectionController();
- if (NS_WARN_IF(!selectionController)) {
- return NS_ERROR_FAILURE;
- }
- bool isVisible = false;
- // ask the selection controller for information about whether any
- // of the data in the node is really rendered. This is really
- // something that frames know about, but we aren't supposed to talk to frames.
- // So we put a call in the selection controller interface, since it's already
- // in bed with frames anyway. (this is a fix for bug 22227, and a
- // partial fix for bug 46209)
- nsresult rv = selectionController->CheckVisibilityContent(aNode, 0, length,
- &isVisible);
- NS_ENSURE_SUCCESS(rv, rv);
- if (isVisible) {
- *outIsEmptyNode = false;
- }
- } else if (length) {
- if (aNode->TextIsOnlyWhitespace()) {
- WSRunObject wsRunObj(this, aNode, 0);
- nsCOMPtr<nsINode> visNode;
- int32_t outVisOffset=0;
- WSType visType;
- wsRunObj.NextVisibleNode(aNode, 0, address_of(visNode),
- &outVisOffset, &visType);
- if (visType == WSType::normalWS || visType == WSType::text) {
- *outIsEmptyNode = (aNode != visNode);
- }
- } else {
- *outIsEmptyNode = false;
- }
- }
- return NS_OK;
+ nsISelectionController* selectionController = GetSelectionController();
+ if (NS_WARN_IF(!selectionController)) {
+ return false;
+ }
+
+ if (!aText.TextDataLength()) {
+ return false;
+ }
+
+ // Ask the selection controller for information about whether any of the
+ // data in the node is really rendered. This is really something that
+ // frames know about, but we aren't supposed to talk to frames. So we put
+ // a call in the selection controller interface, since it's already in bed
+ // with frames anyway. (This is a fix for bug 22227, and a partial fix for
+ // bug 46209.)
+ bool isVisible = false;
+ nsresult rv =
+ selectionController->CheckVisibilityContent(&aText,
+ 0, aText.TextDataLength(),
+ &isVisible);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ return isVisible;
+}
+
+bool
+HTMLEditor::IsVisibleTextNode(Text& aText)
+{
+ if (!aText.TextDataLength()) {
+ return false;
+ }
+
+ if (!aText.TextIsOnlyWhitespace()) {
+ return true;
+ }
+
+ WSRunObject wsRunObj(this, &aText, 0);
+ nsCOMPtr<nsINode> nextVisibleNode;
+ int32_t unused = 0;
+ WSType visibleNodeType;
+ wsRunObj.NextVisibleNode(&aText, 0, address_of(nextVisibleNode),
+ &unused, &visibleNodeType);
+ return (visibleNodeType == WSType::normalWS ||
+ visibleNodeType == WSType::text) &&
+ &aText == nextVisibleNode;
}
/**
* IsEmptyNode() figures out if aNode is an empty node. A block can have
* children and still be considered empty, if the children are empty or
* non-editable.
*/
nsresult
@@ -4028,18 +4028,20 @@ HTMLEditor::IsEmptyNodeImpl(nsINode* aNo
bool* outIsEmptyNode,
bool aSingleBRDoesntCount,
bool aListOrCellNotEmpty,
bool aSafeToAskFrames,
bool* aSeenBR)
{
NS_ENSURE_TRUE(aNode && outIsEmptyNode && aSeenBR, NS_ERROR_NULL_POINTER);
- if (aNode->NodeType() == nsIDOMNode::TEXT_NODE) {
- return IsVisTextNode(static_cast<nsIContent*>(aNode), outIsEmptyNode, aSafeToAskFrames);
+ if (Text* text = aNode->GetAsText()) {
+ *outIsEmptyNode = aSafeToAskFrames ? !IsInVisibleTextFrames(*text) :
+ !IsVisibleTextNode(*text);
+ return NS_OK;
}
// if it's not a text node (handled above) and it's not a container,
// then we don't call it empty (it's an <hr>, or <br>, etc.).
// Also, if it's an anchor then don't treat it as empty - even though
// anchors are containers, named anchors are "empty" but we don't
// want to treat them as such. Also, don't call ListItems or table
// cells empty if caller desires. Form Widgets not empty.
@@ -4059,21 +4061,21 @@ HTMLEditor::IsEmptyNodeImpl(nsINode* aNo
// loop over children of node. if no children, or all children are either
// empty text nodes or non-editable, then node qualifies as empty
for (nsCOMPtr<nsIContent> child = aNode->GetFirstChild();
child;
child = child->GetNextSibling()) {
// Is the child editable and non-empty? if so, return false
if (EditorBase::IsEditable(child)) {
- if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
- nsresult rv = IsVisTextNode(child, outIsEmptyNode, aSafeToAskFrames);
- NS_ENSURE_SUCCESS(rv, rv);
- // break out if we find we aren't emtpy
- if (!*outIsEmptyNode) {
+ if (Text* text = child->GetAsText()) {
+ // break out if we find we aren't empty
+ *outIsEmptyNode = aSafeToAskFrames ? !IsInVisibleTextFrames(*text) :
+ !IsVisibleTextNode(*text);
+ if (!*outIsEmptyNode){
return NS_OK;
}
} else {
// An editable, non-text node. We need to check its content.
// Is it the node we are iterating over?
if (child == aNode) {
break;
}
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -368,22 +368,31 @@ public:
* This will stop at a table, however, since we don't want to
* "drill down" into nested tables.
* @param aSelection Optional. If null, we get current selection.
*/
void CollapseSelectionToDeepestNonTableFirstChild(Selection* aSelection,
nsINode* aNode);
/**
+ * IsInVisibleTextFrames() returns true if all text in aText is in visible
+ * text frames. Callers have to guarantee that there is no pending reflow.
+ */
+ bool IsInVisibleTextFrames(dom::Text& aText);
+
+ /**
+ * IsVisibleTextNode() returns true if aText has visible text. If it has
+ * only whitespaces and they are collapsed, returns false.
+ */
+ bool IsVisibleTextNode(Text& aText);
+
+ /**
* aNode must be a non-null text node.
* outIsEmptyNode must be non-null.
*/
- nsresult IsVisTextNode(nsIContent* aNode,
- bool* outIsEmptyNode,
- bool aSafeToAskFrames);
nsresult IsEmptyNode(nsIDOMNode* aNode, bool* outIsEmptyBlock,
bool aMozBRDoesntCount = false,
bool aListOrCellNotEmpty = false,
bool aSafeToAskFrames = false);
nsresult IsEmptyNode(nsINode* aNode, bool* outIsEmptyBlock,
bool aMozBRDoesntCount = false,
bool aListOrCellNotEmpty = false,
bool aSafeToAskFrames = false);