--- a/accessible/generic/HyperTextAccessible-inl.h
+++ b/accessible/generic/HyperTextAccessible-inl.h
@@ -102,20 +102,23 @@ HyperTextAccessible::CutText(int32_t aSt
textEditor->Cut();
}
}
inline void
HyperTextAccessible::DeleteText(int32_t aStartPos, int32_t aEndPos)
{
RefPtr<TextEditor> textEditor = GetEditor();
- if (textEditor) {
- SetSelectionRange(aStartPos, aEndPos);
- textEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
+ if (!textEditor) {
+ return;
}
+ SetSelectionRange(aStartPos, aEndPos);
+ DebugOnly<nsresult> rv =
+ textEditor->DeleteSelectionAsAction(nsIEditor::eNone, nsIEditor::eStrip);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to delete text");
}
inline void
HyperTextAccessible::PasteText(int32_t aPosition)
{
RefPtr<TextEditor> textEditor = GetEditor();
if (textEditor) {
SetSelectionRange(aPosition, aPosition);
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -2417,17 +2417,21 @@ nsTextEditorState::SetValue(const nsAStr
} else {
// Collapse selection to the end so that we can append data.
mBoundFrame->SelectAllOrCollapseToEndOfText(false);
}
const nsAString& insertValue =
StringTail(newValue, newlength - currentLength);
if (insertValue.IsEmpty()) {
- textEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
+ DebugOnly<nsresult> rv =
+ textEditor->DeleteSelectionAsAction(nsIEditor::eNone,
+ nsIEditor::eStrip);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "Failed to remove the text");
} else {
textEditor->InsertText(insertValue);
}
} else {
AutoDisableUndo disableUndo(textEditor);
if (selection) {
// Since we don't use undo transaction, we don't need to store
// selection state. SetText will set selection to tail.
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -702,18 +702,17 @@ EditorBase::GetSelectionController(nsISe
selCon.forget(aSel);
return NS_OK;
}
NS_IMETHODIMP
EditorBase::DeleteSelection(EDirection aAction,
EStripWrappers aStripWrappers)
{
- MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
- return DeleteSelectionImpl(aAction, aStripWrappers);
+ return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
EditorBase::GetSelection(nsISelection** aSelection)
{
return GetSelection(SelectionType::eNormal, aSelection);
}
@@ -2488,30 +2487,30 @@ EditorBase::GetRootElement(nsIDOMElement
rootElement.forget(aRootElement);
return NS_OK;
}
/**
* All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction.
*/
-NS_IMETHODIMP
+nsresult
EditorBase::StartOperation(EditAction opID,
nsIEditor::EDirection aDirection)
{
mAction = opID;
mDirection = aDirection;
return NS_OK;
}
/**
* All editor operations which alter the doc should be followed
* with a call to EndOperation.
*/
-NS_IMETHODIMP
+nsresult
EditorBase::EndOperation()
{
mAction = EditAction::none;
mDirection = eNone;
return NS_OK;
}
NS_IMETHODIMP
@@ -4257,136 +4256,16 @@ EditorBase::EndUpdateViewBatch()
}
bool
EditorBase::GetShouldTxnSetSelection()
{
return mShouldTxnSetSelection;
}
-NS_IMETHODIMP
-EditorBase::DeleteSelectionImpl(EDirection aAction,
- EStripWrappers aStripWrappers)
-{
- MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
-
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_STATE(selection);
-
- RefPtr<EditAggregateTransaction> deleteSelectionTransaction;
- nsCOMPtr<nsINode> deleteNode;
- int32_t deleteCharOffset = 0, deleteCharLength = 0;
- if (!selection->Collapsed() || aAction != eNone) {
- deleteSelectionTransaction =
- CreateTxnForDeleteSelection(aAction,
- getter_AddRefs(deleteNode),
- &deleteCharOffset,
- &deleteCharLength);
- if (NS_WARN_IF(!deleteSelectionTransaction)) {
- return NS_ERROR_FAILURE;
- }
- }
-
- RefPtr<CharacterData> deleteCharData =
- CharacterData::FromNodeOrNull(deleteNode);
- AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
-
- if (mRules && mRules->AsHTMLEditRules()) {
- if (!deleteNode) {
- RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
- htmlEditRules->WillDeleteSelection(selection);
- } else if (!deleteCharData) {
- RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
- htmlEditRules->WillDeleteNode(deleteNode);
- }
- }
-
- // Notify nsIEditActionListener::WillDelete[Selection|Text]
- if (!mActionListeners.IsEmpty()) {
- if (!deleteNode) {
- AutoActionListenerArray listeners(mActionListeners);
- for (auto& listener : listeners) {
- listener->WillDeleteSelection(selection);
- }
- } else if (deleteCharData) {
- AutoActionListenerArray listeners(mActionListeners);
- for (auto& listener : listeners) {
- listener->WillDeleteText(deleteCharData, deleteCharOffset, 1);
- }
- }
- }
-
- // Delete the specified amount
- nsresult rv = DoTransaction(deleteSelectionTransaction);
-
- if (mRules && mRules->AsHTMLEditRules() && deleteCharData) {
- RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
- htmlEditRules->DidDeleteText(deleteNode, deleteCharOffset, 1);
- }
-
- if (mTextServicesDocument && NS_SUCCEEDED(rv) &&
- deleteNode && !deleteCharData) {
- RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
- textServicesDocument->DidDeleteNode(deleteNode);
- }
-
- // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
- {
- AutoActionListenerArray listeners(mActionListeners);
- if (!deleteNode) {
- for (auto& listener : mActionListeners) {
- listener->DidDeleteSelection(selection);
- }
- } else if (deleteCharData) {
- for (auto& listener : mActionListeners) {
- listener->DidDeleteText(deleteCharData, deleteCharOffset, 1, rv);
- }
- } else {
- for (auto& listener : mActionListeners) {
- listener->DidDeleteNode(deleteNode->AsDOMNode(), rv);
- }
- }
- }
-
- return rv;
-}
-
-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->AnchorRef());
- if (!pointToInsert.IsSet()) {
- return nullptr;
- }
- RefPtr<Element> newElement = CreateNodeWithTransaction(aTag, pointToInsert);
-
- // We want the selection to be just after the new node
- EditorRawDOMPoint afterNewElement(newElement);
- MOZ_ASSERT(afterNewElement.IsSetAndValid());
- DebugOnly<bool> advanced = afterNewElement.AdvanceOffset();
- NS_WARNING_ASSERTION(advanced,
- "Failed to move offset next to the new element");
- ErrorResult error;
- selection->Collapse(afterNewElement, error);
- if (NS_WARN_IF(error.Failed())) {
- // XXX Even if it succeeded to create new element, this returns error
- // when Selection.Collapse() fails something. This could occur with
- // mutation observer or mutation event listener.
- error.SuppressException();
- return nullptr;
- }
- return newElement.forget();
-}
-
TextComposition*
EditorBase::GetComposition() const
{
return mComposition;
}
bool
EditorBase::IsIMEComposing() const
@@ -4398,89 +4277,16 @@ bool
EditorBase::ShouldHandleIMEComposition() const
{
// When the editor is being reframed, the old value may be restored with
// InsertText(). In this time, the text should be inserted as not a part
// of the composition.
return mComposition && mDidPostCreate;
}
-nsresult
-EditorBase::DeleteSelectionAndPrepareToCreateNode()
-{
- RefPtr<Selection> selection = GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
- if (NS_WARN_IF(!selection->GetAnchorFocusRange())) {
- return NS_OK;
- }
-
- if (!selection->GetAnchorFocusRange()->Collapsed()) {
- nsresult rv = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
-
- 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
- EditorDOMPoint atAnchor(selection->AnchorRef());
- if (NS_WARN_IF(!atAnchor.IsSet()) || !atAnchor.IsInDataNode()) {
- return NS_OK;
- }
-
- if (NS_WARN_IF(!atAnchor.GetContainer()->GetParentNode())) {
- return NS_ERROR_FAILURE;
- }
-
- if (atAnchor.IsStartOfContainer()) {
- EditorRawDOMPoint atAnchorContainer(atAnchor.GetContainer());
- if (NS_WARN_IF(!atAnchorContainer.IsSetAndValid())) {
- return NS_ERROR_FAILURE;
- }
- ErrorResult error;
- selection->Collapse(atAnchorContainer, error);
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- return NS_OK;
- }
-
- if (atAnchor.IsEndOfContainer()) {
- EditorRawDOMPoint afterAnchorContainer(atAnchor.GetContainer());
- if (NS_WARN_IF(!afterAnchorContainer.AdvanceOffset())) {
- return NS_ERROR_FAILURE;
- }
- ErrorResult error;
- selection->Collapse(afterAnchorContainer, error);
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- return NS_OK;
- }
-
- ErrorResult error;
- nsCOMPtr<nsIContent> newLeftNode = SplitNodeWithTransaction(atAnchor, error);
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
-
- EditorRawDOMPoint atRightNode(atAnchor.GetContainer());
- 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();
- }
- return NS_OK;
-}
-
void
EditorBase::DoAfterDoTransaction(nsITransaction* aTxn)
{
bool isTransientTransaction;
MOZ_ALWAYS_SUCCEEDS(aTxn->GetIsTransient(&isTransientTransaction));
if (!isTransientTransaction) {
// we need to deal here with the case where the user saved after some
@@ -4892,36 +4698,16 @@ EditorBase::HandleKeyPressEvent(WidgetKe
switch (aKeyboardEvent->mKeyCode) {
case NS_VK_META:
case NS_VK_WIN:
case NS_VK_SHIFT:
case NS_VK_CONTROL:
case NS_VK_ALT:
aKeyboardEvent->PreventDefault(); // consumed
return NS_OK;
- case NS_VK_BACK:
- if (aKeyboardEvent->IsControl() || aKeyboardEvent->IsAlt() ||
- aKeyboardEvent->IsMeta() || aKeyboardEvent->IsOS()) {
- return NS_OK;
- }
- DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
- aKeyboardEvent->PreventDefault(); // consumed
- return NS_OK;
- case NS_VK_DELETE:
- // on certain platforms (such as windows) the shift key
- // modifies what delete does (cmd_cut in this case).
- // bailing here to allow the keybindings to do the cut.
- if (aKeyboardEvent->IsShift() || aKeyboardEvent->IsControl() ||
- aKeyboardEvent->IsAlt() || aKeyboardEvent->IsMeta() ||
- aKeyboardEvent->IsOS()) {
- return NS_OK;
- }
- DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
- aKeyboardEvent->PreventDefault(); // consumed
- return NS_OK;
}
return NS_OK;
}
nsresult
EditorBase::HandleInlineSpellCheck(EditAction action,
Selection* aSelection,
nsINode* previousSelectedNode,
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -324,21 +324,16 @@ public:
nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
Text& aTextNode, int32_t aOffset,
bool aSuppressIME = false);
nsresult SetTextImpl(Selection& aSelection,
const nsAString& aString,
Text& aTextNode);
- NS_IMETHOD DeleteSelectionImpl(EDirection aAction,
- EStripWrappers aStripWrappers);
-
- already_AddRefed<Element> DeleteSelectionAndCreateElement(nsAtom& aTag);
-
/**
* DeleteNodeWithTransaction() removes aNode from the DOM tree.
*
* @param aNode The node which will be removed form the DOM tree.
*/
nsresult DeleteNodeWithTransaction(nsINode& aNode);
/**
@@ -586,25 +581,16 @@ protected:
* @param aCharData The data node which should be modified.
* @param aOffset Start offset of removing text in aCharData.
* @param aLength Length of removing text.
*/
nsresult DeleteTextWithTransaction(dom::CharacterData& aCharacterData,
uint32_t aOffset, uint32_t aLength);
/**
- * This method first deletes the selection, if it's not collapsed. Then if
- * the selection lies in a CharacterData node, it splits it. If the
- * selection is at this point collapsed in a CharacterData node, it's
- * adjusted to be collapsed right before or after the node instead (which is
- * always possible, since the node was split).
- */
- nsresult DeleteSelectionAndPrepareToCreateNode();
-
- /**
* Called after a transaction is done successfully.
*/
void DoAfterDoTransaction(nsITransaction *aTxn);
/**
* Called after a transaction is undone successfully.
*/
@@ -775,24 +761,24 @@ protected:
virtual void InitializeSelectionAncestorLimit(Selection& aSelection,
nsIContent& aAncestorLimit);
public:
/**
* All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction.
*/
- NS_IMETHOD StartOperation(EditAction opID,
- nsIEditor::EDirection aDirection);
+ virtual nsresult StartOperation(EditAction opID,
+ nsIEditor::EDirection aDirection);
/**
* All editor operations which alter the doc should be followed
* with a call to EndOperation.
*/
- NS_IMETHOD EndOperation();
+ virtual nsresult EndOperation();
/**
* Routines for managing the preservation of selection across
* various editor actions.
*/
bool ArePreservingSelection();
void PreserveSelectionAcrossActions(Selection* aSel);
nsresult RestorePreservedSelection(Selection* aSel);
--- a/editor/libeditor/EditorCommands.cpp
+++ b/editor/libeditor/EditorCommands.cpp
@@ -311,17 +311,23 @@ CutOrDeleteCommand::DoCommand(const char
nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
if (!editor) {
return NS_ERROR_FAILURE;
}
TextEditor* textEditor = editor->AsTextEditor();
MOZ_ASSERT(textEditor);
dom::Selection* selection = textEditor->GetSelection();
if (selection && selection->Collapsed()) {
- return textEditor->DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
+ nsresult rv =
+ textEditor->DeleteSelectionAsAction(nsIEditor::eNext,
+ nsIEditor::eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
}
return textEditor->Cut();
}
NS_IMETHODIMP
CutOrDeleteCommand::DoCommandParams(const char* aCommandName,
nsICommandParams* aParams,
nsISupports* aCommandRefCon)
@@ -423,17 +429,23 @@ CopyOrDeleteCommand::DoCommand(const cha
nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
if (!editor) {
return NS_ERROR_FAILURE;
}
TextEditor* textEditor = editor->AsTextEditor();
MOZ_ASSERT(textEditor);
dom::Selection* selection = textEditor->GetSelection();
if (selection && selection->Collapsed()) {
- return textEditor->DeleteSelection(nsIEditor::eNextWord, nsIEditor::eStrip);
+ nsresult rv =
+ textEditor->DeleteSelectionAsAction(nsIEditor::eNextWord,
+ nsIEditor::eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
}
return textEditor->Copy();
}
NS_IMETHODIMP
CopyOrDeleteCommand::DoCommandParams(const char* aCommandName,
nsICommandParams* aParams,
nsISupports* aCommandRefCon)
@@ -784,17 +796,22 @@ DeleteCommand::DoCommand(const char* aCo
} else if (!nsCRT::strcmp("cmd_deleteToEndOfLine", aCommandName)) {
deleteDir = nsIEditor::eToEndOfLine;
} else {
MOZ_CRASH("Unrecognized nsDeleteCommand");
}
TextEditor* textEditor = editor->AsTextEditor();
MOZ_ASSERT(textEditor);
- return textEditor->DeleteSelection(deleteDir, nsIEditor::eStrip);
+ nsresult rv =
+ textEditor->DeleteSelectionAsAction(deleteDir, nsIEditor::eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
}
NS_IMETHODIMP
DeleteCommand::DoCommandParams(const char* aCommandName,
nsICommandParams* aParams,
nsISupports* aCommandRefCon)
{
return DoCommand(aCommandName, aCommandRefCon);
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1283,88 +1283,97 @@ nsresult
HTMLEditRules::WillInsertText(EditAction aAction,
Selection* aSelection,
bool* aCancel,
bool* aHandled,
const nsAString* inString,
nsAString* outString,
int32_t aMaxLength)
{
- if (!aSelection || !aCancel || !aHandled) {
+ if (NS_WARN_IF(!aSelection) ||
+ NS_WARN_IF(!aCancel) ||
+ NS_WARN_IF(!aHandled)) {
return NS_ERROR_NULL_POINTER;
}
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+
// initialize out param
*aCancel = false;
*aHandled = true;
// If the selection isn't collapsed, delete it. Don't delete existing inline
// tags, because we're hopefully going to insert text (bug 787432).
if (!aSelection->Collapsed()) {
- NS_ENSURE_STATE(mHTMLEditor);
nsresult rv =
- mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip);
- NS_ENSURE_SUCCESS(rv, rv);
+ htmlEditor->DeleteSelectionAsAction(nsIEditor::eNone,
+ nsIEditor::eNoStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
WillInsert(*aSelection, aCancel);
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = false;
// we need to get the doc
- NS_ENSURE_STATE(mHTMLEditor);
- nsCOMPtr<nsIDocument> doc = mHTMLEditor->GetDocument();
- NS_ENSURE_STATE(doc);
+ nsCOMPtr<nsIDocument> doc = htmlEditor->GetDocument();
+ if (NS_WARN_IF(!doc)) {
+ return NS_ERROR_FAILURE;
+ }
// for every property that is set, insert a new inline style node
nsresult rv = CreateStyleForInsertText(*aSelection, *doc);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// get the (collapsed) selection location
- NS_ENSURE_STATE(mHTMLEditor);
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
- if (NS_WARN_IF(!mHTMLEditor) ||
- (!EditorBase::IsTextNode(pointToInsert.GetContainer()) &&
- !mHTMLEditor->CanContainTag(*pointToInsert.GetContainer(),
- *nsGkAtoms::textTagName))) {
+ if (!EditorBase::IsTextNode(pointToInsert.GetContainer()) &&
+ !htmlEditor->CanContainTag(*pointToInsert.GetContainer(),
+ *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(pointToInsert.GetContainer());
+ htmlEditor->GetIMESelectionStartOffsetIn(pointToInsert.GetContainer());
if (IMESelectionOffset >= 0) {
pointToInsert.Set(pointToInsert.GetContainer(), IMESelectionOffset);
}
if (inString->IsEmpty()) {
- rv = mHTMLEditor->InsertTextImpl(*doc, *inString,
- EditorRawDOMPoint(pointToInsert));
+ rv = htmlEditor->InsertTextImpl(*doc, *inString,
+ EditorRawDOMPoint(pointToInsert));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
- WSRunObject wsObj(mHTMLEditor, pointToInsert);
+ WSRunObject wsObj(htmlEditor, pointToInsert);
rv = wsObj.InsertText(*doc, *inString, pointToInsert);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// aAction == kInsertText
@@ -1378,26 +1387,24 @@ HTMLEditRules::WillInsertText(EditAction
// 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);
+ AutoTransactionsConserveSelection dontChangeMySelection(htmlEditor);
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, &pointToInsert);
+ AutoTrackDOMPoint tracker(htmlEditor->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;
@@ -1414,21 +1421,22 @@ HTMLEditRules::WillInsertText(EditAction
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<Element> br =
- mHTMLEditor->CreateBRImpl(*aSelection, currentPoint,
- nsIEditor::eNone);
- NS_ENSURE_STATE(br);
+ htmlEditor->CreateBRImpl(*aSelection, currentPoint,
+ nsIEditor::eNone);
+ if (NS_WARN_IF(!br)) {
+ return NS_ERROR_FAILURE;
+ }
pos++;
if (br->GetNextSibling()) {
pointToInsert.Set(br->GetNextSibling());
} else {
pointToInsert.SetToEndOf(currentPoint.GetContainer());
}
// XXX In most cases, pointToInsert and currentPoint are same here.
// But if the <br> element has been moved to different point by
@@ -1436,22 +1444,23 @@ HTMLEditRules::WillInsertText(EditAction
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;
- rv = mHTMLEditor->InsertTextImpl(*doc, subStr,
- EditorRawDOMPoint(currentPoint),
- &pointAfterInsertedString);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = htmlEditor->InsertTextImpl(*doc, subStr,
+ EditorRawDOMPoint(currentPoint),
+ &pointAfterInsertedString);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
currentPoint = pointAfterInsertedString;
pointToInsert = pointAfterInsertedString;
}
}
} else {
NS_NAMED_LITERAL_STRING(tabStr, "\t");
NS_NAMED_LITERAL_STRING(spacesStr, " ");
char specialChars[] = {TAB, nsCRT::LF, 0};
@@ -1468,18 +1477,17 @@ HTMLEditRules::WillInsertText(EditAction
subStrLen = 1;
}
} else {
subStrLen = tString.Length() - oldPos;
pos = tString.Length();
}
nsDependentSubstring subStr(tString, oldPos, subStrLen);
- NS_ENSURE_STATE(mHTMLEditor);
- WSRunObject wsObj(mHTMLEditor, currentPoint);
+ WSRunObject wsObj(htmlEditor, currentPoint);
// is it a tab?
if (subStr.Equals(tabStr)) {
EditorRawDOMPoint pointAfterInsertedSpaces;
rv = wsObj.InsertText(*doc, spacesStr, currentPoint,
&pointAfterInsertedSpaces);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@@ -1623,18 +1631,20 @@ HTMLEditRules::WillInsertBreak(Selection
*aHandled = false;
NS_ENSURE_STATE(mHTMLEditor);
RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
// If the selection isn't collapsed, delete it.
if (!aSelection.Collapsed()) {
nsresult rv =
- htmlEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
+ htmlEditor->DeleteSelectionAsAction(nsIEditor::eNone, nsIEditor::eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
WillInsert(aSelection, aCancel);
// Initialize out param. We want to ignore result of WillInsert().
*aCancel = false;
// Split any mailcites in the way. Should we abort this if we encounter
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -707,21 +707,23 @@ HTMLEditor::HandleKeyPressEvent(WidgetKe
"HandleKeyPressEvent gets non-keypress event");
switch (aKeyboardEvent->mKeyCode) {
case NS_VK_META:
case NS_VK_WIN:
case NS_VK_SHIFT:
case NS_VK_CONTROL:
case NS_VK_ALT:
- case NS_VK_BACK:
- case NS_VK_DELETE:
// These keys are handled on EditorBase, so, we can bypass
// TextEditor.
return EditorBase::HandleKeyPressEvent(aKeyboardEvent);
+ case NS_VK_BACK:
+ case NS_VK_DELETE:
+ // These keys are handled on TextEditor.
+ return TextEditor::HandleKeyPressEvent(aKeyboardEvent);
case NS_VK_TAB: {
if (IsPlaintextEditor()) {
// If this works as plain text editor, e.g., mail editor for plain
// text, should be handled on TextEditor.
return TextEditor::HandleKeyPressEvent(aKeyboardEvent);
}
if (IsTabbable()) {
@@ -1138,18 +1140,20 @@ HTMLEditor::InsertBR()
{
// calling it text insertion to trigger moz br treatment by rules
AutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext);
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_STATE(selection);
if (!selection->Collapsed()) {
- nsresult rv = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rv = DeleteSelectionAsAction(eNone, eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
EditorRawDOMPoint atStartOfSelection(GetStartPoint(selection));
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
return NS_ERROR_FAILURE;
}
// CreateBRImpl() will set selection after the new <br> element.
@@ -1550,18 +1554,20 @@ HTMLEditor::InsertElementAtSelection(nsI
if (!handled) {
if (aDeleteSelection) {
if (!IsBlockNode(element)) {
// E.g., inserting an image. In this case we don't need to delete any
// inline wrappers before we do the insertion. Otherwise we let
// DeleteSelectionAndPrepareToCreateNode do the deletion for us, which
// calls DeleteSelection with aStripWrappers = eStrip.
- rv = DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = DeleteSelectionAsAction(eNone, eNoStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
nsresult rv = DeleteSelectionAndPrepareToCreateNode();
NS_ENSURE_SUCCESS(rv, rv);
}
// If deleting, selection will be collapsed.
// so if not, we collapse it
@@ -3136,23 +3142,23 @@ HTMLEditor::GetEmbeddedObjects(nsIArray*
}
iter->Next();
}
nodes.forget(aNodeList);
return rv;
}
-NS_IMETHODIMP
+nsresult
HTMLEditor::DeleteSelectionImpl(EDirection aAction,
EStripWrappers aStripWrappers)
{
MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
- nsresult rv = EditorBase::DeleteSelectionImpl(aAction, aStripWrappers);
+ nsresult rv = TextEditor::DeleteSelectionImpl(aAction, aStripWrappers);
NS_ENSURE_SUCCESS(rv, rv);
// If we weren't asked to strip any wrappers, we're done.
if (aStripWrappers == eNoStrip) {
return NS_OK;
}
RefPtr<Selection> selection = GetSelection();
@@ -3425,17 +3431,17 @@ HTMLEditor::StyleSheetLoaded(StyleSheet*
return NS_OK;
}
/**
* All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction.
*/
-NS_IMETHODIMP
+nsresult
HTMLEditor::StartOperation(EditAction opID,
nsIEditor::EDirection aDirection)
{
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
EditorBase::StartOperation(opID, aDirection); // will set mAction, mDirection
if (rules) {
@@ -3443,17 +3449,17 @@ HTMLEditor::StartOperation(EditAction op
}
return NS_OK;
}
/**
* All editor operations which alter the doc should be followed
* with a call to EndOperation.
*/
-NS_IMETHODIMP
+nsresult
HTMLEditor::EndOperation()
{
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
// post processing
nsresult rv = rules ? rules->AfterEdit(mAction, mDirection) : NS_OK;
EditorBase::EndOperation(); // will clear mAction, mDirection
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -345,24 +345,24 @@ public:
NS_IMETHOD DebugUnitTests(int32_t* outNumTests,
int32_t* outNumTestsFailed) override;
/**
* All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction.
*/
- NS_IMETHOD StartOperation(EditAction opID,
- nsIEditor::EDirection aDirection) override;
+ virtual nsresult StartOperation(EditAction opID,
+ nsIEditor::EDirection aDirection) override;
/**
* All editor operations which alter the doc should be followed
* with a call to EndOperation.
*/
- NS_IMETHOD EndOperation() override;
+ virtual nsresult EndOperation() override;
/**
* returns true if aParentTag can contain a child of type aChildTag.
*/
virtual bool TagCanContainTag(nsAtom& aParentTag,
nsAtom& aChildTag) const override;
/**
@@ -378,18 +378,18 @@ public:
/**
* Join together any adjacent editable text nodes in the range.
*/
nsresult CollapseAdjacentTextNodes(nsRange* aRange);
virtual bool AreNodesSameType(nsIContent* aNode1,
nsIContent* aNode2) override;
- NS_IMETHOD DeleteSelectionImpl(EDirection aAction,
- EStripWrappers aStripWrappers) override;
+ virtual nsresult DeleteSelectionImpl(EDirection aAction,
+ EStripWrappers aStripWrappers) override;
/**
* DeleteNodeWithTransaction() removes aNode from the DOM tree if it's
* modifiable. Note that this is not an override of same method of
* EditorBase.
*
* @param aNode The node to be removed from the DOM tree.
*/
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -109,18 +109,20 @@ HTMLEditor::LoadHTML(const nsAString& aI
NS_ENSURE_SUCCESS(rv, rv);
if (cancel) {
return NS_OK; // rules canceled the operation
}
if (!handled) {
// Delete Selection, but only if it isn't collapsed, see bug #106269
if (!selection->Collapsed()) {
- rv = DeleteSelection(eNone, eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = DeleteSelectionAsAction(eNone, eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
// Get the first range in the selection, for context:
RefPtr<nsRange> range = selection->GetRangeAt(0);
NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
// Create fragment for pasted HTML.
ErrorResult error;
@@ -248,18 +250,20 @@ HTMLEditor::DoInsertHTMLWithContext(cons
// also occur later; this block is intended to cover the various
// scenarios where we are dropping in an editor (and may want to delete
// the selection before collapsing the selection in the new destination)
if (aDestNode) {
if (aDeleteSelection) {
// Use an auto tracker so that our drop point is correctly
// positioned after the delete.
AutoTrackDOMPoint tracker(mRangeUpdater, &targetPoint);
- rv = DeleteSelection(eNone, eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = DeleteSelectionAsAction(eNone, eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
ErrorResult error;
selection->Collapse(targetPoint, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
}
@@ -275,17 +279,20 @@ HTMLEditor::DoInsertHTMLWithContext(cons
streamStartOffset,
streamEndParent,
streamEndOffset);
if (nodeList.IsEmpty()) {
// We aren't inserting anything, but if aDeleteSelection is set, we do want
// to delete everything.
if (aDeleteSelection) {
- return DeleteSelection(eNone, eStrip);
+ nsresult rv = DeleteSelectionAsAction(eNone, eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
return NS_OK;
}
// Are there any table elements in the list?
// check for table cell selection mode
bool cellSelectionMode = false;
nsCOMPtr<nsIDOMElement> cell;
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -694,17 +694,21 @@ HTMLEditor::DeleteTable2(nsIDOMElement*
// Select the table
nsresult rv = ClearSelection();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = AppendNodeToSelectionAsRange(aTable);
NS_ENSURE_SUCCESS(rv, rv);
- return DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip);
+ rv = DeleteSelectionAsAction(eNext, eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
}
NS_IMETHODIMP
HTMLEditor::DeleteTable()
{
RefPtr<Selection> selection;
nsCOMPtr<nsIDOMElement> table;
nsresult rv = GetCellContext(getter_AddRefs(selection),
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -433,17 +433,18 @@ TextEditRules::WillInsertBreak(Selection
return NS_OK;
}
*aCancel = false;
// if the selection isn't collapsed, delete it.
if (!aSelection->IsCollapsed()) {
NS_ENSURE_STATE(mTextEditor);
- rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
+ rv = mTextEditor->DeleteSelectionAsAction(nsIEditor::eNone,
+ nsIEditor::eStrip);
NS_ENSURE_SUCCESS(rv, rv);
}
WillInsert(*aSelection, aCancel);
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = false;
}
@@ -680,17 +681,18 @@ TextEditRules::WillInsertText(EditAction
nsContentUtils::GetSelectionInTextControl(aSelection,
mTextEditor->GetRoot(),
start, end);
}
// if the selection isn't collapsed, delete it.
if (!aSelection->IsCollapsed()) {
NS_ENSURE_STATE(mTextEditor);
- rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
+ rv = mTextEditor->DeleteSelectionAsAction(nsIEditor::eNone,
+ nsIEditor::eStrip);
NS_ENSURE_SUCCESS(rv, rv);
}
WillInsert(*aSelection, aCancel);
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = false;
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1,28 +1,31 @@
/* -*- 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/TextEditor.h"
+#include "EditAggregateTransaction.h"
+#include "HTMLEditRules.h"
#include "InternetCiter.h"
#include "TextEditUtils.h"
#include "gfxFontUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/EditAction.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditorUtils.h" // AutoPlaceholderBatch, AutoRules
#include "mozilla/HTMLEditor.h"
#include "mozilla/mozalloc.h"
#include "mozilla/Preferences.h"
#include "mozilla/TextEditRules.h"
#include "mozilla/TextComposition.h"
#include "mozilla/TextEvents.h"
+#include "mozilla/TextServicesDocument.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Element.h"
#include "nsAString.h"
#include "nsCRT.h"
#include "nsCaret.h"
#include "nsCharTraits.h"
#include "nsComponentManagerUtils.h"
@@ -351,20 +354,38 @@ TextEditor::HandleKeyPressEvent(WidgetKe
"HandleKeyPressEvent gets non-keypress event");
switch (aKeyboardEvent->mKeyCode) {
case NS_VK_META:
case NS_VK_WIN:
case NS_VK_SHIFT:
case NS_VK_CONTROL:
case NS_VK_ALT:
- case NS_VK_BACK:
- case NS_VK_DELETE:
// These keys are handled on EditorBase
return EditorBase::HandleKeyPressEvent(aKeyboardEvent);
+ case NS_VK_BACK:
+ if (aKeyboardEvent->IsControl() || aKeyboardEvent->IsAlt() ||
+ aKeyboardEvent->IsMeta() || aKeyboardEvent->IsOS()) {
+ return NS_OK;
+ }
+ DeleteSelectionAsAction(nsIEditor::ePrevious, nsIEditor::eStrip);
+ aKeyboardEvent->PreventDefault(); // consumed
+ return NS_OK;
+ case NS_VK_DELETE:
+ // on certain platforms (such as windows) the shift key
+ // modifies what delete does (cmd_cut in this case).
+ // bailing here to allow the keybindings to do the cut.
+ if (aKeyboardEvent->IsShift() || aKeyboardEvent->IsControl() ||
+ aKeyboardEvent->IsAlt() || aKeyboardEvent->IsMeta() ||
+ aKeyboardEvent->IsOS()) {
+ return NS_OK;
+ }
+ DeleteSelectionAsAction(nsIEditor::eNext, nsIEditor::eStrip);
+ aKeyboardEvent->PreventDefault(); // consumed
+ return NS_OK;
case NS_VK_TAB: {
if (IsTabbable()) {
return NS_OK; // let it be used for focus switching
}
if (aKeyboardEvent->IsShift() || aKeyboardEvent->IsControl() ||
aKeyboardEvent->IsAlt() || aKeyboardEvent->IsMeta() ||
aKeyboardEvent->IsOS()) {
@@ -623,69 +644,294 @@ TextEditor::ExtendSelectionForDelete(Sel
// For avoiding several compiler warnings
default:
return NS_OK;
}
}
return NS_OK;
}
-nsresult
+NS_IMETHODIMP
TextEditor::DeleteSelection(EDirection aAction,
EStripWrappers aStripWrappers)
{
+ nsresult rv = DeleteSelectionAsAction(aAction, aStripWrappers);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+nsresult
+TextEditor::DeleteSelectionAsAction(EDirection aDirection,
+ EStripWrappers aStripWrappers)
+{
MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
if (!mRules) {
return NS_ERROR_NOT_INITIALIZED;
}
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
// delete placeholder txns merge.
AutoPlaceholderBatch batch(this, nsGkAtoms::DeleteTxnName);
- AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
+ AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aDirection);
// pre-process
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
// If there is an existing selection when an extended delete is requested,
// platforms that use "caret-style" caret positioning collapse the
// selection to the start and then create a new selection.
// Platforms that use "selection-style" caret positioning just delete the
// existing selection without extending it.
- if (!selection->Collapsed() &&
- (aAction == eNextWord || aAction == ePreviousWord ||
- aAction == eToBeginningOfLine || aAction == eToEndOfLine)) {
- if (mCaretStyle == 1) {
- nsresult rv = selection->CollapseToStart();
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- aAction = eNone;
+ if (!selection->Collapsed()) {
+ switch (aDirection) {
+ case eNextWord:
+ case ePreviousWord:
+ case eToBeginningOfLine:
+ case eToEndOfLine: {
+ if (mCaretStyle != 1) {
+ aDirection = eNone;
+ break;
+ }
+ ErrorResult error;
+ selection->CollapseToStart(error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+ break;
+ }
+ default:
+ break;
}
}
RulesInfo ruleInfo(EditAction::deleteSelection);
- ruleInfo.collapsedAction = aAction;
+ ruleInfo.collapsedAction = aDirection;
ruleInfo.stripWrappers = aStripWrappers;
bool cancel, handled;
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
NS_ENSURE_SUCCESS(rv, rv);
if (!cancel && !handled) {
- rv = DeleteSelectionImpl(aAction, aStripWrappers);
+ rv = DeleteSelectionImpl(aDirection, aStripWrappers);
}
if (!cancel) {
// post-process
rv = rules->DidDoAction(selection, &ruleInfo, rv);
}
return rv;
}
+nsresult
+TextEditor::DeleteSelectionImpl(EDirection aDirection,
+ EStripWrappers aStripWrappers)
+{
+ MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
+
+ RefPtr<Selection> selection = GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ RefPtr<EditAggregateTransaction> deleteSelectionTransaction;
+ nsCOMPtr<nsINode> deleteNode;
+ int32_t deleteCharOffset = 0, deleteCharLength = 0;
+ if (!selection->Collapsed() || aDirection != eNone) {
+ deleteSelectionTransaction =
+ CreateTxnForDeleteSelection(aDirection,
+ getter_AddRefs(deleteNode),
+ &deleteCharOffset,
+ &deleteCharLength);
+ if (NS_WARN_IF(!deleteSelectionTransaction)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ RefPtr<CharacterData> deleteCharData =
+ CharacterData::FromNodeOrNull(deleteNode);
+ AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aDirection);
+
+ if (mRules && mRules->AsHTMLEditRules()) {
+ if (!deleteNode) {
+ RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+ htmlEditRules->WillDeleteSelection(selection);
+ } else if (!deleteCharData) {
+ RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+ htmlEditRules->WillDeleteNode(deleteNode);
+ }
+ }
+
+ // Notify nsIEditActionListener::WillDelete[Selection|Text]
+ if (!mActionListeners.IsEmpty()) {
+ if (!deleteNode) {
+ AutoActionListenerArray listeners(mActionListeners);
+ for (auto& listener : listeners) {
+ listener->WillDeleteSelection(selection);
+ }
+ } else if (deleteCharData) {
+ AutoActionListenerArray listeners(mActionListeners);
+ for (auto& listener : listeners) {
+ listener->WillDeleteText(deleteCharData, deleteCharOffset, 1);
+ }
+ }
+ }
+
+ // Delete the specified amount
+ nsresult rv = DoTransaction(deleteSelectionTransaction);
+
+ if (mRules && mRules->AsHTMLEditRules() && deleteCharData) {
+ RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+ htmlEditRules->DidDeleteText(deleteNode, deleteCharOffset, 1);
+ }
+
+ if (mTextServicesDocument && NS_SUCCEEDED(rv) &&
+ deleteNode && !deleteCharData) {
+ RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
+ textServicesDocument->DidDeleteNode(deleteNode);
+ }
+
+ // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
+ {
+ AutoActionListenerArray listeners(mActionListeners);
+ if (!deleteNode) {
+ for (auto& listener : mActionListeners) {
+ listener->DidDeleteSelection(selection);
+ }
+ } else if (deleteCharData) {
+ for (auto& listener : mActionListeners) {
+ listener->DidDeleteText(deleteCharData, deleteCharOffset, 1, rv);
+ }
+ } else {
+ for (auto& listener : mActionListeners) {
+ listener->DidDeleteNode(deleteNode->AsDOMNode(), rv);
+ }
+ }
+ }
+
+ return rv;
+}
+
+already_AddRefed<Element>
+TextEditor::DeleteSelectionAndCreateElement(nsAtom& aTag)
+{
+ nsresult rv = DeleteSelectionAndPrepareToCreateNode();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ RefPtr<Selection> selection = GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return nullptr;
+ }
+
+ EditorRawDOMPoint pointToInsert(selection->AnchorRef());
+ if (!pointToInsert.IsSet()) {
+ return nullptr;
+ }
+ RefPtr<Element> newElement = CreateNodeWithTransaction(aTag, pointToInsert);
+
+ // We want the selection to be just after the new node
+ EditorRawDOMPoint afterNewElement(newElement);
+ MOZ_ASSERT(afterNewElement.IsSetAndValid());
+ DebugOnly<bool> advanced = afterNewElement.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to move offset next to the new element");
+ ErrorResult error;
+ selection->Collapse(afterNewElement, error);
+ if (NS_WARN_IF(error.Failed())) {
+ // XXX Even if it succeeded to create new element, this returns error
+ // when Selection.Collapse() fails something. This could occur with
+ // mutation observer or mutation event listener.
+ error.SuppressException();
+ return nullptr;
+ }
+ return newElement.forget();
+}
+
+nsresult
+TextEditor::DeleteSelectionAndPrepareToCreateNode()
+{
+ RefPtr<Selection> selection = GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (NS_WARN_IF(!selection->GetAnchorFocusRange())) {
+ return NS_OK;
+ }
+
+ if (!selection->GetAnchorFocusRange()->Collapsed()) {
+ nsresult rv = DeleteSelectionAsAction(eNone, eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ 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
+ EditorDOMPoint atAnchor(selection->AnchorRef());
+ if (NS_WARN_IF(!atAnchor.IsSet()) || !atAnchor.IsInDataNode()) {
+ return NS_OK;
+ }
+
+ if (NS_WARN_IF(!atAnchor.GetContainer()->GetParentNode())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (atAnchor.IsStartOfContainer()) {
+ EditorRawDOMPoint atAnchorContainer(atAnchor.GetContainer());
+ if (NS_WARN_IF(!atAnchorContainer.IsSetAndValid())) {
+ return NS_ERROR_FAILURE;
+ }
+ ErrorResult error;
+ selection->Collapse(atAnchorContainer, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+ return NS_OK;
+ }
+
+ if (atAnchor.IsEndOfContainer()) {
+ EditorRawDOMPoint afterAnchorContainer(atAnchor.GetContainer());
+ if (NS_WARN_IF(!afterAnchorContainer.AdvanceOffset())) {
+ return NS_ERROR_FAILURE;
+ }
+ ErrorResult error;
+ selection->Collapse(afterAnchorContainer, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+ return NS_OK;
+ }
+
+ ErrorResult error;
+ nsCOMPtr<nsIContent> newLeftNode = SplitNodeWithTransaction(atAnchor, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+
+ EditorRawDOMPoint atRightNode(atAnchor.GetContainer());
+ 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();
+ }
+ return NS_OK;
+}
+
NS_IMETHODIMP
TextEditor::InsertText(const nsAString& aStringToInsert)
{
if (!mRules) {
return NS_ERROR_NOT_INITIALIZED;
}
// Protect the edit rules object from dying
@@ -853,17 +1099,18 @@ TextEditor::SetText(const nsAString& aSt
return NS_ERROR_FAILURE;
}
rv = selection->Collapse(rootElement, 0);
} else {
rv = EditorBase::SelectEntireDocument(selection);
}
if (NS_SUCCEEDED(rv)) {
if (aString.IsEmpty()) {
- rv = DeleteSelection(eNone, eStrip);
+ rv = DeleteSelectionAsAction(eNone, eStrip);
+ NS_WARNING_ASSERTION(NS_FAILED(rv), "Failed to remove all text");
} else {
rv = InsertText(aString);
}
}
}
// post-process
return rules->DidDoAction(selection, &ruleInfo, rv);
}
@@ -1280,17 +1527,17 @@ TextEditor::FireClipboardEvent(EventMess
return !mDidPreDestroy;
}
NS_IMETHODIMP
TextEditor::Cut()
{
bool actionTaken = false;
if (FireClipboardEvent(eCut, nsIClipboard::kGlobalClipboard, &actionTaken)) {
- DeleteSelection(eNone, eStrip);
+ DeleteSelectionAsAction(eNone, eStrip);
}
return actionTaken ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
TextEditor::CanCut(bool* aCanCut)
{
NS_ENSURE_ARG_POINTER(aCanCut);
@@ -1663,17 +1910,17 @@ TextEditor::GetEmbeddedObjects(nsIArray*
*aNodeList = nullptr;
return NS_OK;
}
/**
* All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction.
*/
-NS_IMETHODIMP
+nsresult
TextEditor::StartOperation(EditAction opID,
nsIEditor::EDirection aDirection)
{
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
EditorBase::StartOperation(opID, aDirection); // will set mAction, mDirection
if (rules) {
@@ -1681,17 +1928,17 @@ TextEditor::StartOperation(EditAction op
}
return NS_OK;
}
/**
* All editor operations which alter the doc should be followed
* with a call to EndOperation.
*/
-NS_IMETHODIMP
+nsresult
TextEditor::EndOperation()
{
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
// post processing
nsresult rv = rules ? rules->AfterEdit(mAction, mDirection) : NS_OK;
EditorBase::EndOperation(); // will clear mAction, mDirection
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -54,33 +54,17 @@ public:
TextEditor();
// nsIPlaintextEditor methods
NS_DECL_NSIPLAINTEXTEDITOR
// nsIEditorMailSupport overrides
NS_DECL_NSIEDITORMAILSUPPORT
- // Overrides of EditorBase
- virtual nsresult RemoveAttributeOrEquivalent(
- Element* aElement,
- nsAtom* aAttribute,
- bool aSuppressTransaction) override;
- virtual nsresult SetAttributeOrEquivalent(Element* aElement,
- nsAtom* aAttribute,
- const nsAString& aValue,
- bool aSuppressTransaction) override;
- using EditorBase::RemoveAttributeOrEquivalent;
- using EditorBase::SetAttributeOrEquivalent;
-
- virtual nsresult Init(nsIDocument& aDoc, Element* aRoot,
- nsISelectionController* aSelCon, uint32_t aFlags,
- const nsAString& aValue) override;
-
- nsresult DocumentIsEmpty(bool* aIsEmpty);
+ // Overrides of nsIEditor
NS_IMETHOD GetDocumentIsEmpty(bool* aDocumentIsEmpty) override;
NS_IMETHOD DeleteSelection(EDirection aAction,
EStripWrappers aStripWrappers) override;
NS_IMETHOD SetDocumentCharacterSet(const nsACString& characterSet) override;
// If there are some good name to create non-virtual Undo()/Redo() methods,
@@ -103,28 +87,46 @@ public:
uint32_t aFlags,
nsAString& aOutputString) override;
NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream,
const nsAString& aFormatType,
const nsACString& aCharsetOverride,
uint32_t aFlags) override;
+ // Overrides of EditorBase
+ virtual nsresult RemoveAttributeOrEquivalent(
+ Element* aElement,
+ nsAtom* aAttribute,
+ bool aSuppressTransaction) override;
+ virtual nsresult SetAttributeOrEquivalent(Element* aElement,
+ nsAtom* aAttribute,
+ const nsAString& aValue,
+ bool aSuppressTransaction) override;
+ using EditorBase::RemoveAttributeOrEquivalent;
+ using EditorBase::SetAttributeOrEquivalent;
+
+ virtual nsresult Init(nsIDocument& aDoc, Element* aRoot,
+ nsISelectionController* aSelCon, uint32_t aFlags,
+ const nsAString& aValue) override;
+
+ nsresult DocumentIsEmpty(bool* aIsEmpty);
+
/**
* All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction.
*/
- NS_IMETHOD StartOperation(EditAction opID,
- nsIEditor::EDirection aDirection) override;
+ virtual nsresult StartOperation(EditAction opID,
+ nsIEditor::EDirection aDirection) override;
/**
* All editor operations which alter the doc should be followed
* with a call to EndOperation.
*/
- NS_IMETHOD EndOperation() override;
+ virtual nsresult EndOperation() override;
/**
* Make the given selection span the entire document.
*/
virtual nsresult SelectEntireDocument(Selection* aSelection) override;
virtual nsresult HandleKeyPressEvent(
WidgetKeyboardEvent* aKeyboardEvent) override;
@@ -132,16 +134,31 @@ public:
virtual dom::EventTarget* GetDOMEventTarget() override;
virtual nsresult BeginIMEComposition(WidgetCompositionEvent* aEvent) override;
virtual nsresult UpdateIMEComposition(
WidgetCompositionEvent* aCompositionChangeEvet) override;
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override;
+ /**
+ * DeleteSelectionAsAction() removes selection content or content around
+ * caret with transactions. This should be used for handling it as an
+ * edit action.
+ *
+ * @param aDirection How much range should be removed.
+ * @param aStripWrappers Whether the parent blocks should be removed
+ * when they become empty.
+ */
+ nsresult DeleteSelectionAsAction(EDirection aDirection,
+ EStripWrappers aStripWrappers);
+
+ virtual nsresult DeleteSelectionImpl(EDirection aAction,
+ EStripWrappers aStripWrappers);
+
// Utility Routines, not part of public API
NS_IMETHOD TypedText(const nsAString& aString, ETypingAction aAction);
nsresult InsertTextAt(const nsAString& aStringToInsert,
nsINode* aDestinationNode,
int32_t aDestOffset,
bool aDoDeleteSelection);
@@ -243,16 +260,34 @@ protected:
/**
* Factored methods for handling insertion of data from transferables
* (drag&drop or clipboard).
*/
NS_IMETHOD PrepareTransferable(nsITransferable** transferable);
nsresult InsertTextFromTransferable(nsITransferable* transferable);
/**
+ * DeleteSelectionAndCreateElement() creates a element whose name is aTag.
+ * And insert it into the DOM tree after removing the selected content.
+ *
+ * @param aTag The element name to be created.
+ * @return Created new element.
+ */
+ already_AddRefed<Element> DeleteSelectionAndCreateElement(nsAtom& aTag);
+
+ /**
+ * This method first deletes the selection, if it's not collapsed. Then if
+ * the selection lies in a CharacterData node, it splits it. If the
+ * selection is at this point collapsed in a CharacterData node, it's
+ * adjusted to be collapsed right before or after the node instead (which is
+ * always possible, since the node was split).
+ */
+ nsresult DeleteSelectionAndPrepareToCreateNode();
+
+ /**
* Shared outputstring; returns whether selection is collapsed and resulting
* string.
*/
nsresult SharedOutputString(uint32_t aFlags, bool* aIsCollapsed,
nsAString& aResult);
enum PasswordFieldAllowed
{
--- a/editor/libeditor/TextEditorDataTransfer.cpp
+++ b/editor/libeditor/TextEditorDataTransfer.cpp
@@ -77,22 +77,27 @@ TextEditor::InsertTextAt(const nsAString
nsCOMPtr<nsINode> targetNode = aDestinationNode;
int32_t targetOffset = aDestOffset;
if (aDoDeleteSelection) {
// Use an auto tracker so that our drop point is correctly
// positioned after the delete.
AutoTrackDOMPoint tracker(mRangeUpdater, &targetNode, &targetOffset);
- nsresult rv = DeleteSelection(eNone, eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rv = DeleteSelectionAsAction(eNone, eStrip);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
- nsresult rv = selection->Collapse(targetNode, targetOffset);
- NS_ENSURE_SUCCESS(rv, rv);
+ ErrorResult error;
+ selection->Collapse(RawRangeBoundary(targetNode, targetOffset), error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
}
return InsertText(aStringToInsert);
}
nsresult
TextEditor::InsertTextFromTransferable(nsITransferable* aTransferable)
{
--- a/editor/spellchecker/TextServicesDocument.cpp
+++ b/editor/spellchecker/TextServicesDocument.cpp
@@ -1094,17 +1094,18 @@ TextServicesDocument::DeleteSelection()
// Make sure mIterator always points to something valid!
AdjustContentIterator();
// Now delete the actual content!
RefPtr<TextEditor> textEditor = mTextEditor;
nsresult rv =
- textEditor->DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
+ textEditor->DeleteSelectionAsAction(nsIEditor::ePrevious,
+ nsIEditor::eStrip);
if (NS_FAILED(rv)) {
UNLOCK_DOC(this);
return rv;
}
// Now that we've actually deleted the selected content,
// check to see if our mExtent has changed, if so, then
// we have to create a new content iterator!