Bug 1408227 - part 1: TextEditor::CreateBRImpl() should take |const EditorRawDOMPoint&| for insertion point of new <br> element r=m_kato

TextEditor::CreateBRImpl() should take |const EditorRawDOMPoint&| as insertion
point of new <br> element.  Additionally, it doesn't need to have out argument
for the point of after <br> element because callers can get it with
EditorRawDOMPoint(nsINode*) and the new <br> node, and calling AdvanceOffset().
This cost must be enough cheap.

MozReview-Commit-ID: Hxawz3D2dCd

--HG--
extra : rebase_source : 866ff50efd70499ed733da9efcce7399f44bd4a0
This commit is contained in:
Masayuki Nakano 2017-11-21 14:38:19 +09:00
parent 5c35d9705d
commit 7c6b57669f
7 changed files with 198 additions and 143 deletions

View File

@ -534,9 +534,15 @@ HTMLEditor::AbsolutelyPositionElement(nsIDOMElement* aElement,
nsINode* parentNode = element->GetParentNode();
if (parentNode->GetChildCount() == 1) {
nsCOMPtr<nsIDOMNode> brNode;
nsresult rv = CreateBR(parentNode->AsDOMNode(), 0, address_of(brNode));
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
RefPtr<Element> newBRElement =
CreateBRImpl(*selection, EditorRawDOMPoint(parentNode, 0), eNone);
if (NS_WARN_IF(!newBRElement)) {
return NS_ERROR_FAILURE;
}
}
}
else {

View File

@ -1438,20 +1438,27 @@ HTMLEditRules::WillInsertText(EditAction aAction,
// 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,
mHTMLEditor->CreateBRImpl(*aSelection, currentPoint.AsRaw(),
nsIEditor::eNone);
NS_ENSURE_STATE(br);
pos++;
if (br->GetNextSibling()) {
pointToInsert.Set(br->GetNextSibling());
} else {
pointToInsert.Set(curNode, curNode->Length());
pointToInsert.Set(currentPoint.Container(),
currentPoint.Container()->Length());
}
currentPoint.Set(curNode, curOffset);
MOZ_ASSERT(currentPoint == pointToInsert);
// XXX In most cases, pointToInsert and currentPoint are same here.
// But if the <br> element has been moved to different point by
// mutation observer, those points become different.
currentPoint.Set(br);
DebugOnly<bool> advanced = currentPoint.AdvanceOffset();
NS_WARNING_ASSERTION(advanced,
"Failed to advance offset after the new <br> element");
NS_WARNING_ASSERTION(currentPoint == pointToInsert,
"Perhaps, <br> element position has been moved to different point "
"by mutation observer");
} else {
NS_ENSURE_STATE(mHTMLEditor);
EditorRawDOMPoint pointAfterInsertedString;

View File

@ -1049,15 +1049,15 @@ HTMLEditor::InsertBR()
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsINode> selNode;
int32_t selOffset;
nsresult rv =
GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
NS_ENSURE_SUCCESS(rv, rv);
EditorRawDOMPoint atStartOfSelection(GetStartPoint(selection));
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
return NS_ERROR_FAILURE;
}
// position selection after br
RefPtr<Element> br = CreateBR(selNode, selOffset, nsIEditor::eNext);
if (NS_WARN_IF(!br)) {
// CreateBRImpl() will set selection after the new <br> element.
RefPtr<Element> newBRElement =
CreateBRImpl(*selection, atStartOfSelection, nsIEditor::eNext);
if (NS_WARN_IF(!newBRElement)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
@ -1508,13 +1508,16 @@ HTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement,
}
// check for inserting a whole table at the end of a block. If so insert
// a br after it.
if (HTMLEditUtils::IsTable(node)) {
if (IsLastEditableChild(element)) {
nsCOMPtr<nsIDOMNode> brNode;
rv = CreateBR(parentSelectedDOMNode, offsetForInsert + 1,
address_of(brNode));
NS_ENSURE_SUCCESS(rv, rv);
selection->Collapse(parentSelectedDOMNode, offsetForInsert+1);
if (HTMLEditUtils::IsTable(node) &&
IsLastEditableChild(element)) {
// Collapse selection to the new <br> element node after creating it.
RefPtr<Element> newBRElement =
CreateBRImpl(*selection,
EditorRawDOMPoint(parentSelectedDOMNode,
offsetForInsert + 1),
ePrevious);
if (NS_WARN_IF(!newBRElement)) {
return NS_ERROR_FAILURE;
}
}
}

View File

@ -470,6 +470,10 @@ protected:
nsresult TabInTable(bool inIsShift, bool* outHandled);
/**
* InsertBR() inserts a new <br> element at selection. If there is
* non-collapsed selection ranges, the selected ranges is deleted first.
*/
nsresult InsertBR();
// Table Editing (implemented in nsTableEditor.cpp)

View File

@ -416,120 +416,113 @@ TextEditor::TypedText(const nsAString& aString, ETypingAction aAction)
}
}
already_AddRefed<Element>
TextEditor::CreateBRImpl(nsCOMPtr<nsINode>* aInOutParent,
int32_t* aInOutOffset,
EDirection aSelect)
{
nsCOMPtr<nsIDOMNode> parent(GetAsDOMNode(*aInOutParent));
nsCOMPtr<nsIDOMNode> br;
// We ignore the retval, and assume it's fine if the br is non-null
CreateBRImpl(address_of(parent), aInOutOffset, address_of(br), aSelect);
*aInOutParent = do_QueryInterface(parent);
nsCOMPtr<Element> ret(do_QueryInterface(br));
return ret.forget();
}
nsresult
TextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
int32_t* aInOutOffset,
nsCOMPtr<nsIDOMNode>* outBRNode,
EDirection aSelect)
{
NS_ENSURE_TRUE(aInOutParent && *aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER);
*outBRNode = nullptr;
// we need to insert a br. unfortunately, we may have to split a text node to do it.
nsCOMPtr<nsINode> node = do_QueryInterface(*aInOutParent);
int32_t theOffset = *aInOutOffset;
RefPtr<Element> brNode;
if (IsTextNode(node)) {
EditorRawDOMPoint pointToInsertBrNode(node);
if (NS_WARN_IF(!pointToInsertBrNode.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
if (!theOffset) {
// we are already set to go
} else if (theOffset == static_cast<int32_t>(node->Length())) {
// update offset to point AFTER the text node
pointToInsertBrNode.AdvanceOffset();
} else {
MOZ_DIAGNOSTIC_ASSERT(theOffset < static_cast<int32_t>(node->Length()));
// split the text node
EditorRawDOMPoint atStartOfNewLine(node, theOffset);
ErrorResult error;
nsCOMPtr<nsIContent> newLeftNode = SplitNode(atStartOfNewLine, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
// The right node offset in the parent is now changed. Recompute it.
pointToInsertBrNode.Set(node);
Unused << newLeftNode;
}
// Lock the offset of pointToInsertBrNode because it'll be referred after
// inserting a new <br> node before it.
Unused << pointToInsertBrNode.Offset();
// create br
brNode = CreateNode(nsGkAtoms::br, pointToInsertBrNode);
if (NS_WARN_IF(!brNode)) {
return NS_ERROR_FAILURE;
}
*aInOutParent = GetAsDOMNode(pointToInsertBrNode.Container());
*aInOutOffset = pointToInsertBrNode.Offset() + 1;
} else {
EditorRawDOMPoint pointToInsertBrNode(node, theOffset);
brNode = CreateNode(nsGkAtoms::br, pointToInsertBrNode);
if (NS_WARN_IF(!brNode)) {
return NS_ERROR_FAILURE;
}
(*aInOutOffset)++;
}
*outBRNode = GetAsDOMNode(brNode);
if (*outBRNode && (aSelect != eNone)) {
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_STATE(selection);
if (aSelect == eNext) {
selection->SetInterlinePosition(true);
// position selection after br
EditorRawDOMPoint afterBrNode(brNode);
if (NS_WARN_IF(!afterBrNode.AdvanceOffset())) {
return NS_OK;
}
selection->Collapse(afterBrNode);
} else if (aSelect == ePrevious) {
selection->SetInterlinePosition(true);
// position selection before br
EditorRawDOMPoint atBrNode(brNode);
if (NS_WARN_IF(!atBrNode.IsSetAndValid())) {
return NS_OK;
}
selection->Collapse(atBrNode);
}
}
return NS_OK;
}
already_AddRefed<Element>
TextEditor::CreateBR(nsINode* aNode,
int32_t aOffset,
EDirection aSelect)
EDirection aSelect /* = eNone */)
{
nsCOMPtr<nsINode> parent = aNode;
int32_t offset = aOffset;
return CreateBRImpl(address_of(parent), &offset, aSelect);
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return nullptr;
}
// We assume everything is fine if newBRElement is not null.
return CreateBRImpl(*selection, EditorRawDOMPoint(aNode, aOffset), aSelect);
}
nsresult
TextEditor::CreateBR(nsIDOMNode* aNode,
int32_t aOffset,
nsCOMPtr<nsIDOMNode>* outBRNode,
EDirection aSelect)
already_AddRefed<Element>
TextEditor::CreateBRImpl(Selection& aSelection,
const EditorRawDOMPoint& aPointToInsert,
EDirection aSelect)
{
nsCOMPtr<nsIDOMNode> parent = aNode;
int32_t offset = aOffset;
return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect);
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
return nullptr;
}
// We need to insert a <br> node.
RefPtr<Element> newBRElement;
if (IsTextNode(aPointToInsert.Container())) {
EditorDOMPoint pointInContainer;
if (aPointToInsert.IsStartOfContainer()) {
// Insert before the text node.
pointInContainer.Set(aPointToInsert.Container());
if (NS_WARN_IF(!pointInContainer.IsSet())) {
return nullptr;
}
} else if (aPointToInsert.IsEndOfContainer()) {
// Insert after the text node.
pointInContainer.Set(aPointToInsert.Container());
if (NS_WARN_IF(!pointInContainer.IsSet())) {
return nullptr;
}
DebugOnly<bool> advanced = pointInContainer.AdvanceOffset();
NS_WARNING_ASSERTION(advanced,
"Failed to advance offset to after the text node");
} else {
MOZ_DIAGNOSTIC_ASSERT(aPointToInsert.IsSetAndValid());
// Unfortunately, we need to split the text node at the offset.
ErrorResult error;
nsCOMPtr<nsIContent> newLeftNode = SplitNode(aPointToInsert, error);
if (NS_WARN_IF(error.Failed())) {
error.SuppressException();
return nullptr;
}
Unused << newLeftNode;
// Insert new <br> before the right node.
pointInContainer.Set(aPointToInsert.Container());
}
// Create a <br> node.
newBRElement = CreateNode(nsGkAtoms::br, pointInContainer.AsRaw());
if (NS_WARN_IF(!newBRElement)) {
return nullptr;
}
} else {
newBRElement = CreateNode(nsGkAtoms::br, aPointToInsert);
if (NS_WARN_IF(!newBRElement)) {
return nullptr;
}
}
switch (aSelect) {
case eNone:
break;
case eNext: {
aSelection.SetInterlinePosition(true);
// Collapse selection after the <br> node.
EditorRawDOMPoint afterBRElement(newBRElement);
if (afterBRElement.IsSet()) {
DebugOnly<bool> advanced = afterBRElement.AdvanceOffset();
NS_WARNING_ASSERTION(advanced,
"Failed to advance offset after the <br> element");
ErrorResult error;
aSelection.Collapse(afterBRElement, error);
NS_WARNING_ASSERTION(!error.Failed(),
"Failed to collapse selection after the <br> element");
} else {
NS_WARNING("The <br> node is not in the DOM tree?");
}
break;
}
case ePrevious: {
aSelection.SetInterlinePosition(true);
// Collapse selection at the <br> node.
EditorRawDOMPoint atBRElement(newBRElement);
if (atBRElement.IsSet()) {
ErrorResult error;
aSelection.Collapse(atBRElement, error);
NS_WARNING_ASSERTION(!error.Failed(),
"Failed to collapse selection at the <br> element");
} else {
NS_WARNING("The <br> node is not in the DOM tree?");
}
break;
}
default:
NS_WARNING("aSelect has invalid value, the caller need to set selection "
"by itself");
break;
}
return newBRElement.forget();
}
nsresult

