--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2467,120 +2467,119 @@ EditorBase::FindBetterInsertionPoint(con
aPoint.Container()->AsContent(), 0);
}
}
return aPoint;
}
nsresult
-EditorBase::InsertTextImpl(const nsAString& aStringToInsert,
- nsCOMPtr<nsINode>* aInOutNode,
- nsCOMPtr<nsIContent>* aInOutChildAtOffset,
- int32_t* aInOutOffset,
- nsIDocument* aDoc)
+EditorBase::InsertTextImpl(nsIDocument& aDocument,
+ const nsAString& aStringToInsert,
+ const EditorRawDOMPoint& aPointToInsert,
+ EditorRawDOMPoint* aPointAfterInsertedString)
{
// NOTE: caller *must* have already used AutoTransactionsConserveSelection
// stack-based class to turn off txn selection updating. Caller also turned
// on rules sniffing if desired.
- NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc,
- NS_ERROR_NULL_POINTER);
+ if (NS_WARN_IF(!aPointToInsert.IsSet())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ MOZ_ASSERT(aPointToInsert.IsSetAndValid());
if (!ShouldHandleIMEComposition() && aStringToInsert.IsEmpty()) {
+ if (aPointAfterInsertedString) {
+ *aPointAfterInsertedString = aPointToInsert;
+ }
return NS_OK;
}
// This method doesn't support over INT32_MAX length text since aInOutOffset
// is int32_t*.
CheckedInt<int32_t> lengthToInsert(aStringToInsert.Length());
- NS_ENSURE_TRUE(lengthToInsert.isValid(), NS_ERROR_INVALID_ARG);
-
- nsCOMPtr<nsINode> node = *aInOutNode;
- int32_t offset = *aInOutOffset;
- nsCOMPtr<nsIContent> child = *aInOutChildAtOffset;
-
- MOZ_ASSERT(!node->IsContainerNode() ||
- node->Length() == static_cast<uint32_t>(offset) ||
- node->GetChildAt(offset) == *aInOutChildAtOffset,
- "|child| must be a child node at |offset| in |node| unless it's a text "
- "or some other data node, or after the last child");
+ if (NS_WARN_IF(!lengthToInsert.isValid())) {
+ return NS_ERROR_INVALID_ARG;
+ }
// In some cases, the node may be the anonymous div elemnt or a mozBR
// element. Let's try to look for better insertion point in the nearest
// text node if there is.
- EditorRawDOMPoint insertionPoint;
- if (child) {
- insertionPoint.Set(child);
- } else {
- insertionPoint.Set(node, offset);
- }
- EditorRawDOMPoint betterPoint = FindBetterInsertionPoint(insertionPoint);
- node = betterPoint.Container();
- offset = betterPoint.Offset();
- child = betterPoint.GetChildAtOffset();
+ EditorRawDOMPoint pointToInsert = FindBetterInsertionPoint(aPointToInsert);
// If a neighboring text node already exists, use that
- if (!node->IsNodeOfType(nsINode::eTEXT)) {
- if (offset && child && child->GetPreviousSibling() &&
- child->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) {
- node = child->GetPreviousSibling();
- offset = node->Length();
- } else if (offset < static_cast<int32_t>(node->Length()) &&
- child && child->IsNodeOfType(nsINode::eTEXT)) {
- node = child;
- offset = 0;
+ if (!pointToInsert.Container()->IsNodeOfType(nsINode::eTEXT)) {
+ nsIContent* child = nullptr;
+ if (!pointToInsert.IsStartOfContainer() &&
+ (child = pointToInsert.GetPreviousSiblingOfChildAtOffset()) &&
+ child->IsNodeOfType(nsINode::eTEXT)) {
+ pointToInsert.Set(child, child->Length());
+ } else if (!pointToInsert.IsEndOfContainer() &&
+ (child = pointToInsert.GetChildAtOffset()) &&
+ child->IsNodeOfType(nsINode::eTEXT)) {
+ pointToInsert.Set(child, 0);
}
}
if (ShouldHandleIMEComposition()) {
CheckedInt<int32_t> newOffset;
- if (!node->IsNodeOfType(nsINode::eTEXT)) {
+ if (!pointToInsert.Container()->IsNodeOfType(nsINode::eTEXT)) {
// create a text node
RefPtr<nsTextNode> newNode =
- EditorBase::CreateTextNode(*aDoc, EmptyString());
+ EditorBase::CreateTextNode(aDocument, EmptyString());
// then we insert it into the dom tree
- nsresult rv = InsertNode(*newNode, *node, offset);
+ nsresult rv = InsertNode(*newNode, *pointToInsert.Container(),
+ pointToInsert.Offset());
NS_ENSURE_SUCCESS(rv, rv);
- node = newNode;
- offset = 0;
+ pointToInsert.Set(newNode, 0);
newOffset = lengthToInsert;
} else {
- newOffset = lengthToInsert + offset;
+ newOffset = lengthToInsert + pointToInsert.Offset();
NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
}
nsresult rv =
- InsertTextIntoTextNodeImpl(aStringToInsert, *node->GetAsText(), offset);
+ InsertTextIntoTextNodeImpl(aStringToInsert,
+ *pointToInsert.Container()->GetAsText(),
+ pointToInsert.Offset());
NS_ENSURE_SUCCESS(rv, rv);
- offset = newOffset.value();
- } else {
- if (node->IsNodeOfType(nsINode::eTEXT)) {
- CheckedInt<int32_t> newOffset = lengthToInsert + offset;
- NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
- // we are inserting text into an existing text node.
- nsresult rv =
- InsertTextIntoTextNodeImpl(aStringToInsert, *node->GetAsText(), offset);
- NS_ENSURE_SUCCESS(rv, rv);
- offset = newOffset.value();
- } else {
- // we are inserting text into a non-text node. first we have to create a
- // textnode (this also populates it with the text)
- RefPtr<nsTextNode> newNode =
- EditorBase::CreateTextNode(*aDoc, aStringToInsert);
- // then we insert it into the dom tree
- nsresult rv = InsertNode(*newNode, *node, offset);
- NS_ENSURE_SUCCESS(rv, rv);
- node = newNode;
- offset = lengthToInsert.value();
+ if (aPointAfterInsertedString) {
+ aPointAfterInsertedString->Set(pointToInsert.Container(),
+ newOffset.value());
}
- }
-
- *aInOutNode = node;
- *aInOutOffset = offset;
- *aInOutChildAtOffset = nullptr;
+ return NS_OK;
+ }
+
+ if (pointToInsert.Container()->IsNodeOfType(nsINode::eTEXT)) {
+ CheckedInt<int32_t> newOffset = lengthToInsert + pointToInsert.Offset();
+ NS_ENSURE_TRUE(newOffset.isValid(), NS_ERROR_FAILURE);
+ // we are inserting text into an existing text node.
+ nsresult rv =
+ InsertTextIntoTextNodeImpl(aStringToInsert,
+ *pointToInsert.Container()->GetAsText(),
+ pointToInsert.Offset());
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (aPointAfterInsertedString) {
+ aPointAfterInsertedString->Set(pointToInsert.Container(),
+ newOffset.value());
+ }
+ return NS_OK;
+ }
+
+ // we are inserting text into a non-text node. first we have to create a
+ // textnode (this also populates it with the text)
+ RefPtr<nsTextNode> newNode =
+ EditorBase::CreateTextNode(aDocument, aStringToInsert);
+ // then we insert it into the dom tree
+ nsresult rv = InsertNode(*newNode, *pointToInsert.Container(),
+ pointToInsert.Offset());
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (aPointAfterInsertedString) {
+ aPointAfterInsertedString->Set(newNode, lengthToInsert.value());
+ }
return NS_OK;
}
nsresult
EditorBase::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
Text& aTextNode,
int32_t aOffset,
bool aSuppressIME)
@@ -3872,26 +3871,43 @@ EditorBase::GetEndNodeAndOffset(Selectio
{
MOZ_ASSERT(aSelection);
MOZ_ASSERT(aEndContainer);
MOZ_ASSERT(aEndOffset);
*aEndContainer = nullptr;
*aEndOffset = 0;
- NS_ENSURE_TRUE(aSelection->RangeCount(), NS_ERROR_FAILURE);
+ EditorRawDOMPoint point = EditorBase::GetEndPoint(aSelection);
+ if (!point.IsSet()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ADDREF(*aEndContainer = point.Container());
+ *aEndOffset = point.Offset();
+ return NS_OK;
+}
+
+// static
+EditorRawDOMPoint
+EditorBase::GetEndPoint(Selection* aSelection)
+{
+ MOZ_ASSERT(aSelection);
+
+ if (NS_WARN_IF(!aSelection->RangeCount())) {
+ return EditorRawDOMPoint();
+ }
const nsRange* range = aSelection->GetRangeAt(0);
- NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
-
- NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE);
-
- NS_IF_ADDREF(*aEndContainer = range->GetEndContainer());
- *aEndOffset = range->EndOffset();
- return NS_OK;
+ if (NS_WARN_IF(!range) ||
+ NS_WARN_IF(!range->IsPositioned())) {
+ return EditorRawDOMPoint();
+ }
+
+ return EditorRawDOMPoint(range->EndRef());
}
nsresult
EditorBase::GetEndChildNode(Selection* aSelection,
nsIContent** aEndNode)
{
MOZ_ASSERT(aSelection);
MOZ_ASSERT(aEndNode);
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -281,21 +281,42 @@ public:
void NotifyEditorObservers(NotificationForEditorObservers aNotification);
// nsIEditor methods
NS_DECL_NSIEDITOR
public:
virtual bool IsModifiableNode(nsINode* aNode);
- virtual nsresult InsertTextImpl(const nsAString& aStringToInsert,
- nsCOMPtr<nsINode>* aInOutNode,
- nsCOMPtr<nsIContent>* aInOutChildAtOffset,
- int32_t* aInOutOffset,
- nsIDocument* aDoc);
+ /**
+ * InsertTextImpl() inserts aStringToInsert to aPointToInsert or better
+ * insertion point around it. If aPointToInsert isn't in a text node,
+ * this method looks for the nearest point in a text node with
+ * FindBetterInsertionPoint(). If there is no text node, this creates
+ * new text node and put aStringToInsert to it.
+ *
+ * @param aDocument The document of this editor.
+ * @param aStringToInsert The string to insert.
+ * @param aPointToInser The point to insert aStringToInsert.
+ * Must be valid DOM point.
+ * @param aPointAfterInsertedString
+ * The point after inserted aStringToInsert.
+ * So, when this method actually inserts string,
+ * this is set to a point in the text node.
+ * Otherwise, this may be set to aPointToInsert.
+ * @return When this succeeds to insert the string or
+ * does nothing during composition, returns NS_OK.
+ * Otherwise, an error code.
+ */
+ virtual nsresult
+ InsertTextImpl(nsIDocument& aDocument,
+ const nsAString& aStringToInsert,
+ const EditorRawDOMPoint& aPointToInsert,
+ EditorRawDOMPoint* aPointAfterInsertedString = nullptr);
+
nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
Text& aTextNode, int32_t aOffset,
bool aSuppressIME = false);
nsresult SetTextImpl(Selection& aSelection,
const nsAString& aString,
Text& aTextNode);
@@ -929,16 +950,17 @@ public:
int32_t* aStartOffset);
static EditorRawDOMPoint GetStartPoint(Selection* aSelection);
static nsresult GetEndNodeAndOffset(Selection* aSelection,
nsIDOMNode** outEndNode,
int32_t* outEndOffset);
static nsresult GetEndNodeAndOffset(Selection* aSelection,
nsINode** aEndContainer,
int32_t* aEndOffset);
+ static EditorRawDOMPoint GetEndPoint(Selection* aSelection);
static nsresult GetEndChildNode(Selection* aSelection,
nsIContent** aEndNode);
#if DEBUG_JOE
static void DumpNode(nsIDOMNode* aNode, int32_t indent = 0);
#endif
Selection* GetSelection(SelectionType aSelectionType =
--- a/editor/libeditor/EditorDOMPoint.h
+++ b/editor/libeditor/EditorDOMPoint.h
@@ -79,16 +79,29 @@ public:
{
}
explicit EditorDOMPointBase(const RawRangeBoundary& aRawRangeBoundary)
: RangeBoundaryBase<ParentType, RefType>(aRawRangeBoundary)
{
}
+ EditorDOMPointBase<nsINode*, nsIContent*>
+ AsRaw() const
+ {
+ return EditorDOMPointBase<nsINode*, nsIContent*>(*this);
+ }
+
+ template<typename A, typename B>
+ EditorDOMPointBase& operator=(const EditorDOMPointBase<A, B>& aOther)
+ {
+ RangeBoundaryBase<ParentType, RefType>::operator=(aOther);
+ return *this;
+ }
+
private:
static nsIContent* GetRef(nsINode* aContainerNode, nsIContent* aPointedNode)
{
// If referring one of a child of the container, the 'ref' should be the
// previous sibling of the referring child.
if (aPointedNode) {
return aPointedNode->GetPreviousSibling();
}
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1321,203 +1321,248 @@ HTMLEditRules::WillInsertText(EditAction
NS_ENSURE_STATE(doc);
// for every property that is set, insert a new inline style node
nsresult rv = CreateStyleForInsertText(*aSelection, *doc);
NS_ENSURE_SUCCESS(rv, rv);
// get the (collapsed) selection location
NS_ENSURE_STATE(mHTMLEditor);
- NS_ENSURE_STATE(aSelection->GetRangeAt(0));
- nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartContainer();
- nsCOMPtr<nsIContent> selChild =
- aSelection->GetRangeAt(0)->GetChildAtStartOffset();
- int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
- NS_ENSURE_STATE(selNode);
+ nsRange* firstRange = aSelection->GetRangeAt(0);
+ if (NS_WARN_IF(!firstRange)) {
+ return NS_ERROR_FAILURE;
+ }
+ EditorDOMPoint pointToInsert(firstRange->StartRef());
+ if (NS_WARN_IF(!pointToInsert.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+ MOZ_ASSERT(pointToInsert.IsSetAndValid());
// dont put text in places that can't have it
- NS_ENSURE_STATE(mHTMLEditor);
- if (!EditorBase::IsTextNode(selNode) &&
- (!mHTMLEditor || !mHTMLEditor->CanContainTag(*selNode,
- *nsGkAtoms::textTagName))) {
+ if (NS_WARN_IF(!mHTMLEditor) ||
+ (!EditorBase::IsTextNode(pointToInsert.Container()) &&
+ !mHTMLEditor->CanContainTag(*pointToInsert.Container(),
+ *nsGkAtoms::textTagName))) {
return NS_ERROR_FAILURE;
}
if (aAction == EditAction::insertIMEText) {
// Right now the WSRunObject code bails on empty strings, but IME needs
// the InsertTextImpl() call to still happen since empty strings are meaningful there.
NS_ENSURE_STATE(mHTMLEditor);
// If there is one or more IME selections, its minimum offset should be
// the insertion point.
int32_t IMESelectionOffset =
- mHTMLEditor->GetIMESelectionStartOffsetIn(selNode);
+ mHTMLEditor->GetIMESelectionStartOffsetIn(pointToInsert.Container());
if (IMESelectionOffset >= 0) {
- selOffset = IMESelectionOffset;
- }
+ pointToInsert.Set(pointToInsert.Container(), IMESelectionOffset);
+ }
+
if (inString->IsEmpty()) {
- rv = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode),
- address_of(selChild),
- &selOffset, doc);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- } else {
- WSRunObject wsObj(mHTMLEditor, selNode, selOffset);
- rv = wsObj.InsertText(*inString, address_of(selNode),
- address_of(selChild), &selOffset, doc);
+ rv = mHTMLEditor->InsertTextImpl(*doc, *inString, pointToInsert.AsRaw());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- }
- }
+ return NS_OK;
+ }
+
+ WSRunObject wsObj(mHTMLEditor,
+ pointToInsert.Container(), pointToInsert.Offset());
+ nsCOMPtr<nsINode> selNode = pointToInsert.Container();
+ nsCOMPtr<nsIContent> selChild = pointToInsert.GetChildAtOffset();
+ int32_t selOffset = pointToInsert.Offset();
+ rv = wsObj.InsertText(*inString, address_of(selNode),
+ address_of(selChild), &selOffset, doc);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
// aAction == kInsertText
- else {
- // find where we are
- nsCOMPtr<nsINode> curNode = selNode;
- int32_t curOffset = selOffset;
-
- // is our text going to be PREformatted?
- // We remember this so that we know how to handle tabs.
- bool isPRE;
- NS_ENSURE_STATE(mHTMLEditor);
- rv = mHTMLEditor->IsPreformatted(GetAsDOMNode(selNode), &isPRE);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // turn off the edit listener: we know how to
- // build the "doc changed range" ourselves, and it's
- // must faster to do it once here than to track all
- // the changes one at a time.
- AutoLockListener lockit(&mListenerEnabled);
-
- // don't change my selection in subtransactions
+
+ // find where we are
+ EditorDOMPoint currentPoint(pointToInsert);
+
+ // is our text going to be PREformatted?
+ // We remember this so that we know how to handle tabs.
+ bool isPRE;
+ NS_ENSURE_STATE(mHTMLEditor);
+ rv = mHTMLEditor->IsPreformatted(GetAsDOMNode(pointToInsert.Container()),
+ &isPRE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // turn off the edit listener: we know how to
+ // build the "doc changed range" ourselves, and it's
+ // must faster to do it once here than to track all
+ // the changes one at a time.
+ AutoLockListener lockit(&mListenerEnabled);
+
+ // don't change my selection in subtransactions
+ NS_ENSURE_STATE(mHTMLEditor);
+ AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
+ nsAutoString tString(*inString);
+ const char16_t *unicodeBuf = tString.get();
+ int32_t pos = 0;
+ NS_NAMED_LITERAL_STRING(newlineStr, LFSTR);
+
+ {
NS_ENSURE_STATE(mHTMLEditor);
- AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
- nsAutoString tString(*inString);
- const char16_t *unicodeBuf = tString.get();
- int32_t pos = 0;
- NS_NAMED_LITERAL_STRING(newlineStr, LFSTR);
-
- {
- NS_ENSURE_STATE(mHTMLEditor);
- AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
- address_of(selNode), &selOffset);
-
- // for efficiency, break out the pre case separately. This is because
- // its a lot cheaper to search the input string for only newlines than
- // it is to search for both tabs and newlines.
- if (isPRE || IsPlaintextEditor()) {
- while (unicodeBuf && pos != -1 &&
- pos < static_cast<int32_t>(inString->Length())) {
- int32_t oldPos = pos;
- int32_t subStrLen;
- pos = tString.FindChar(nsCRT::LF, oldPos);
-
- if (pos != -1) {
- subStrLen = pos - oldPos;
- // if first char is newline, then use just it
- if (!subStrLen) {
- subStrLen = 1;
- }
- } else {
- subStrLen = tString.Length() - oldPos;
- pos = tString.Length();
+ AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, &pointToInsert);
+
+ // for efficiency, break out the pre case separately. This is because
+ // its a lot cheaper to search the input string for only newlines than
+ // it is to search for both tabs and newlines.
+ if (isPRE || IsPlaintextEditor()) {
+ while (unicodeBuf && pos != -1 &&
+ pos < static_cast<int32_t>(inString->Length())) {
+ int32_t oldPos = pos;
+ int32_t subStrLen;
+ pos = tString.FindChar(nsCRT::LF, oldPos);
+
+ if (pos != -1) {
+ subStrLen = pos - oldPos;
+ // if first char is newline, then use just it
+ if (!subStrLen) {
+ subStrLen = 1;
}
-
- nsDependentSubstring subStr(tString, oldPos, subStrLen);
-
- // is it a return?
- if (subStr.Equals(newlineStr)) {
- NS_ENSURE_STATE(mHTMLEditor);
- nsCOMPtr<Element> br =
- mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset,
- nsIEditor::eNone);
- NS_ENSURE_STATE(br);
- pos++;
- selChild = br->GetNextSibling();
- MOZ_ASSERT(curNode->GetChildAt(curOffset) == selChild);
+ } else {
+ subStrLen = tString.Length() - oldPos;
+ pos = tString.Length();
+ }
+
+ nsDependentSubstring subStr(tString, oldPos, subStrLen);
+
+ // is it a return?
+ if (subStr.Equals(newlineStr)) {
+ NS_ENSURE_STATE(mHTMLEditor);
+ nsCOMPtr<nsINode> curNode = currentPoint.Container();
+ int32_t curOffset = currentPoint.Offset();
+ nsCOMPtr<Element> br =
+ mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset,
+ nsIEditor::eNone);
+ NS_ENSURE_STATE(br);
+ pos++;
+ if (br->GetNextSibling()) {
+ pointToInsert.Set(br->GetNextSibling());
} else {
- NS_ENSURE_STATE(mHTMLEditor);
- rv = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode),
- address_of(selChild),
- &curOffset, doc);
- NS_ENSURE_SUCCESS(rv, rv);
+ pointToInsert.Set(curNode, curNode->Length());
}
- }
- } else {
- NS_NAMED_LITERAL_STRING(tabStr, "\t");
- NS_NAMED_LITERAL_STRING(spacesStr, " ");
- char specialChars[] = {TAB, nsCRT::LF, 0};
- while (unicodeBuf && pos != -1 &&
- pos < static_cast<int32_t>(inString->Length())) {
- int32_t oldPos = pos;
- int32_t subStrLen;
- pos = tString.FindCharInSet(specialChars, oldPos);
-
- if (pos != -1) {
- subStrLen = pos - oldPos;
- // if first char is newline, then use just it
- if (!subStrLen) {
- subStrLen = 1;
- }
- } else {
- subStrLen = tString.Length() - oldPos;
- pos = tString.Length();
- }
-
- nsDependentSubstring subStr(tString, oldPos, subStrLen);
+ currentPoint.Set(curNode, curOffset);
+ MOZ_ASSERT(currentPoint == pointToInsert);
+ } else {
NS_ENSURE_STATE(mHTMLEditor);
- WSRunObject wsObj(mHTMLEditor, curNode, curOffset);
-
- // is it a tab?
- if (subStr.Equals(tabStr)) {
- rv =
- wsObj.InsertText(spacesStr, address_of(curNode),
- address_of(selChild), &curOffset, doc);
- NS_ENSURE_SUCCESS(rv, rv);
- pos++;
- }
- // is it a return?
- else if (subStr.Equals(newlineStr)) {
- nsCOMPtr<Element> br = wsObj.InsertBreak(address_of(curNode),
- &curOffset,
- nsIEditor::eNone);
- NS_ENSURE_TRUE(br, NS_ERROR_FAILURE);
- pos++;
- selChild = br->GetNextSibling();
- MOZ_ASSERT(curNode->GetChildAt(curOffset) == selChild);
- } else {
- rv = wsObj.InsertText(subStr, address_of(curNode),
- address_of(selChild), &curOffset, doc);
- NS_ENSURE_SUCCESS(rv, rv);
- }
+ EditorRawDOMPoint pointAfterInsertedString;
+ rv = mHTMLEditor->InsertTextImpl(*doc, subStr, currentPoint.AsRaw(),
+ &pointAfterInsertedString);
+ NS_ENSURE_SUCCESS(rv, rv);
+ currentPoint = pointAfterInsertedString;
+ pointToInsert = pointAfterInsertedString;
}
}
- }
- aSelection->SetInterlinePosition(false);
- if (curNode) {
- aSelection->Collapse(curNode, curOffset);
- }
- // manually update the doc changed range so that AfterEdit will clean up
- // the correct portion of the document.
- if (!mDocChangeRange) {
- mDocChangeRange = new nsRange(selNode);
- }
-
- if (curNode) {
- rv = mDocChangeRange->SetStartAndEnd(selNode, selOffset,
- curNode, curOffset);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
} else {
- rv = mDocChangeRange->CollapseTo(selNode, selOffset);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
+ NS_NAMED_LITERAL_STRING(tabStr, "\t");
+ NS_NAMED_LITERAL_STRING(spacesStr, " ");
+ char specialChars[] = {TAB, nsCRT::LF, 0};
+ while (unicodeBuf && pos != -1 &&
+ pos < static_cast<int32_t>(inString->Length())) {
+ int32_t oldPos = pos;
+ int32_t subStrLen;
+ pos = tString.FindCharInSet(specialChars, oldPos);
+
+ if (pos != -1) {
+ subStrLen = pos - oldPos;
+ // if first char is newline, then use just it
+ if (!subStrLen) {
+ subStrLen = 1;
+ }
+ } else {
+ subStrLen = tString.Length() - oldPos;
+ pos = tString.Length();
+ }
+
+ nsDependentSubstring subStr(tString, oldPos, subStrLen);
+ NS_ENSURE_STATE(mHTMLEditor);
+ WSRunObject wsObj(mHTMLEditor, currentPoint.Container(),
+ currentPoint.Offset());
+
+ // is it a tab?
+ if (subStr.Equals(tabStr)) {
+ nsCOMPtr<nsINode> curNode = currentPoint.Container();
+ nsCOMPtr<nsIContent> selChild = currentPoint.GetChildAtOffset();
+ int32_t curOffset = currentPoint.Offset();
+ rv =
+ wsObj.InsertText(spacesStr, address_of(curNode),
+ address_of(selChild), &curOffset, doc);
+ NS_ENSURE_SUCCESS(rv, rv);
+ pos++;
+ currentPoint.Set(curNode, curOffset);
+ pointToInsert = currentPoint;
+ }
+ // is it a return?
+ else if (subStr.Equals(newlineStr)) {
+ nsCOMPtr<nsINode> curNode = currentPoint.Container();
+ int32_t curOffset = currentPoint.Offset();
+ nsCOMPtr<Element> br = wsObj.InsertBreak(address_of(curNode),
+ &curOffset,
+ nsIEditor::eNone);
+ NS_ENSURE_TRUE(br, NS_ERROR_FAILURE);
+ pos++;
+ if (br->GetNextSibling()) {
+ pointToInsert.Set(br->GetNextSibling());
+ } else {
+ pointToInsert.Set(curNode, curNode->Length());
+ }
+ currentPoint.Set(curNode, curOffset);
+ MOZ_ASSERT(currentPoint == pointToInsert);
+ } else {
+ nsCOMPtr<nsINode> curNode = currentPoint.Container();
+ nsCOMPtr<nsIContent> selChild = currentPoint.GetChildAtOffset();
+ int32_t curOffset = currentPoint.Offset();
+ rv = wsObj.InsertText(subStr, address_of(curNode),
+ address_of(selChild), &curOffset, doc);
+ NS_ENSURE_SUCCESS(rv, rv);
+ currentPoint.Set(curNode, curOffset);
+ pointToInsert = currentPoint;
+ }
+ }
+ }
+
+ // After this block, pointToInsert is updated by AutoTrackDOMPoint.
+ }
+
+ aSelection->SetInterlinePosition(false);
+
+ if (currentPoint.IsSet()) {
+ ErrorResult error;
+ aSelection->Collapse(currentPoint.AsRaw(), error);
+ if (error.Failed()) {
+ NS_WARNING("Failed to collapse at current point");
+ error.SuppressException();
+ }
+ }
+
+ // manually update the doc changed range so that AfterEdit will clean up
+ // the correct portion of the document.
+ if (!mDocChangeRange) {
+ mDocChangeRange = new nsRange(pointToInsert.Container());
+ }
+
+ if (currentPoint.IsSet()) {
+ rv = mDocChangeRange->SetStartAndEnd(pointToInsert.AsRaw(),
+ currentPoint.AsRaw());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ rv = mDocChangeRange->CollapseTo(pointToInsert.AsRaw());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
}
return NS_OK;
}
nsresult
HTMLEditRules::WillLoadHTML(Selection* aSelection,
bool* aCancel)
{
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3181,30 +3181,32 @@ HTMLEditor::DeleteText(nsGenericDOMDataN
if (!IsModifiableNode(&aCharData)) {
return NS_ERROR_FAILURE;
}
return EditorBase::DeleteText(aCharData, aOffset, aLength);
}
nsresult
-HTMLEditor::InsertTextImpl(const nsAString& aStringToInsert,
- nsCOMPtr<nsINode>* aInOutNode,
- nsCOMPtr<nsIContent>* aInOutChildAtOffset,
- int32_t* aInOutOffset,
- nsIDocument* aDoc)
+HTMLEditor::InsertTextImpl(nsIDocument& aDocument,
+ const nsAString& aStringToInsert,
+ const EditorRawDOMPoint& aPointToInsert,
+ EditorRawDOMPoint* aPointAfterInsertedString)
{
+ if (NS_WARN_IF(!aPointToInsert.IsSet())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
// Do nothing if the node is read-only
- if (!IsModifiableNode(*aInOutNode)) {
+ if (!IsModifiableNode(aPointToInsert.Container())) {
return NS_ERROR_FAILURE;
}
- return EditorBase::InsertTextImpl(aStringToInsert, aInOutNode,
- aInOutChildAtOffset,
- aInOutOffset, aDoc);
+ return EditorBase::InsertTextImpl(aDocument, aStringToInsert, aPointToInsert,
+ aPointAfterInsertedString);
}
void
HTMLEditor::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aFirstNewContent)
{
DoContentInserted(aDocument, aContainer, aFirstNewContent, eAppended);
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -316,21 +316,22 @@ public:
nsIContent* aNode2) override;
NS_IMETHOD DeleteSelectionImpl(EDirection aAction,
EStripWrappers aStripWrappers) override;
nsresult DeleteNode(nsINode* aNode);
NS_IMETHOD DeleteNode(nsIDOMNode* aNode) override;
nsresult DeleteText(nsGenericDOMDataNode& aTextNode, uint32_t aOffset,
uint32_t aLength);
- virtual nsresult InsertTextImpl(const nsAString& aStringToInsert,
- nsCOMPtr<nsINode>* aInOutNode,
- nsCOMPtr<nsIContent>* aInOutChildAtOffset,
- int32_t* aInOutOffset,
- nsIDocument* aDoc) override;
+ virtual nsresult
+ InsertTextImpl(nsIDocument& aDocument,
+ const nsAString& aStringToInsert,
+ const EditorRawDOMPoint& aPointToInsert,
+ EditorRawDOMPoint* aPointAfterInsertedString =
+ nullptr) override;
NS_IMETHOD_(bool) IsModifiableNode(nsIDOMNode* aNode) override;
virtual bool IsModifiableNode(nsINode* aNode) override;
NS_IMETHOD SelectAll() override;
// nsICSSLoaderObserver
NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet,
bool aWasAlternate, nsresult aStatus) override;
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -756,46 +756,45 @@ TextEditRules::WillInsertText(EditAction
// the insertion point.
int32_t IMESelectionOffset =
mTextEditor->GetIMESelectionStartOffsetIn(
betterInsertionPoint.Container());
if (IMESelectionOffset >= 0) {
betterInsertionPoint.Set(betterInsertionPoint.Container(),
IMESelectionOffset);
}
- nsCOMPtr<nsINode> selNode = betterInsertionPoint.Container();
- int32_t selOffset = betterInsertionPoint.Offset();
- nsCOMPtr<nsIContent> selChild = betterInsertionPoint.GetChildAtOffset();
- rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode),
- address_of(selChild), &selOffset, doc);
+ rv = mTextEditor->InsertTextImpl(*doc, *outString, betterInsertionPoint);
NS_ENSURE_SUCCESS(rv, rv);
} else {
- // aAction == EditAction::insertText; find where we are
- nsCOMPtr<nsINode> curNode = atStartOfSelection.Container();
- int32_t curOffset = atStartOfSelection.Offset();
- nsCOMPtr<nsIContent> selChild = atStartOfSelection.GetChildAtOffset();
+ // aAction == EditAction::insertText
// don't change my selection in subtransactions
NS_ENSURE_STATE(mTextEditor);
AutoTransactionsConserveSelection dontChangeMySelection(mTextEditor);
- rv = mTextEditor->InsertTextImpl(*outString, address_of(curNode),
- address_of(selChild), &curOffset, doc);
+ EditorRawDOMPoint pointAfterStringInserted;
+ rv = mTextEditor->InsertTextImpl(*doc, *outString, atStartOfSelection,
+ &pointAfterStringInserted);
NS_ENSURE_SUCCESS(rv, rv);
- if (curNode) {
+ if (pointAfterStringInserted.IsSet()) {
// Make the caret attach to the inserted text, unless this text ends with a LF,
// in which case make the caret attach to the next line.
bool endsWithLF =
!outString->IsEmpty() && outString->Last() == nsCRT::LF;
aSelection->SetInterlinePosition(endsWithLF);
- MOZ_ASSERT(!selChild,
- "After inserting text into a text node, selChild should be nullptr");
- aSelection->Collapse(curNode, curOffset);
+ MOZ_ASSERT(!pointAfterStringInserted.GetChildAtOffset(),
+ "After inserting text into a text node, pointAfterStringInserted."
+ "GetChildAtOffset() should be nullptr");
+ ErrorResult error;
+ aSelection->Collapse(pointAfterStringInserted, error);
+ if (error.Failed()) {
+ NS_WARNING("Failed to collapse selection after inserting string");
+ }
}
}
ASSERT_PASSWORD_LENGTHS_EQUAL()
return NS_OK;
}
nsresult
TextEditRules::DidInsertText(Selection* aSelection,
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -729,63 +729,69 @@ TextEditor::InsertLineBreak()
// pre-process
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
TextRulesInfo ruleInfo(EditAction::insertBreak);
ruleInfo.maxLength = mMaxTextLength;
bool cancel, handled;
+ // XXX DidDoAction() won't be called when this returns error. Perhaps,
+ // we should move the code between WillDoAction() and DidDoAction()
+ // to a new method and guarantee that DidDoAction() is always called
+ // after WillDoAction().
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
NS_ENSURE_SUCCESS(rv, rv);
if (!cancel && !handled) {
// get the (collapsed) selection location
- NS_ENSURE_STATE(selection->GetRangeAt(0));
- nsCOMPtr<nsINode> selNode = selection->GetRangeAt(0)->GetStartContainer();
- nsCOMPtr<nsIContent> selChild = selection->GetRangeAt(0)->GetChildAtStartOffset();
- int32_t selOffset = selection->GetRangeAt(0)->StartOffset();
- NS_ENSURE_STATE(selNode);
+ nsRange* firstRange = selection->GetRangeAt(0);
+ if (NS_WARN_IF(!firstRange)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ EditorRawDOMPoint pointToInsert(firstRange->StartRef());
+ if (NS_WARN_IF(!pointToInsert.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+ MOZ_ASSERT(pointToInsert.IsSetAndValid());
// don't put text in places that can't have it
- if (!IsTextNode(selNode) && !CanContainTag(*selNode,
- *nsGkAtoms::textTagName)) {
+ if (!IsTextNode(pointToInsert.Container()) &&
+ !CanContainTag(*pointToInsert.Container(), *nsGkAtoms::textTagName)) {
return NS_ERROR_FAILURE;
}
// we need to get the doc
nsCOMPtr<nsIDocument> doc = GetDocument();
NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
// don't change my selection in subtransactions
AutoTransactionsConserveSelection dontChangeMySelection(this);
// insert a linefeed character
- rv = InsertTextImpl(NS_LITERAL_STRING("\n"), address_of(selNode),
- address_of(selChild), &selOffset, doc);
- if (!selNode) {
+ EditorRawDOMPoint pointAfterInsertedLineBreak;
+ rv = InsertTextImpl(*doc, NS_LITERAL_STRING("\n"), pointToInsert,
+ &pointAfterInsertedLineBreak);
+ if (NS_WARN_IF(!pointAfterInsertedLineBreak.IsSet())) {
rv = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
}
if (NS_SUCCEEDED(rv)) {
// set the selection to the correct location
- MOZ_ASSERT(!selChild,
- "After inserting text into a text node, selChild should be nullptr");
- rv = selection->Collapse(EditorRawDOMPoint(selNode, selOffset));
+ MOZ_ASSERT(!pointAfterInsertedLineBreak.GetChildAtOffset(),
+ "After inserting text into a text node, pointAfterInsertedLineBreak."
+ "GetChildAtOffset() should be nullptr");
+ rv = selection->Collapse(pointAfterInsertedLineBreak);
if (NS_SUCCEEDED(rv)) {
// see if we're at the end of the editor range
- nsCOMPtr<nsIDOMNode> endNode;
- int32_t endOffset;
- rv = GetEndNodeAndOffset(selection,
- getter_AddRefs(endNode), &endOffset);
-
- if (NS_SUCCEEDED(rv) &&
- endNode == GetAsDOMNode(selNode) && endOffset == selOffset) {
- // SetInterlinePosition(true) means we want the caret to stick to the content on the "right".
- // We want the caret to stick to whatever is past the break. This is
- // because the break is on the same line we were on, but the next content
- // will be on the following line.
+ EditorRawDOMPoint endPoint = GetEndPoint(selection);
+ if (endPoint == pointAfterInsertedLineBreak) {
+ // SetInterlinePosition(true) means we want the caret to stick to the
+ // content on the "right". We want the caret to stick to whatever is
+ // past the break. This is because the break is on the same line we
+ // were on, but the next content will be on the following line.
selection->SetInterlinePosition(true);
}
}
}
}
if (!cancel) {
// post-process, always called if WillInsertBreak didn't return cancel==true
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -372,18 +372,27 @@ WSRunObject::InsertText(const nsAString&
prevWS = true;
}
} else {
prevWS = false;
}
}
// Ready, aim, fire!
- mHTMLEditor->InsertTextImpl(theString, aInOutParent, aInOutChildAtOffset,
- aInOutOffset, aDoc);
+ EditorRawDOMPoint pointToInsert(*aInOutParent, *aInOutChildAtOffset,
+ *aInOutOffset);
+ EditorRawDOMPoint pointAfterInsertedString;
+ nsresult rv = mHTMLEditor->InsertTextImpl(*aDoc, theString, pointToInsert,
+ &pointAfterInsertedString);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_OK;
+ }
+ *aInOutParent = pointAfterInsertedString.Container();
+ *aInOutChildAtOffset = pointAfterInsertedString.GetChildAtOffset();
+ *aInOutOffset = pointAfterInsertedString.Offset();
return NS_OK;
}
nsresult
WSRunObject::DeleteWSBackward()
{
WSPoint point = GetCharBefore(mNode, mOffset);
NS_ENSURE_TRUE(point.mTextNode, NS_OK); // nothing to delete