Bug 1415800 - part 1: Redesign EditorBase::GetPriorNode() with EditorRawDOMPoint r?m_kato
An overload of EditorBase::GetPriorNode() takes a set of container, child node
and offset of the child in the container. Replacing it with EditorRawDOMPoint
makes the caller simpler.
Additionally, it has two bool arguments, one is for searching if editable node,
the other is for searching if in same block. So, they can be hidden with
some human readable inline methods.
Finally, "Prior" isn't a term of DOM. So, let's use "Previous" instead.
MozReview-Commit-ID: A9uKzHaikY9
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -3280,48 +3280,70 @@ EditorBase::GetLengthOfDOMNode(nsIDOMNod
aCount = 0;
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
aCount = node->Length();
return NS_OK;
}
nsIContent*
-EditorBase::GetPriorNode(nsINode* aParentNode,
- int32_t aOffset,
- nsINode* aChildAtOffset,
- bool aEditableNode,
- bool aNoBlockCrossing)
-{
- MOZ_ASSERT(aParentNode);
+EditorBase::GetPreviousNodeInternal(nsINode& aNode,
+ bool aFindEditableNode,
+ bool aNoBlockCrossing)
+{
+ if (!IsDescendantOfEditorRoot(&aNode)) {
+ return nullptr;
+ }
+ return FindNode(&aNode, false, aFindEditableNode, aNoBlockCrossing);
+}
+
+nsIContent*
+EditorBase::GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint,
+ bool aFindEditableNode,
+ bool aNoBlockCrossing)
+{
+ MOZ_ASSERT(aPoint.IsSetAndValid());
+ NS_WARNING_ASSERTION(!aPoint.Container()->IsNodeOfType(nsINode::eDATA_NODE) ||
+ aPoint.Container()->IsNodeOfType(nsINode::eTEXT),
+ "GetPreviousNodeInternal() doesn't assume that the start point is a "
+ "data node except text node");
// If we are at the beginning of the node, or it is a text node, then just
// look before it.
- if (!aOffset || aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) {
- if (aNoBlockCrossing && IsBlockNode(aParentNode)) {
+ if (aPoint.IsStartOfContainer() ||
+ aPoint.Container()->IsNodeOfType(nsINode::eTEXT)) {
+ if (aNoBlockCrossing && IsBlockNode(aPoint.Container())) {
// If we aren't allowed to cross blocks, don't look before this block.
return nullptr;
}
- return GetPriorNode(aParentNode, aEditableNode, aNoBlockCrossing);
+ return GetPreviousNodeInternal(*aPoint.Container(),
+ aFindEditableNode, aNoBlockCrossing);
}
// else look before the child at 'aOffset'
- if (aChildAtOffset) {
- return GetPriorNode(aChildAtOffset, aEditableNode, aNoBlockCrossing);
+ if (aPoint.GetChildAtOffset()) {
+ return GetPreviousNodeInternal(*aPoint.GetChildAtOffset(),
+ aFindEditableNode, aNoBlockCrossing);
}
// unless there isn't one, in which case we are at the end of the node
// and want the deep-right child.
- nsIContent* resultNode = GetRightmostChild(aParentNode, aNoBlockCrossing);
- if (!resultNode || !aEditableNode || IsEditable(resultNode)) {
- return resultNode;
+ nsIContent* rightMostNode =
+ GetRightmostChild(aPoint.Container(), aNoBlockCrossing);
+ if (!rightMostNode) {
+ return nullptr;
+ }
+
+ if (!aFindEditableNode || IsEditable(rightMostNode)) {
+ return rightMostNode;
}
// restart the search from the non-editable node we just found
- return GetPriorNode(resultNode, aEditableNode, aNoBlockCrossing);
+ return GetPreviousNodeInternal(*rightMostNode,
+ aFindEditableNode, aNoBlockCrossing);
}
nsIContent*
EditorBase::GetNextNode(nsINode* aParentNode,
int32_t aOffset,
nsINode* aChildAtOffset,
bool aEditableNode,
bool aNoBlockCrossing)
@@ -3367,30 +3389,16 @@ EditorBase::GetNextNode(nsINode* aParent
// don't cross out of parent block
return nullptr;
}
return GetNextNode(aParentNode, aEditableNode, aNoBlockCrossing);
}
nsIContent*
-EditorBase::GetPriorNode(nsINode* aCurrentNode,
- bool aEditableNode,
- bool aNoBlockCrossing /* = false */)
-{
- MOZ_ASSERT(aCurrentNode);
-
- if (!IsDescendantOfEditorRoot(aCurrentNode)) {
- return nullptr;
- }
-
- return FindNode(aCurrentNode, false, aEditableNode, aNoBlockCrossing);
-}
-
-nsIContent*
EditorBase::FindNextLeafNode(nsINode* aCurrentNode,
bool aGoForward,
bool bNoBlockCrossing)
{
// called only by GetPriorNode so we don't need to check params.
NS_PRECONDITION(IsDescendantOfEditorRoot(aCurrentNode) &&
!IsEditorRoot(aCurrentNode),
"Bogus arguments");
@@ -4594,17 +4602,17 @@ EditorBase::CreateTxnForDeleteRange(nsRa
// XXX: if isFirst && isLast, then we'll need to delete the node
// as well as the 1 child
// build a transaction for deleting the appropriate data
// XXX: this has to come from rule section
if (aAction == ePrevious && isFirst) {
// we're backspacing from the beginning of the node. Delete the first
// thing to our left
- nsCOMPtr<nsIContent> priorNode = GetPriorNode(node, true);
+ nsCOMPtr<nsIContent> priorNode = GetPreviousEditableNode(*node);
if (NS_WARN_IF(!priorNode)) {
return nullptr;
}
// there is a priorNode, so delete its last child (if chardata, delete the
// last char). if it has no children, delete it
if (priorNode->IsNodeOfType(nsINode::eDATA_NODE)) {
RefPtr<nsGenericDOMDataNode> priorNodeAsCharData =
@@ -4688,27 +4696,28 @@ EditorBase::CreateTxnForDeleteRange(nsRa
node.forget(aRemovingNode);
return deleteTextTransaction.forget();
}
// we're either deleting a node or chardata, need to dig into the next/prev
// node to find out
nsCOMPtr<nsINode> selectedNode;
if (aAction == ePrevious) {
- selectedNode = GetPriorNode(node, offset, child, true);
+ selectedNode =
+ GetPreviousEditableNode(EditorRawDOMPoint(node, child, offset));
} else if (aAction == eNext) {
selectedNode = GetNextNode(node, offset, child, true);
}
while (selectedNode &&
selectedNode->IsNodeOfType(nsINode::eDATA_NODE) &&
!selectedNode->Length()) {
// Can't delete an empty chardata node (bug 762183)
if (aAction == ePrevious) {
- selectedNode = GetPriorNode(selectedNode, true);
+ selectedNode = GetPreviousEditableNode(*selectedNode);
} else if (aAction == eNext) {
selectedNode = GetNextNode(selectedNode, true);
}
}
if (NS_WARN_IF(!selectedNode)) {
return nullptr;
}
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -602,22 +602,44 @@ protected:
* that the editor's sync/async settings for reflowing, painting, and
* scrolling match.
*/
nsresult ScrollSelectionIntoView(bool aScrollToAnchor);
virtual bool IsBlockNode(nsINode* aNode);
/**
- * Helper for GetPriorNode() and GetNextNode().
+ * Helper for GetPreviousNodeInternal() and GetNextNode().
*/
nsIContent* FindNextLeafNode(nsINode* aCurrentNode,
bool aGoForward,
bool bNoBlockCrossing);
+ /**
+ * Get the node immediately previous node of aNode.
+ * @param atNode The node from which we start the search.
+ * @param aFindEditableNode If true, only return an editable node.
+ * @param aNoBlockCrossing If true, don't move across "block" nodes,
+ * whatever that means.
+ * @return The node that occurs before aNode in
+ * the tree, skipping non-editable nodes if
+ * aFindEditableNode is true. If there is no
+ * previous node, returns nullptr.
+ */
+ nsIContent* GetPreviousNodeInternal(nsINode& aNode,
+ bool aFindEditableNode,
+ bool aNoBlockCrossing);
+
+ /**
+ * And another version that takes a point in DOM tree rather than a node.
+ */
+ nsIContent* GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint,
+ bool aFindEditableNode,
+ bool aNoBlockCrossing);
+
virtual nsresult InstallEventListeners();
virtual void CreateEventListeners();
virtual void RemoveEventListeners();
/**
* Return true if spellchecking should be enabled for this editor.
*/
bool GetDesiredSpellCheckState();
@@ -732,38 +754,51 @@ public:
* @param aNode is the node to get the length of.
* If aNode is text, returns number of characters.
* If not, returns number of children nodes.
* @param aCount [OUT] the result of the above calculation.
*/
static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount);
/**
- * Get the node immediately prior to aCurrentNode.
- * @param aCurrentNode the node from which we start the search
- * @param aEditableNode if true, only return an editable node
- * @param aResultNode [OUT] the node that occurs before aCurrentNode in
- * the tree, skipping non-editable nodes if
- * aEditableNode is true. If there is no prior
- * node, aResultNode will be nullptr.
- * @param bNoBlockCrossing If true, don't move across "block" nodes,
- * whatever that means.
+ * Get the previous node.
*/
- nsIContent* GetPriorNode(nsINode* aCurrentNode, bool aEditableNode,
- bool aNoBlockCrossing = false);
-
- /**
- * And another version that takes a {parent,offset} pair rather than a node.
- */
- nsIContent* GetPriorNode(nsINode* aParentNode,
- int32_t aOffset,
- nsINode* aChildAtOffset,
- bool aEditableNode,
- bool aNoBlockCrossing = false);
-
+ nsIContent* GetPreviousNode(const EditorRawDOMPoint& aPoint)
+ {
+ return GetPreviousNodeInternal(aPoint, false, false);
+ }
+ nsIContent* GetPreviousEditableNode(const EditorRawDOMPoint& aPoint)
+ {
+ return GetPreviousNodeInternal(aPoint, true, false);
+ }
+ nsIContent* GetPreviousNodeInBlock(const EditorRawDOMPoint& aPoint)
+ {
+ return GetPreviousNodeInternal(aPoint, false, true);
+ }
+ nsIContent* GetPreviousEditableNodeInBlock(
+ const EditorRawDOMPoint& aPoint)
+ {
+ return GetPreviousNodeInternal(aPoint, true, true);
+ }
+ nsIContent* GetPreviousNode(nsINode& aNode)
+ {
+ return GetPreviousNodeInternal(aNode, false, false);
+ }
+ nsIContent* GetPreviousEditableNode(nsINode& aNode)
+ {
+ return GetPreviousNodeInternal(aNode, true, false);
+ }
+ nsIContent* GetPreviousNodeInBlock(nsINode& aNode)
+ {
+ return GetPreviousNodeInternal(aNode, false, true);
+ }
+ nsIContent* GetPreviousEditableNodeInBlock(nsINode& aNode)
+ {
+ return GetPreviousNodeInternal(aNode, true, true);
+ }
/**
* Get the node immediately after to aCurrentNode.
* @param aCurrentNode the node from which we start the search
* @param aEditableNode if true, only return an editable node
* @param aResultNode [OUT] the node that occurs after aCurrentNode in the
* tree, skipping non-editable nodes if
* aEditableNode is true. If there is no prior
@@ -778,17 +813,17 @@ public:
*/
nsIContent* GetNextNode(nsINode* aParentNode,
int32_t aOffset,
nsINode* aChildAtOffset,
bool aEditableNode,
bool aNoBlockCrossing = false);
/**
- * Helper for GetNextNode() and GetPriorNode().
+ * Helper for GetNextNode() and GetPreviousNodeInternal().
*/
nsIContent* FindNode(nsINode* aCurrentNode,
bool aGoForward,
bool aEditableNode,
bool bNoBlockCrossing);
/**
* Get the rightmost child of aCurrentNode;
* return nullptr if aCurrentNode has no children.
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -5267,21 +5267,19 @@ HTMLEditRules::CheckForEmptyBlock(nsINod
}
nsresult rv = aSelection->Collapse(afterEmptyBlock);
NS_ENSURE_SUCCESS(rv, rv);
}
} else if (aAction == nsIEditor::ePrevious ||
aAction == nsIEditor::ePreviousWord ||
aAction == nsIEditor::eToBeginningOfLine) {
// Move to the end of the previous node
- int32_t offset = blockParent->IndexOf(emptyBlock);
- nsCOMPtr<nsIContent> priorNode = htmlEditor->GetPriorNode(blockParent,
- offset,
- emptyBlock,
- true);
+ EditorRawDOMPoint atEmptyBlock(emptyBlock);
+ nsCOMPtr<nsIContent> priorNode =
+ htmlEditor->GetPreviousEditableNode(atEmptyBlock);
if (priorNode) {
EditorDOMPoint pt = GetGoodSelPointForNode(*priorNode, aAction);
nsresult rv = aSelection->Collapse(pt.AsRaw());
NS_ENSURE_SUCCESS(rv, rv);
} else {
EditorRawDOMPoint afterEmptyBlock(emptyBlock);
if (NS_WARN_IF(!afterEmptyBlock.AdvanceOffset())) {
return NS_ERROR_FAILURE;
@@ -7934,16 +7932,18 @@ HTMLEditRules::AdjustSelection(Selection
// the user will see no new line for the break. Also, things
// like table cells won't grow in height.
nsCOMPtr<nsIDOMNode> brNode;
rv = CreateMozBR(GetAsDOMNode(selNode), selOffset,
getter_AddRefs(brNode));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> brParent =
EditorBase::GetNodeLocation(brNode, &selOffset);
+ nsCOMPtr<nsIContent> br = do_QueryInterface(brNode);
+ child = br;
// selection stays *before* moz-br, sticking to it
aSelection->SetInterlinePosition(true);
rv = aSelection->Collapse(brParent, selOffset);
NS_ENSURE_SUCCESS(rv, rv);
} else {
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<nsIContent> nextNode =
mHTMLEditor->GetNextHTMLNode(nearNode, true);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3876,17 +3876,18 @@ HTMLEditor::GetPriorHTMLNode(nsINode* aN
bool aNoBlockCrossing)
{
MOZ_ASSERT(aNode);
if (!GetActiveEditingHost()) {
return nullptr;
}
- return GetPriorNode(aNode, true, aNoBlockCrossing);
+ return aNoBlockCrossing ? GetPreviousEditableNodeInBlock(*aNode) :
+ GetPreviousEditableNode(*aNode);
}
/**
* GetPriorHTMLNode() is same as above but takes {parent,offset} instead of
* node.
*/
nsIContent*
HTMLEditor::GetPriorHTMLNode(nsINode* aParent,
@@ -3895,17 +3896,22 @@ HTMLEditor::GetPriorHTMLNode(nsINode* aP
bool aNoBlockCrossing)
{
MOZ_ASSERT(aParent);
if (!GetActiveEditingHost()) {
return nullptr;
}
- return GetPriorNode(aParent, aOffset, aChildAtOffset, true, aNoBlockCrossing);
+ EditorRawDOMPoint point(aParent,
+ aChildAtOffset && aChildAtOffset->IsContent() ?
+ aChildAtOffset->AsContent() : nullptr,
+ aOffset);
+ return aNoBlockCrossing ? GetPreviousEditableNodeInBlock(point) :
+ GetPreviousEditableNode(point);
}
/**
* GetNextHTMLNode() returns the next editable leaf node, if there is
* one within the <body>.
*/
nsIContent*
HTMLEditor::GetNextHTMLNode(nsINode* aNode,