View File

@ -187,18 +187,44 @@ protected:
uint32_t aFlags,
const nsACString& aCharset);
/**
* CreateBR() creates new <br> element and inserts it before the point,
* aNode - aOffset, and collapse selection if it's necessary.
*
* @param aNode The container node to insert new <br> element.
* @param aOffset The offset in aNode to insert new <br> element.
* @param aSelect If eNone, this won't change selection.
* If eNext, selection will be collapsed after the <br>
* element.
* If ePrevious, selection will be collapsed at the <br>
* element.
* @return The new <br> node. If failed to create new <br> node,
* returns nullptr.
*/
already_AddRefed<Element> CreateBR(nsINode* aNode, int32_t aOffset,
EDirection aSelect = eNone);
nsresult CreateBR(nsIDOMNode* aNode, int32_t aOffset,
nsCOMPtr<nsIDOMNode>* outBRNode,
EDirection aSelect = eNone);
already_AddRefed<Element> CreateBRImpl(nsCOMPtr<nsINode>* aInOutParent,
int32_t* aInOutOffset,
EDirection aSelect);
nsresult CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
int32_t* aInOutOffset,
nsCOMPtr<nsIDOMNode>* outBRNode,
EDirection aSelect);
/**
* CreateBRImpl() creates a <br> element and inserts it before aPointToInsert.
* Then, tries to collapse selection at or after the new <br> node if
* aSelect is not eNone.
* XXX Perhaps, this should be merged with CreateBR().
*
* @param aSelection The selection of this editor.
* @param aPointToInsert The DOM point where should be <br> node inserted
* before.
* @param aSelect If eNone, this won't change selection.
* If eNext, selection will be collapsed after
* the <br> element.
* If ePrevious, selection will be collapsed at
* the <br> element.
* @return The new <br> node. If failed to create new
* <br> node, returns nullptr.
*/
already_AddRefed<Element>
CreateBRImpl(Selection& aSelection,
const EditorRawDOMPoint& aPointToInsert,
EDirection aSelect);
/**
* Factored methods for handling insertion of data from transferables

View File

@ -228,8 +228,24 @@ WSRunObject::InsertBreak(nsCOMPtr<nsINode>* aInOutParent,
}
}
// ready, aim, fire!
return mHTMLEditor->CreateBRImpl(aInOutParent, aInOutOffset, aSelect);
RefPtr<Selection> selection = mHTMLEditor->GetSelection();
if (NS_WARN_IF(!selection)) {
return nullptr;
}
RefPtr<Element> newBRElement =
mHTMLEditor->CreateBRImpl(*selection,
EditorRawDOMPoint(*aInOutParent, *aInOutOffset),
aSelect);
if (NS_WARN_IF(!newBRElement)) {
return nullptr;
}
EditorRawDOMPoint atNewBRElement(newBRElement);
DebugOnly<bool> advanced = atNewBRElement.AdvanceOffset();
NS_WARNING_ASSERTION(advanced,
"Failed to advance offset to after the new <br> element");
*aInOutParent = atNewBRElement.Container();
*aInOutOffset = atNewBRElement.Offset();
return newBRElement.forget();
}
nsresult