Bug 1423097 - part 1: Implement Selection::AnchorRef() and Selection::FocusRef() r?smaug
Some methods of editor retrieves anchor and focus of selection. However, there
are no methods which directly access RangeBoundary of anchor and focus.
This patch adds it for making editor code simpler and avoiding unnecessary
child offset computation.
MozReview-Commit-ID: EvepQpFMi8S
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -811,42 +811,58 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection)
+const RangeBoundary&
+Selection::AnchorRef()
+{
+ if (!mAnchorFocusRange) {
+ static RangeBoundary sEmpty;
+ return sEmpty;
+ }
+
+ if (GetDirection() == eDirNext) {
+ return mAnchorFocusRange->StartRef();
+ }
+
+ return mAnchorFocusRange->EndRef();
+}
+
+const RangeBoundary&
+Selection::FocusRef()
+{
+ if (!mAnchorFocusRange) {
+ static RangeBoundary sEmpty;
+ return sEmpty;
+ }
+
+ if (GetDirection() == eDirNext){
+ return mAnchorFocusRange->EndRef();
+ }
+
+ return mAnchorFocusRange->StartRef();
+}
NS_IMETHODIMP
Selection::GetAnchorNode(nsIDOMNode** aAnchorNode)
{
nsINode* anchorNode = GetAnchorNode();
if (anchorNode) {
return CallQueryInterface(anchorNode, aAnchorNode);
}
*aAnchorNode = nullptr;
return NS_OK;
}
-nsINode*
-Selection::GetAnchorNode()
-{
- if (!mAnchorFocusRange)
- return nullptr;
-
- if (GetDirection() == eDirNext) {
- return mAnchorFocusRange->GetStartContainer();
- }
-
- return mAnchorFocusRange->GetEndContainer();
-}
-
NS_IMETHODIMP
Selection::GetAnchorOffset(int32_t* aAnchorOffset)
{
*aAnchorOffset = static_cast<int32_t>(AnchorOffset());
return NS_OK;
}
// note: this can return a nil focus node
@@ -857,29 +873,16 @@ Selection::GetFocusNode(nsIDOMNode** aFo
if (focusNode) {
return CallQueryInterface(focusNode, aFocusNode);
}
*aFocusNode = nullptr;
return NS_OK;
}
-nsINode*
-Selection::GetFocusNode()
-{
- if (!mAnchorFocusRange)
- return nullptr;
-
- if (GetDirection() == eDirNext){
- return mAnchorFocusRange->GetEndContainer();
- }
-
- return mAnchorFocusRange->GetStartContainer();
-}
-
NS_IMETHODIMP
Selection::GetFocusOffset(int32_t* aFocusOffset)
{
*aFocusOffset = static_cast<int32_t>(FocusOffset());
return NS_OK;
}
void
@@ -891,70 +894,16 @@ Selection::SetAnchorFocusRange(int32_t i
{
mAnchorFocusRange = nullptr;
}
else{
mAnchorFocusRange = mRanges[indx].mRange;
}
}
-uint32_t
-Selection::AnchorOffset()
-{
- if (!mAnchorFocusRange)
- return 0;
-
- if (GetDirection() == eDirNext){
- return mAnchorFocusRange->StartOffset();
- }
-
- return mAnchorFocusRange->EndOffset();
-}
-
-uint32_t
-Selection::FocusOffset()
-{
- if (!mAnchorFocusRange)
- return 0;
-
- if (GetDirection() == eDirNext){
- return mAnchorFocusRange->EndOffset();
- }
-
- return mAnchorFocusRange->StartOffset();
-}
-
-nsIContent*
-Selection::GetChildAtAnchorOffset()
-{
- if (!mAnchorFocusRange) {
- return nullptr;
- }
-
- if (GetDirection() == eDirNext) {
- return mAnchorFocusRange->GetChildAtStartOffset();
- }
-
- return mAnchorFocusRange->GetChildAtEndOffset();
-}
-
-nsIContent*
-Selection::GetChildAtFocusOffset()
-{
- if (!mAnchorFocusRange) {
- return nullptr;
- }
-
- if (GetDirection() == eDirNext){
- return mAnchorFocusRange->GetChildAtEndOffset();
- }
-
- return mAnchorFocusRange->GetChildAtStartOffset();
-}
-
static nsresult
CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
nsRange* aRange, int32_t* aCmp)
{
nsINode* start = aRange->GetStartContainer();
NS_ENSURE_STATE(aCompareNode && start);
// If the nodes that we're comparing are not in the same document,
// assume that aCompareNode will fall at the end of the ranges.
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -180,23 +180,50 @@ public:
const nsPoint& aPoint,
uint32_t aDelay);
nsresult StopAutoScrollTimer();
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// WebIDL methods
- nsINode* GetAnchorNode();
- uint32_t AnchorOffset();
- nsINode* GetFocusNode();
- uint32_t FocusOffset();
+ nsINode* GetAnchorNode()
+ {
+ const RangeBoundary& anchor = AnchorRef();
+ return anchor.IsSet() ? anchor.Container() : nullptr;
+ }
+ uint32_t AnchorOffset()
+ {
+ const RangeBoundary& anchor = AnchorRef();
+ return anchor.IsSet() ? anchor.Offset() : 0;
+ }
+ nsINode* GetFocusNode()
+ {
+ const RangeBoundary& focus = FocusRef();
+ return focus.IsSet() ? focus.Container() : nullptr;
+ }
+ uint32_t FocusOffset()
+ {
+ const RangeBoundary& focus = FocusRef();
+ return focus.IsSet() ? focus.Offset() : 0;
+ }
- nsIContent* GetChildAtAnchorOffset();
- nsIContent* GetChildAtFocusOffset();
+ nsIContent* GetChildAtAnchorOffset()
+ {
+ const RangeBoundary& anchor = AnchorRef();
+ return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr;
+ }
+ nsIContent* GetChildAtFocusOffset()
+ {
+ const RangeBoundary& focus = FocusRef();
+ return focus.IsSet() ? focus.GetChildAtOffset() : nullptr;
+ }
+
+ const RangeBoundary& AnchorRef();
+ const RangeBoundary& FocusRef();
/*
* IsCollapsed -- is the whole selection just one point, or unset?
*/
bool IsCollapsed() const
{
uint32_t cnt = mRanges.Length();
if (cnt == 0) {
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -4478,23 +4478,19 @@ already_AddRefed<Element>
EditorBase::DeleteSelectionAndCreateElement(nsAtom& aTag)
{
nsresult rv = DeleteSelectionAndPrepareToCreateNode();
NS_ENSURE_SUCCESS(rv, nullptr);
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, nullptr);
- EditorRawDOMPoint pointToInsert(selection->GetChildAtAnchorOffset());
+ EditorRawDOMPoint pointToInsert(selection->AnchorRef());
if (!pointToInsert.IsSet()) {
- // Perhaps, the anchor point is in a text node.
- pointToInsert.Set(selection->GetAnchorNode(), selection->AnchorOffset());
- if (NS_WARN_IF(!pointToInsert.IsSet())) {
- return nullptr;
- }
+ return nullptr;
}
RefPtr<Element> newElement = CreateNode(&aTag, pointToInsert);
// We want the selection to be just after the new node
DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
NS_WARNING_ASSERTION(advanced,
"Failed to move offset next to the new element");
ErrorResult error;
@@ -4543,66 +4539,59 @@ EditorBase::DeleteSelectionAndPrepareToC
MOZ_ASSERT(selection->GetAnchorFocusRange() &&
selection->GetAnchorFocusRange()->Collapsed(),
"Selection not collapsed after delete");
}
// If the selection is a chardata node, split it if necessary and compute
// where to put the new node
- nsCOMPtr<nsINode> node = selection->GetAnchorNode();
- MOZ_ASSERT(node, "Selection has no ranges in it");
-
- if (!node || !node->IsNodeOfType(nsINode::eDATA_NODE)) {
+ EditorDOMPoint atAnchor(selection->AnchorRef());
+ if (NS_WARN_IF(!atAnchor.IsSet()) ||
+ !atAnchor.Container()->IsNodeOfType(nsINode::eDATA_NODE)) {
return NS_OK;
}
- NS_ASSERTION(node->GetParentNode(),
- "It's impossible to insert into chardata with no parent -- "
- "fix the caller");
- NS_ENSURE_STATE(node->GetParentNode());
-
- // XXX We want Selection::AnchorRef()
- uint32_t offset = selection->AnchorOffset();
-
- if (!offset) {
- EditorRawDOMPoint atNode(node);
- if (NS_WARN_IF(!atNode.IsSetAndValid())) {
+ if (NS_WARN_IF(!atAnchor.Container()->GetParentNode())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (atAnchor.IsStartOfContainer()) {
+ EditorRawDOMPoint atAnchorContainer(atAnchor.Container());
+ if (NS_WARN_IF(!atAnchorContainer.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
ErrorResult error;
- selection->Collapse(atNode, error);
+ selection->Collapse(atAnchorContainer, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
- if (offset == node->Length()) {
- EditorRawDOMPoint afterNode(node);
- if (NS_WARN_IF(!afterNode.AdvanceOffset())) {
+ if (atAnchor.IsEndOfContainer()) {
+ EditorRawDOMPoint afterAnchorContainer(atAnchor.Container());
+ if (NS_WARN_IF(!afterAnchorContainer.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
ErrorResult error;
- selection->Collapse(afterNode, error);
+ selection->Collapse(afterAnchorContainer, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
- EditorRawDOMPoint atStartOfRightNode(node, offset);
- MOZ_ASSERT(atStartOfRightNode.IsSetAndValid());
ErrorResult error;
- nsCOMPtr<nsIContent> newLeftNode = SplitNode(atStartOfRightNode, error);
+ nsCOMPtr<nsIContent> newLeftNode = SplitNode(atAnchor.AsRaw(), error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
- EditorRawDOMPoint atRightNode(atStartOfRightNode.Container());
+ EditorRawDOMPoint atRightNode(atAnchor.Container());
if (NS_WARN_IF(!atRightNode.IsSet())) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(atRightNode.IsSetAndValid());
selection->Collapse(atRightNode, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1470,22 +1470,17 @@ HTMLEditor::InsertElementAtSelection(nsI
if (HTMLEditUtils::IsNamedAnchor(node)) {
selection->CollapseToStart();
} else {
selection->CollapseToEnd();
}
}
if (selection->GetAnchorNode()) {
- EditorRawDOMPoint atAnchor;
- if (selection->GetChildAtAnchorOffset()) {
- atAnchor.Set(selection->GetChildAtAnchorOffset());
- } else {
- atAnchor.Set(selection->GetAnchorNode(), selection->AnchorOffset());
- }
+ EditorRawDOMPoint atAnchor(selection->AnchorRef());
// Adjust position based on the node we are going to insert.
EditorRawDOMPoint pointToInsert =
GetBetterInsertionPointFor(*element, atAnchor);
if (NS_WARN_IF(!pointToInsert.IsSet())) {
return NS_ERROR_FAILURE;
}
EditorDOMPoint insertedPoint =
@@ -2242,28 +2237,33 @@ HTMLEditor::GetElementOrParentByTagName(
nsINode* aNode)
{
MOZ_ASSERT(!aTagName.IsEmpty());
nsCOMPtr<nsINode> node = aNode;
if (!node) {
// If no node supplied, get it from anchor node of current selection
RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, nullptr);
-
- nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
- NS_ENSURE_TRUE(anchorNode, nullptr);
+ if (NS_WARN_IF(!selection)) {
+ return nullptr;
+ }
+
+ const EditorDOMPoint atAnchor(selection->AnchorRef());
+ if (NS_WARN_IF(!atAnchor.IsSet())) {
+ return nullptr;
+ }
// Try to get the actual selected node
- if (anchorNode->HasChildNodes() && anchorNode->IsContent()) {
- node = selection->GetChildAtAnchorOffset();
+ if (atAnchor.Container()->HasChildNodes() &&
+ atAnchor.Container()->IsContent()) {
+ node = atAnchor.GetChildAtOffset();
}
// Anchor node is probably a text node - just use that
if (!node) {
- node = anchorNode;
+ node = atAnchor.Container();
}
}
nsCOMPtr<Element> current;
if (node->IsElement()) {
current = node->AsElement();
} else if (node->GetParentElement()) {
current = node->GetParentElement();
@@ -2390,57 +2390,54 @@ HTMLEditor::GetSelectedElement(const nsA
}
}
if (!bNodeFound) {
if (isLinkTag) {
// Link tag is a special case - we return the anchor node
// found for any selection that is totally within a link,
// included a collapsed selection (just a caret in a link)
- nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
- nsIContent* anchorChild = selection->GetChildAtAnchorOffset();
-
- nsCOMPtr<nsINode> focusNode = selection->GetFocusNode();
- nsIContent* focusChild = selection->GetChildAtFocusOffset();
-
+ const RangeBoundary& anchor = selection->AnchorRef();
+ const RangeBoundary& focus = selection->FocusRef();
// Link node must be the same for both ends of selection
- if (anchorNode) {
+ if (anchor.IsSet()) {
nsCOMPtr<nsIDOMElement> parentLinkOfAnchor;
nsresult rv =
GetElementOrParentByTagName(NS_LITERAL_STRING("href"),
- GetAsDOMNode(anchorNode),
+ anchor.Container()->AsDOMNode(),
getter_AddRefs(parentLinkOfAnchor));
// XXX: ERROR_HANDLING can parentLinkOfAnchor be null?
if (NS_SUCCEEDED(rv) && parentLinkOfAnchor) {
if (isCollapsed) {
// We have just a caret in the link
bNodeFound = true;
- } else if (focusNode) {
+ } else if (focus.IsSet()) {
// Link node must be the same for both ends of selection.
nsCOMPtr<nsIDOMElement> parentLinkOfFocus;
rv = GetElementOrParentByTagName(NS_LITERAL_STRING("href"),
- GetAsDOMNode(focusNode),
+ focus.Container()->AsDOMNode(),
getter_AddRefs(parentLinkOfFocus));
if (NS_SUCCEEDED(rv) && parentLinkOfFocus == parentLinkOfAnchor) {
bNodeFound = true;
}
}
// We found a link node parent
if (bNodeFound) {
// GetElementOrParentByTagName addref'd this, so we don't need to do it here
parentLinkOfAnchor.forget(aReturn);
return NS_OK;
}
- } else if (anchorChild && focusChild) {
+ } else if (anchor.GetChildAtOffset() && focus.GetChildAtOffset()) {
// Check if link node is the only thing selected
- if (HTMLEditUtils::IsLink(anchorChild) &&
- anchorNode == focusNode &&
- focusChild == anchorChild->GetNextSibling()) {
- selectedElement = do_QueryInterface(anchorChild);
+ if (HTMLEditUtils::IsLink(anchor.GetChildAtOffset()) &&
+ anchor.Container() == focus.Container() &&
+ focus.GetChildAtOffset() ==
+ anchor.GetChildAtOffset()->GetNextSibling()) {
+ selectedElement = do_QueryInterface(anchor.GetChildAtOffset());
bNodeFound = true;
}
}
}
}
if (!isCollapsed) {
RefPtr<nsRange> currange = selection->GetRangeAt(0);