--- a/dom/base/RangeBoundary.h
+++ b/dom/base/RangeBoundary.h
@@ -198,23 +198,51 @@ public:
mOffset = mozilla::Some(mParent->GetChildCount());
return mOffset.value();
}
// Use nsINode::IndexOf() as the last resort due to being expensive.
mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
return mOffset.value();
}
+ /**
+ * Set() sets a point to aOffset or aChild.
+ * If it's set with offset, mRef is invalidated. If it's set with aChild,
+ * mOffset may be invalidated unless the offset can be computed simply.
+ */
void
Set(nsINode* aContainer, int32_t aOffset)
{
mParent = aContainer;
mRef = nullptr;
mOffset = mozilla::Some(aOffset);
}
+ void
+ Set(const nsIContent* aChild)
+ {
+ MOZ_ASSERT(aChild);
+ mParent = aChild->GetParentNode();
+ mRef = aChild->GetPreviousSibling();
+ if (!mRef) {
+ mOffset = mozilla::Some(0);
+ } else {
+ mOffset.reset();
+ }
+ }
+
+ /**
+ * Clear() makes the instance not point anywhere.
+ */
+ void
+ Clear()
+ {
+ mParent = nullptr;
+ mRef = nullptr;
+ mOffset.reset();
+ }
/**
* AdvanceOffset() tries to reference next sibling of mRef if its container
* can have children or increments offset if the container is a text node or
* something.
* If the container can have children and there is no next sibling, this
* outputs warning and does nothing. So, callers need to check if there is
* next sibling which you need to refer.
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -3964,17 +3964,17 @@ EditorBase::SplitNodeDeep(nsIContent& aN
nsIContent& aSplitPointParent,
int32_t aSplitPointOffset,
EmptyContainers aEmptyContainers,
nsIContent** aOutLeftNode,
nsIContent** aOutRightNode,
nsCOMPtr<nsIContent>* ioChildAtSplitPointOffset)
{
MOZ_ASSERT(&aSplitPointParent == &aNode ||
- EditorUtils::IsDescendantOf(&aSplitPointParent, &aNode));
+ EditorUtils::IsDescendantOf(aSplitPointParent, aNode));
int32_t offset = aSplitPointOffset;
nsCOMPtr<nsIContent> leftNode, rightNode;
OwningNonNull<nsIContent> nodeToSplit = aSplitPointParent;
while (true) {
// Need to insert rules code call here to do things like not split a list
// if you are after the last <li> or before the first, etc. For now we
// just have some smarts about unneccessarily splitting text nodes, which
--- a/editor/libeditor/EditorUtils.cpp
+++ b/editor/libeditor/EditorUtils.cpp
@@ -1,15 +1,16 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/EditorUtils.h"
+#include "mozilla/EditorDOMPoint.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/dom/Selection.h"
#include "nsComponentManagerUtils.h"
#include "nsError.h"
#include "nsIClipboardDragDropHookList.h"
// hooks
#include "nsIClipboardDragDropHooks.h"
#include "nsIContent.h"
@@ -130,70 +131,64 @@ DOMSubtreeIterator::~DOMSubtreeIterator(
{
}
/******************************************************************************
* some general purpose editor utils
*****************************************************************************/
bool
-EditorUtils::IsDescendantOf(nsINode* aNode,
- nsINode* aParent,
- int32_t* aOffset)
+EditorUtils::IsDescendantOf(const nsINode& aNode,
+ const nsINode& aParent,
+ EditorRawDOMPoint* aOutPoint /* = nullptr */)
{
- MOZ_ASSERT(aNode && aParent);
- if (aNode == aParent) {
+ if (aOutPoint) {
+ aOutPoint->Clear();
+ }
+
+ if (&aNode == &aParent) {
return false;
}
- for (nsCOMPtr<nsINode> node = aNode; node; node = node->GetParentNode()) {
- if (node->GetParentNode() == aParent) {
- if (aOffset) {
- *aOffset = aParent->IndexOf(node);
+ for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
+ if (node->GetParentNode() == &aParent) {
+ if (aOutPoint) {
+ MOZ_ASSERT(node->IsContent());
+ aOutPoint->Set(node->AsContent());
}
return true;
}
}
return false;
}
bool
-EditorUtils::IsDescendantOf(nsINode* aNode,
- nsINode* aParent,
- nsIContent** aChild)
+EditorUtils::IsDescendantOf(const nsINode& aNode,
+ const nsINode& aParent,
+ EditorDOMPoint* aOutPoint)
{
- MOZ_ASSERT(aNode && aParent && aChild);
- *aChild = nullptr;
- if (aNode == aParent) {
+ MOZ_ASSERT(aOutPoint);
+ aOutPoint->Clear();
+ if (&aNode == &aParent) {
return false;
}
- for (nsCOMPtr<nsINode> node = aNode; node; node = node->GetParentNode()) {
- if (node->GetParentNode() == aParent) {
- *aChild = node->AsContent();
+ for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
+ if (node->GetParentNode() == &aParent) {
+ MOZ_ASSERT(node->IsContent());
+ aOutPoint->Set(node->AsContent());
return true;
}
}
return false;
}
bool
-EditorUtils::IsDescendantOf(nsIDOMNode* aNode,
- nsIDOMNode* aParent,
- int32_t* aOffset)
-{
- nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
- nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
- NS_ENSURE_TRUE(node && parent, false);
- return IsDescendantOf(node, parent, aOffset);
-}
-
-bool
EditorUtils::IsLeafNode(nsIDOMNode* aNode)
{
bool hasChildren = false;
if (aNode)
aNode->HasChildNodes(&hasChildren);
return !hasChildren;
}
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -4,16 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_EditorUtils_h
#define mozilla_EditorUtils_h
#include "mozilla/dom/Selection.h"
#include "mozilla/EditorBase.h"
+#include "mozilla/EditorDOMPoint.h"
#include "mozilla/GuardObjects.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsIDOMNode.h"
#include "nsIEditor.h"
#include "nscore.h"
class nsAtom;
@@ -373,23 +374,29 @@ public:
{
return true;
}
};
class EditorUtils final
{
public:
- // Note that aChild isn't a normal XPCOM outparam and won't get AddRef'ed.
- static bool IsDescendantOf(nsINode* aNode, nsINode* aParent,
- nsIContent** aChild);
- static bool IsDescendantOf(nsINode* aNode, nsINode* aParent,
- int32_t* aOffset = nullptr);
- static bool IsDescendantOf(nsIDOMNode* aNode, nsIDOMNode* aParent,
- int32_t* aOffset = nullptr);
+ /**
+ * IsDescendantOf() checks if aNode is a child or a descendant of aParent.
+ * aOutPoint is set to the child of aParent.
+ *
+ * @return true if aNode is a child or a descendant of aParent.
+ */
+ static bool IsDescendantOf(const nsINode& aNode,
+ const nsINode& aParent,
+ EditorRawDOMPoint* aOutPoint = nullptr);
+ static bool IsDescendantOf(const nsINode& aNode,
+ const nsINode& aParent,
+ EditorDOMPoint* aOutPoint);
+
static bool IsLeafNode(nsIDOMNode* aNode);
};
class EditorHookUtils final
{
public:
static bool DoInsertionHook(nsIDOMDocument* aDoc, nsIDOMEvent* aEvent,
nsITransferable* aTrans);
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -2853,221 +2853,240 @@ HTMLEditRules::TryToJoinBlocks(nsIConten
rightBlock->GetParentNode() == leftBlock) {
return EditActionHandled();
}
// Special rule here: if we are trying to join list items, and they are in
// different lists, join the lists instead.
bool mergeLists = false;
nsAtom* existingList = nsGkAtoms::_empty;
- nsIContent* childInBlock = nullptr;
+ EditorDOMPoint childInBlock;
nsCOMPtr<Element> leftList, rightList;
if (HTMLEditUtils::IsListItem(leftBlock) &&
HTMLEditUtils::IsListItem(rightBlock)) {
leftList = leftBlock->GetParentElement();
rightList = rightBlock->GetParentElement();
if (leftList && rightList && leftList != rightList &&
- !EditorUtils::IsDescendantOf(leftList, rightBlock, &childInBlock) &&
- !EditorUtils::IsDescendantOf(rightList, leftBlock, &childInBlock)) {
+ !EditorUtils::IsDescendantOf(*leftList, *rightBlock, &childInBlock) &&
+ !EditorUtils::IsDescendantOf(*rightList, *leftBlock, &childInBlock)) {
// There are some special complications if the lists are descendants of
// the other lists' items. Note that it is okay for them to be
// descendants of the other lists themselves, which is the usual case for
// sublists in our implementation.
+ MOZ_DIAGNOSTIC_ASSERT(!childInBlock.IsSet());
leftBlock = leftList;
rightBlock = rightList;
mergeLists = true;
existingList = leftList->NodeInfo()->NameAtom();
}
}
AutoTransactionsConserveSelection dontChangeMySelection(htmlEditor);
- int32_t rightOffset = 0;
- int32_t leftOffset = -1;
-
// offset below is where you find yourself in rightBlock when you traverse
// upwards from leftBlock
- if (EditorUtils::IsDescendantOf(leftBlock, rightBlock, &rightOffset)) {
+ EditorDOMPoint rightBlockChild;
+ if (EditorUtils::IsDescendantOf(*leftBlock, *rightBlock, &rightBlockChild)) {
// Tricky case. Left block is inside right block. Do ws adjustment. This
// just destroys non-visible ws at boundaries we will be joining.
- rightOffset++;
+ rightBlockChild.AdvanceOffset();
nsresult rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
WSRunObject::kBlockEnd,
leftBlock);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionIgnored(rv);
}
{
// We can't just track rightBlock because it's an Element.
- nsCOMPtr<nsINode> trackingRightBlock(rightBlock);
- AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater,
- address_of(trackingRightBlock), &rightOffset);
+ AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater, &rightBlockChild);
rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
WSRunObject::kAfterBlock,
- rightBlock, rightOffset);
+ rightBlock,
+ rightBlockChild.Offset());
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionIgnored(rv);
}
- if (trackingRightBlock->IsElement()) {
- rightBlock = trackingRightBlock->AsElement();
+ // XXX AutoTrackDOMPoint instance, tracker, hasn't been destroyed here.
+ // Do we really need to do update rightBlock here??
+ MOZ_ASSERT(rightBlock == rightBlockChild.Container());
+ if (rightBlockChild.Container()->IsElement()) {
+ rightBlock = rightBlockChild.Container()->AsElement();
} else {
- if (NS_WARN_IF(!trackingRightBlock->GetParentElement())) {
+ if (NS_WARN_IF(!rightBlockChild.Container()->GetParentElement())) {
return EditActionIgnored(NS_ERROR_UNEXPECTED);
}
- rightBlock = trackingRightBlock->GetParentElement();
+ rightBlock = rightBlockChild.Container()->GetParentElement();
}
}
// Do br adjustment.
nsCOMPtr<Element> brNode =
CheckForInvisibleBR(*leftBlock, BRLocation::blockEnd);
EditActionResult ret(NS_OK);
if (mergeLists) {
// The idea here is to take all children in rightList that are past
// offset, and pull them into leftlist.
- for (nsCOMPtr<nsIContent> child = childInBlock;
- child; child = rightList->GetChildAt(rightOffset)) {
+ // XXX Looks like that when mergeLists is true, childInBlock has never
+ // been set. So, this block must be dead code.
+ MOZ_DIAGNOSTIC_ASSERT(childInBlock.IsSet());
+ uint32_t offset = rightBlockChild.Offset();
+ for (nsCOMPtr<nsIContent> child = childInBlock.GetChildAtOffset();
+ child; child = rightList->GetChildAt(offset)) {
rv = htmlEditor->MoveNode(child, leftList, -1);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionIgnored(rv);
}
}
// XXX Should this set to true only when above for loop moves the node?
ret.MarkAsHandled();
+ // childInBlock and rightBlockChild were moved to leftList. So, they
+ // are now invalid.
+ rightBlockChild.Clear();
+ childInBlock.Clear();
} else {
// XXX Why do we ignore the result of MoveBlock()?
EditActionResult retMoveBlock =
- MoveBlock(*leftBlock, *rightBlock, leftOffset, rightOffset);
+ MoveBlock(*leftBlock, *rightBlock,
+ -1, rightBlockChild.Offset());
if (retMoveBlock.Handled()) {
ret.MarkAsHandled();
}
+ // Now, all children of rightBlock were moved to leftBlock. So,
+ // rightBlockChild is now invalid.
+ rightBlockChild.Clear();
}
if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
ret.MarkAsHandled();
}
return ret;
}
+ MOZ_DIAGNOSTIC_ASSERT(!rightBlockChild.IsSet());
+
// Offset below is where you find yourself in leftBlock when you traverse
// upwards from rightBlock
- if (EditorUtils::IsDescendantOf(rightBlock, leftBlock, &leftOffset)) {
+ EditorDOMPoint leftBlockChild;
+ if (EditorUtils::IsDescendantOf(*rightBlock, *leftBlock, &leftBlockChild)) {
// Tricky case. Right block is inside left block. Do ws adjustment. This
// just destroys non-visible ws at boundaries we will be joining.
nsresult rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
WSRunObject::kBlockStart,
rightBlock);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionIgnored(rv);
}
{
// We can't just track leftBlock because it's an Element, so track
// something else.
- nsCOMPtr<nsINode> trackingLeftBlock(leftBlock);
- AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater,
- address_of(trackingLeftBlock), &leftOffset);
+ AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater, &leftBlockChild);
rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
WSRunObject::kBeforeBlock,
- leftBlock, leftOffset);
+ leftBlock, leftBlockChild.Offset());
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionIgnored(rv);
}
-
- if (trackingLeftBlock->IsElement()) {
- leftBlock = trackingLeftBlock->AsElement();
+ // XXX AutoTrackDOMPoint instance, tracker, hasn't been destroyed here.
+ // Do we really need to do update rightBlock here??
+ MOZ_DIAGNOSTIC_ASSERT(leftBlock == leftBlockChild.Container());
+ if (leftBlockChild.Container()->IsElement()) {
+ leftBlock = leftBlockChild.Container()->AsElement();
} else {
- if (NS_WARN_IF(!trackingLeftBlock->GetParentElement())) {
+ if (NS_WARN_IF(!leftBlockChild.Container()->GetParentElement())) {
return EditActionIgnored(NS_ERROR_UNEXPECTED);
}
- leftBlock = trackingLeftBlock->GetParentElement();
+ leftBlock = leftBlockChild.Container()->GetParentElement();
}
}
// Do br adjustment.
nsCOMPtr<Element> brNode =
- CheckForInvisibleBR(*leftBlock, BRLocation::beforeBlock, leftOffset);
+ CheckForInvisibleBR(*leftBlock, BRLocation::beforeBlock,
+ leftBlockChild.Offset());
EditActionResult ret(NS_OK);
if (mergeLists) {
// XXX Why do we ignore the result of MoveContents()?
+ int32_t offset = leftBlockChild.Offset();
EditActionResult retMoveContents =
- MoveContents(*rightList, *leftList, &leftOffset);
+ MoveContents(*rightList, *leftList, &offset);
if (retMoveContents.Handled()) {
ret.MarkAsHandled();
}
+ // leftBlockChild was moved to rightList. So, it's invalid now.
+ leftBlockChild.Clear();
} else {
// Left block is a parent of right block, and the parent of the previous
// visible content. Right block is a child and contains the contents we
// want to move.
- int32_t previousContentOffset;
- nsCOMPtr<nsINode> previousContentParent;
-
+ EditorDOMPoint previousContent;
if (&aLeftNode == leftBlock) {
// We are working with valid HTML, aLeftNode is a block node, and is
// therefore allowed to contain rightBlock. This is the simple case,
// we will simply move the content in rightBlock out of its block.
- previousContentParent = leftBlock;
- previousContentOffset = leftOffset;
+ previousContent = leftBlockChild;
} else {
// We try to work as well as possible with HTML that's already invalid.
// Although "right block" is a block, and a block must not be contained
// in inline elements, reality is that broken documents do exist. The
// DIRECT parent of "left NODE" might be an inline element. Previous
// versions of this code skipped inline parents until the first block
// parent was found (and used "left block" as the destination).
// However, in some situations this strategy moves the content to an
// unexpected position. (see bug 200416) The new idea is to make the
// moving content a sibling, next to the previous visible content.
-
- previousContentParent = aLeftNode.GetParentNode();
- previousContentOffset = previousContentParent ?
- previousContentParent->IndexOf(&aLeftNode) : -1;
+ previousContent.Set(&aLeftNode);
// We want to move our content just after the previous visible node.
- previousContentOffset++;
+ previousContent.AdvanceOffset();
}
// Because we don't want the moving content to receive the style of the
// previous content, we split the previous content's style.
nsCOMPtr<Element> editorRoot = htmlEditor->GetEditorRoot();
if (!editorRoot || &aLeftNode != editorRoot) {
nsCOMPtr<nsIContent> splittedPreviousContent;
+ nsCOMPtr<nsINode> previousContentParent = previousContent.Container();
+ int32_t previousContentOffset = previousContent.Offset();
rv = htmlEditor->SplitStyleAbovePoint(
address_of(previousContentParent),
&previousContentOffset,
nullptr, nullptr, nullptr,
getter_AddRefs(splittedPreviousContent));
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditActionIgnored(rv);
}
if (splittedPreviousContent) {
- previousContentParent = splittedPreviousContent->GetParentNode();
- previousContentOffset = previousContentParent ?
- previousContentParent->IndexOf(splittedPreviousContent) : -1;
+ previousContent.Set(splittedPreviousContent);
+ } else {
+ previousContent.Set(previousContentParent, previousContentOffset);
}
}
- if (NS_WARN_IF(!previousContentParent)) {
+ if (NS_WARN_IF(!previousContent.IsSet())) {
return EditActionIgnored(NS_ERROR_NULL_POINTER);
}
- ret |= MoveBlock(*previousContentParent->AsElement(), *rightBlock,
- previousContentOffset, rightOffset);
+ ret |= MoveBlock(*previousContent.Container()->AsElement(), *rightBlock,
+ previousContent.Offset(), 0);
if (NS_WARN_IF(ret.Failed())) {
return ret;
}
}
if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
ret.MarkAsHandled();
}
return ret;
}
+ MOZ_DIAGNOSTIC_ASSERT(!rightBlockChild.IsSet());
+ MOZ_DIAGNOSTIC_ASSERT(!leftBlockChild.IsSet());
+
// Normal case. Blocks are siblings, or at least close enough. An example
// of the latter is <p>paragraph</p><ul><li>one<li>two<li>three</ul>. The
// first li and the p are not true siblings, but we still want to join them
// if you backspace from li into p.
// Adjust whitespace at block boundaries
nsresult rv =
WSRunObject::PrepareToJoinBlocks(htmlEditor, leftBlock, rightBlock);
@@ -3084,17 +3103,17 @@ HTMLEditRules::TryToJoinBlocks(nsIConten
EditorDOMPoint pt = JoinNodesSmart(*leftBlock, *rightBlock);
if (pt.IsSet() && mergeLists) {
RefPtr<Element> newBlock =
ConvertListType(rightBlock, existingList, nsGkAtoms::li);
}
ret.MarkAsHandled();
} else {
// Nodes are dissimilar types.
- ret |= MoveBlock(*leftBlock, *rightBlock, leftOffset, rightOffset);
+ ret |= MoveBlock(*leftBlock, *rightBlock, -1, 0);
if (NS_WARN_IF(ret.Failed())) {
return ret;
}
}
if (brNode) {
rv = htmlEditor->DeleteNode(brNode);
// XXX In other top level if blocks, the result of DeleteNode()
// is ignored. Why does only this result is respected?
@@ -3433,17 +3452,17 @@ HTMLEditRules::WillMakeList(Selection* a
if (TextEditUtils::IsBreak(curNode)) {
prevListItem = nullptr;
}
continue;
}
if (HTMLEditUtils::IsList(curNode)) {
// do we have a curList already?
- if (curList && !EditorUtils::IsDescendantOf(curNode, curList)) {
+ if (curList && !EditorUtils::IsDescendantOf(*curNode, *curList)) {
// move all of our children into curList. cheezy way to do it: move
// whole list and then RemoveContainer() on the list. ConvertListType
// first: that routine handles converting the list item types, if
// needed
NS_ENSURE_STATE(mHTMLEditor);
rv = mHTMLEditor->MoveNode(curNode, curList, -1);
NS_ENSURE_SUCCESS(rv, rv);
newBlock = ConvertListType(curNode->AsElement(), listType, itemType);
@@ -3464,17 +3483,17 @@ HTMLEditRules::WillMakeList(Selection* a
continue;
}
if (HTMLEditUtils::IsListItem(curNode)) {
NS_ENSURE_STATE(mHTMLEditor);
if (!curParent->IsHTMLElement(listType)) {
// list item is in wrong type of list. if we don't have a curList,
// split the old list and make a new list of correct type.
- if (!curList || EditorUtils::IsDescendantOf(curNode, curList)) {
+ if (!curList || EditorUtils::IsDescendantOf(*curNode, *curList)) {
NS_ENSURE_STATE(mHTMLEditor);
NS_ENSURE_STATE(curParent->IsContent());
ErrorResult rv;
nsCOMPtr<nsIContent> splitNode =
mHTMLEditor->SplitNode(*curParent->AsContent(), offset, rv);
NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
newBlock = splitNode ? splitNode->AsElement() : nullptr;
int32_t offset;
@@ -4400,17 +4419,17 @@ HTMLEditRules::WillOutdent(Selection& aS
}
rv = PopListItem(*curNode->AsContent());
NS_ENSURE_SUCCESS(rv, rv);
continue;
}
// Do we have a blockquote that we are already committed to removing?
if (curBlockQuote) {
// If so, is this node a descendant?
- if (EditorUtils::IsDescendantOf(curNode, curBlockQuote)) {
+ if (EditorUtils::IsDescendantOf(*curNode, *curBlockQuote)) {
lastBQChild = curNode;
// Then we don't need to do anything different for this node
continue;
}
// Otherwise, we have progressed beyond end of curBlockQuote, so
// let's handle it now. We need to remove the portion of
// curBlockQuote that contains [firstBQChild - lastBQChild].
rv = OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
@@ -4527,28 +4546,28 @@ HTMLEditRules::WillOutdent(Selection& aS
if (aSelection.Collapsed()) {
// Push selection past end of rememberedLeftBQ
NS_ENSURE_TRUE(aSelection.GetRangeAt(0), NS_OK);
nsCOMPtr<nsINode> startNode =
aSelection.GetRangeAt(0)->GetStartContainer();
int32_t startOffset = aSelection.GetRangeAt(0)->StartOffset();
if (rememberedLeftBQ &&
(startNode == rememberedLeftBQ ||
- EditorUtils::IsDescendantOf(startNode, rememberedLeftBQ))) {
+ EditorUtils::IsDescendantOf(*startNode, *rememberedLeftBQ))) {
// Selection is inside rememberedLeftBQ - push it past it.
startNode = rememberedLeftBQ->GetParentNode();
startOffset = startNode ? 1 + startNode->IndexOf(rememberedLeftBQ) : 0;
aSelection.Collapse(startNode, startOffset);
}
// And pull selection before beginning of rememberedRightBQ
startNode = aSelection.GetRangeAt(0)->GetStartContainer();
startOffset = aSelection.GetRangeAt(0)->StartOffset();
if (rememberedRightBQ &&
(startNode == rememberedRightBQ ||
- EditorUtils::IsDescendantOf(startNode, rememberedRightBQ))) {
+ EditorUtils::IsDescendantOf(*startNode, *rememberedRightBQ))) {
// Selection is inside rememberedRightBQ - push it before it.
startNode = rememberedRightBQ->GetParentNode();
startOffset = startNode ? startNode->IndexOf(rememberedRightBQ) : -1;
aSelection.Collapse(startNode, startOffset);
}
}
return NS_OK;
}
@@ -4579,18 +4598,18 @@ void
HTMLEditRules::SplitBlock(Element& aBlock,
nsIContent& aStartChild,
nsIContent& aEndChild,
nsIContent** aOutLeftNode,
nsIContent** aOutRightNode,
nsIContent** aOutMiddleNode)
{
// aStartChild and aEndChild must be exclusive descendants of aBlock
- MOZ_ASSERT(EditorUtils::IsDescendantOf(&aStartChild, &aBlock) &&
- EditorUtils::IsDescendantOf(&aEndChild, &aBlock));
+ MOZ_ASSERT(EditorUtils::IsDescendantOf(aStartChild, aBlock) &&
+ EditorUtils::IsDescendantOf(aEndChild, aBlock));
NS_ENSURE_TRUE_VOID(mHTMLEditor);
RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
// Get split point location
OwningNonNull<nsIContent> startParent = *aStartChild.GetParent();
int32_t startOffset = startParent->IndexOf(&aStartChild);
// Do the splits!
@@ -6391,17 +6410,17 @@ HTMLEditRules::GetHighestInlineParent(ns
if (&aNode == host) {
return nullptr;
}
// If aNode is outside of the <body> element, we don't support to edit
// such elements for now.
// XXX This should be MOZ_ASSERT after fixing bug 1413131 for avoiding
// calling this expensive method.
- if (NS_WARN_IF(!EditorUtils::IsDescendantOf(&aNode, host))) {
+ if (NS_WARN_IF(!EditorUtils::IsDescendantOf(aNode, *host))) {
return nullptr;
}
// Looks for the highest inline parent in the editing host.
nsIContent* content = aNode.AsContent();
for (nsIContent* parent = content->GetParent();
parent && parent != host && IsInlineNode(*parent);
parent = parent->GetParent()) {
@@ -7083,17 +7102,17 @@ HTMLEditRules::RemoveBlockStyle(nsTArray
// Recursion time
nsTArray<OwningNonNull<nsINode>> childArray;
GetChildNodesForOperation(*curNode, childArray);
nsresult rv = RemoveBlockStyle(childArray);
NS_ENSURE_SUCCESS(rv, rv);
} else if (IsInlineNode(curNode)) {
if (curBlock) {
// If so, is this node a descendant?
- if (EditorUtils::IsDescendantOf(curNode, curBlock)) {
+ if (EditorUtils::IsDescendantOf(*curNode, *curBlock)) {
// Then we don't need to do anything different for this node
lastNode = curNode->AsContent();
continue;
}
// Otherwise, we have progressed beyond end of curBlock, so let's
// handle it now. We need to remove the portion of curBlock that
// contains [firstNode - lastNode].
nsresult rv = RemovePartOfBlock(*curBlock, *firstNode, *lastNode);
@@ -8155,31 +8174,31 @@ HTMLEditRules::SelectionEndpointInNode(n
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
nsINode* startContainer = range->GetStartContainer();
if (startContainer) {
if (aNode == startContainer) {
*aResult = true;
return NS_OK;
}
- if (EditorUtils::IsDescendantOf(startContainer, aNode)) {
+ if (EditorUtils::IsDescendantOf(*startContainer, *aNode)) {
*aResult = true;
return NS_OK;
}
}
nsINode* endContainer = range->GetEndContainer();
if (startContainer == endContainer) {
continue;
}
if (endContainer) {
if (aNode == endContainer) {
*aResult = true;
return NS_OK;
}
- if (EditorUtils::IsDescendantOf(endContainer, aNode)) {
+ if (EditorUtils::IsDescendantOf(*endContainer, *aNode)) {
*aResult = true;
return NS_OK;
}
}
}
return NS_OK;
}
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -753,17 +753,17 @@ HTMLEditor::IsBlockNode(nsINode* aNode)
*/
Element*
HTMLEditor::GetBlockNodeParent(nsINode* aNode,
nsINode* aAncestorLimiter)
{
MOZ_ASSERT(aNode);
MOZ_ASSERT(!aAncestorLimiter ||
aNode == aAncestorLimiter ||
- EditorUtils::IsDescendantOf(aNode, aAncestorLimiter),
+ EditorUtils::IsDescendantOf(*aNode, *aAncestorLimiter),
"aNode isn't in aAncestorLimiter");
// The caller has already reached the limiter.
if (aNode == aAncestorLimiter) {
return nullptr;
}
nsCOMPtr<nsINode> p = aNode->GetParentNode();
@@ -786,17 +786,17 @@ HTMLEditor::GetBlockNodeParent(nsINode*
* Returns the node if it's a block, otherwise GetBlockNodeParent
*/
Element*
HTMLEditor::GetBlock(nsINode& aNode,
nsINode* aAncestorLimiter)
{
MOZ_ASSERT(!aAncestorLimiter ||
&aNode == aAncestorLimiter ||
- EditorUtils::IsDescendantOf(&aNode, aAncestorLimiter),
+ EditorUtils::IsDescendantOf(aNode, *aAncestorLimiter),
"aNode isn't in aAncestorLimiter");
if (NodeIsBlockStatic(&aNode)) {
return aNode.AsElement();
}
return GetBlockNodeParent(&aNode);
}
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -437,17 +437,21 @@ HTMLEditor::DoInsertHTMLWithContext(cons
NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(!TextEditUtils::IsBody(curNode), NS_ERROR_FAILURE);
if (insertedContextParent) {
// if we had to insert something higher up in the paste hierarchy, we want to
// skip any further paste nodes that descend from that. Else we will paste twice.
- if (EditorUtils::IsDescendantOf(curNode, insertedContextParent)) {
+ nsCOMPtr<nsINode> insertedContextParentNode =
+ do_QueryInterface(insertedContextParent);
+ if (NS_WARN_IF(!insertedContextParentNode) ||
+ EditorUtils::IsDescendantOf(*nodeList[j],
+ *insertedContextParentNode)) {
continue;
}
}
// give the user a hand on table element insertion. if they have
// a table or table row on the clipboard, and are trying to insert
// into a table or table row, insert the appropriate children instead.
if (HTMLEditUtils::IsTableRow(curNode) &&
@@ -2375,17 +2379,17 @@ HTMLEditor::ReplaceOrphanedStructure(
// insert them twice.
uint32_t removedCount = 0;
uint32_t originalLength = aNodeArray.Length();
for (uint32_t i = 0; i < originalLength; i++) {
uint32_t idx = aStartOrEnd == StartOrEnd::start ?
(i - removedCount) : (originalLength - i - 1);
OwningNonNull<nsINode> endpoint = aNodeArray[idx];
if (endpoint == replaceNode ||
- EditorUtils::IsDescendantOf(endpoint, replaceNode)) {
+ EditorUtils::IsDescendantOf(*endpoint, *replaceNode)) {
aNodeArray.RemoveElementAt(idx);
removedCount++;
}
}
// Now replace the removed nodes with the structural parent
if (aStartOrEnd == StartOrEnd::end) {
aNodeArray.AppendElement(*replaceNode);
--- a/editor/libeditor/SelectionState.cpp
+++ b/editor/libeditor/SelectionState.cpp
@@ -282,25 +282,25 @@ RangeUpdater::SelAdjDeleteNode(nsINode*
}
if (item->mEndContainer == aNode) {
item->mEndContainer = parent;
item->mEndOffset = offset;
}
// check for range endpoints that are in descendants of aNode
nsCOMPtr<nsINode> oldStart;
- if (EditorUtils::IsDescendantOf(item->mStartContainer, aNode)) {
+ if (EditorUtils::IsDescendantOf(*item->mStartContainer, *aNode)) {
oldStart = item->mStartContainer; // save for efficiency hack below.
item->mStartContainer = parent;
item->mStartOffset = offset;
}
// avoid having to call IsDescendantOf() for common case of range startnode == range endnode.
if (item->mEndContainer == oldStart ||
- EditorUtils::IsDescendantOf(item->mEndContainer, aNode)) {
+ EditorUtils::IsDescendantOf(*item->mEndContainer, *aNode)) {
item->mEndContainer = parent;
item->mEndOffset = offset;
}
}
}
nsresult
RangeUpdater::SelAdjSplitNode(nsIContent& aOldRightNode,
--- a/editor/libeditor/SelectionState.h
+++ b/editor/libeditor/SelectionState.h
@@ -1,16 +1,17 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_SelectionState_h
#define mozilla_SelectionState_h
+#include "mozilla/EditorDOMPoint.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsINode.h"
#include "nsTArray.h"
#include "nscore.h"
class nsCycleCollectionTraversalCallback;
class nsIDOMCharacterData;
@@ -175,52 +176,75 @@ ImplCycleCollectionUnlink(RangeUpdater&
class MOZ_STACK_CLASS AutoTrackDOMPoint final
{
private:
RangeUpdater& mRangeUpdater;
// Allow tracking either nsIDOMNode or nsINode until nsIDOMNode is gone
nsCOMPtr<nsINode>* mNode;
nsCOMPtr<nsIDOMNode>* mDOMNode;
int32_t* mOffset;
+ EditorDOMPoint* mPoint;
RefPtr<RangeItem> mRangeItem;
public:
AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
nsCOMPtr<nsINode>* aNode, int32_t* aOffset)
: mRangeUpdater(aRangeUpdater)
, mNode(aNode)
, mDOMNode(nullptr)
, mOffset(aOffset)
+ , mPoint(nullptr)
{
mRangeItem = new RangeItem();
mRangeItem->mStartContainer = *mNode;
mRangeItem->mEndContainer = *mNode;
mRangeItem->mStartOffset = *mOffset;
mRangeItem->mEndOffset = *mOffset;
mRangeUpdater.RegisterRangeItem(mRangeItem);
}
AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset)
: mRangeUpdater(aRangeUpdater)
, mNode(nullptr)
, mDOMNode(aNode)
, mOffset(aOffset)
+ , mPoint(nullptr)
{
mRangeItem = new RangeItem();
mRangeItem->mStartContainer = do_QueryInterface(*mDOMNode);
mRangeItem->mEndContainer = do_QueryInterface(*mDOMNode);
mRangeItem->mStartOffset = *mOffset;
mRangeItem->mEndOffset = *mOffset;
mRangeUpdater.RegisterRangeItem(mRangeItem);
}
+ AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
+ EditorDOMPoint* aPoint)
+ : mRangeUpdater(aRangeUpdater)
+ , mNode(nullptr)
+ , mDOMNode(nullptr)
+ , mOffset(nullptr)
+ , mPoint(aPoint)
+ {
+ mRangeItem = new RangeItem();
+ mRangeItem->mStartContainer = mPoint->Container();
+ mRangeItem->mEndContainer = mPoint->Container();
+ mRangeItem->mStartOffset = mPoint->Offset();
+ mRangeItem->mEndOffset = mPoint->Offset();
+ mRangeUpdater.RegisterRangeItem(mRangeItem);
+ }
+
~AutoTrackDOMPoint()
{
mRangeUpdater.DropRangeItem(mRangeItem);
+ if (mPoint) {
+ mPoint->Set(mRangeItem->mStartContainer, mRangeItem->mStartOffset);
+ return;
+ }
if (mNode) {
*mNode = mRangeItem->mStartContainer;
} else {
*mDOMNode = GetAsDOMNode(mRangeItem->mStartContainer);
}
*mOffset = mRangeItem->mStartOffset;
}
};