--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -9,17 +9,16 @@
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
#include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
#include "mozilla/Maybe.h" // for Maybe
#include "mozilla/OwningNonNull.h" // for OwningNonNull
#include "mozilla/PresShell.h" // for PresShell
#include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary
#include "mozilla/SelectionState.h" // for RangeUpdater, etc.
#include "mozilla/StyleSheet.h" // for StyleSheet
-#include "mozilla/TextEditRules.h" // for TextEditRules
#include "mozilla/TransactionManager.h" // for TransactionManager
#include "mozilla/WeakPtr.h" // for WeakPtr
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/Text.h"
#include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr
#include "nsCycleCollectionParticipant.h"
#include "nsGkAtoms.h"
#include "nsIDocument.h" // for nsIDocument
@@ -71,16 +70,17 @@ class InsertNodeTransaction;
class InsertTextTransaction;
class JoinNodeTransaction;
class PlaceholderTransaction;
class RemoveStyleSheetTransaction;
class SplitNodeResult;
class SplitNodeTransaction;
class TextComposition;
class TextEditor;
+class TextEditRules;
class TextInputListener;
class TextServicesDocument;
enum class EditAction : int32_t;
namespace dom {
class DataTransfer;
class DragEvent;
class Element;
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -522,17 +522,17 @@ HTMLEditor::SetPositionToStatic(Element&
EmptyString());
}
if (aElement.IsHTMLElement(nsGkAtoms::div) &&
!HasStyleOrIdOrClass(&aElement)) {
RefPtr<HTMLEditRules> htmlRules =
static_cast<HTMLEditRules*>(mRules.get());
NS_ENSURE_TRUE(htmlRules, NS_ERROR_FAILURE);
- nsresult rv = htmlRules->MakeSureElemStartsOrEndsOnCR(aElement);
+ nsresult rv = htmlRules->MakeSureElemStartsAndEndsOnCR(aElement);
NS_ENSURE_SUCCESS(rv, rv);
rv = RemoveContainerWithTransaction(aElement);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -240,62 +240,77 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHER
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
mDocChangeRange, mUtilRange, mNewBlock,
mRangeItem)
nsresult
HTMLEditRules::Init(TextEditor* aTextEditor)
{
- if (NS_WARN_IF(!aTextEditor)) {
+ if (NS_WARN_IF(!aTextEditor) ||
+ NS_WARN_IF(!aTextEditor->AsHTMLEditor())) {
return NS_ERROR_INVALID_ARG;
}
InitFields();
mHTMLEditor = aTextEditor->AsHTMLEditor();
if (NS_WARN_IF(!mHTMLEditor)) {
- return NS_ERROR_INVALID_ARG;
- }
-
- // call through to base class Init
+ return NS_ERROR_FAILURE;
+ }
+ Selection* selection = aTextEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
nsresult rv = TextEditRules::Init(aTextEditor);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_FAILURE;
+ }
// cache any prefs we care about
static const char kPrefName[] =
"editor.html.typing.returnInEmptyListItemClosesList";
nsAutoCString returnInEmptyLIKillsList;
Preferences::GetCString(kPrefName, returnInEmptyLIKillsList);
// only when "false", becomes FALSE. Otherwise (including empty), TRUE.
// XXX Why was this pref designed as a string and not bool?
mReturnInEmptyLIKillsList = !returnInEmptyLIKillsList.EqualsLiteral("false");
// make a utility range for use by the listenter
- nsCOMPtr<nsINode> node = mHTMLEditor->GetRoot();
+ nsCOMPtr<nsINode> node = HTMLEditorRef().GetRoot();
if (!node) {
- node = mHTMLEditor->GetDocument();
- }
-
- NS_ENSURE_STATE(node);
+ node = HTMLEditorRef().GetDocument();
+ if (NS_WARN_IF(!node)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
mUtilRange = new nsRange(node);
// set up mDocChangeRange to be whole doc
// temporarily turn off rules sniffing
AutoLockRulesSniffing lockIt(this);
if (!mDocChangeRange) {
mDocChangeRange = new nsRange(node);
}
if (node->IsElement()) {
ErrorResult rv;
mDocChangeRange->SelectNode(*node, rv);
- NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
AdjustSpecialBreaks();
}
StartToListenToEditActions();
return NS_OK;
}
@@ -310,46 +325,48 @@ HTMLEditRules::DetachEditor()
nsresult
HTMLEditRules::BeforeEdit(EditAction aAction,
nsIEditor::EDirection aDirection)
{
if (mLockRulesSniffing) {
return NS_OK;
}
- NS_ENSURE_STATE(mHTMLEditor);
- RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
AutoLockRulesSniffing lockIt(this);
mDidExplicitlySetInterline = false;
if (!mActionNesting) {
mActionNesting++;
// Clear our flag about if just deleted a range
mDidRangedDelete = false;
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
// Remember where our selection was before edit action took place:
- // Get selection
- RefPtr<Selection> selection = htmlEditor->GetSelection();
-
// Get the selection location
- if (NS_WARN_IF(!selection) || !selection->RangeCount()) {
+ if (!SelectionRef().RangeCount()) {
return NS_ERROR_UNEXPECTED;
}
- mRangeItem->mStartContainer = selection->GetRangeAt(0)->GetStartContainer();
- mRangeItem->mStartOffset = selection->GetRangeAt(0)->StartOffset();
- mRangeItem->mEndContainer = selection->GetRangeAt(0)->GetEndContainer();
- mRangeItem->mEndOffset = selection->GetRangeAt(0)->EndOffset();
+ mRangeItem->StoreRange(SelectionRef().GetRangeAt(0));
nsCOMPtr<nsINode> selStartNode = mRangeItem->mStartContainer;
nsCOMPtr<nsINode> selEndNode = mRangeItem->mEndContainer;
// Register with range updater to track this as we perturb the doc
- htmlEditor->mRangeUpdater.RegisterRangeItem(mRangeItem);
+ HTMLEditorRef().mRangeUpdater.RegisterRangeItem(mRangeItem);
// Clear deletion state bool
mDidDeleteSelection = false;
// Clear out mDocChangeRange and mUtilRange
if (mDocChangeRange) {
// Clear out our accounting of what changed
mDocChangeRange->Reset();
@@ -362,21 +379,23 @@ HTMLEditRules::BeforeEdit(EditAction aAc
// Remember current inline styles for deletion and normal insertion ops
if (aAction == EditAction::insertText ||
aAction == EditAction::insertIMEText ||
aAction == EditAction::deleteSelection ||
IsStyleCachePreservingAction(aAction)) {
nsCOMPtr<nsINode> selNode =
aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
nsresult rv = CacheInlineStyles(selNode);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
// Stabilize the document against contenteditable count changes
- nsHTMLDocument* htmlDoc = htmlEditor->GetHTMLDocument();
+ nsHTMLDocument* htmlDoc = HTMLEditorRef().GetHTMLDocument();
if (NS_WARN_IF(!htmlDoc)) {
return NS_ERROR_FAILURE;
}
if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
htmlDoc->ChangeContentEditableCount(nullptr, +1);
mRestoreContentEditableCount = true;
}
@@ -392,46 +411,54 @@ HTMLEditRules::BeforeEdit(EditAction aAc
nsresult
HTMLEditRules::AfterEdit(EditAction aAction,
nsIEditor::EDirection aDirection)
{
if (mLockRulesSniffing) {
return NS_OK;
}
- NS_ENSURE_STATE(mHTMLEditor);
- RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
-
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
AutoLockRulesSniffing lockIt(this);
MOZ_ASSERT(mActionNesting > 0);
nsresult rv = NS_OK;
mActionNesting--;
if (!mActionNesting) {
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
// Do all the tricky stuff
rv = AfterEditInner(aAction, aDirection);
// Free up selectionState range item
- htmlEditor->mRangeUpdater.DropRangeItem(mRangeItem);
+ HTMLEditorRef().mRangeUpdater.DropRangeItem(mRangeItem);
// Reset the contenteditable count to its previous value
if (mRestoreContentEditableCount) {
- nsHTMLDocument* htmlDoc = htmlEditor->GetHTMLDocument();
+ nsHTMLDocument* htmlDoc = HTMLEditorRef().GetHTMLDocument();
if (NS_WARN_IF(!htmlDoc)) {
return NS_ERROR_FAILURE;
}
if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
htmlDoc->ChangeContentEditableCount(nullptr, -1);
}
mRestoreContentEditableCount = false;
}
}
- NS_ENSURE_SUCCESS(rv, rv);
-
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
return NS_OK;
}
nsresult
HTMLEditRules::AfterEditInner(EditAction aAction,
nsIEditor::EDirection aDirection)
{
ConfirmSelectionInBody();
@@ -573,131 +600,150 @@ HTMLEditRules::AfterEditInner(EditAction
}
nsresult
HTMLEditRules::WillDoAction(Selection* aSelection,
RulesInfo* aInfo,
bool* aCancel,
bool* aHandled)
{
- MOZ_ASSERT(aInfo && aCancel && aHandled);
+ if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ MOZ_ASSERT(aCancel);
+ MOZ_ASSERT(aHandled);
*aCancel = false;
*aHandled = false;
// Deal with actions for which we don't need to check whether the selection is
// editable.
if (aInfo->action == EditAction::outputText ||
aInfo->action == EditAction::undo ||
aInfo->action == EditAction::redo) {
return TextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
}
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
+
// Nothing to do if there's no selection to act on
- if (!aSelection) {
+ if (NS_WARN_IF(!SelectionRef().RangeCount())) {
return NS_OK;
}
- NS_ENSURE_TRUE(aSelection->RangeCount(), NS_OK);
RefPtr<nsRange> range = aSelection->GetRangeAt(0);
nsCOMPtr<nsINode> selStartNode = range->GetStartContainer();
- NS_ENSURE_STATE(mHTMLEditor);
- if (!mHTMLEditor->IsModifiableNode(selStartNode)) {
+ if (!HTMLEditorRef().IsModifiableNode(selStartNode)) {
*aCancel = true;
return NS_OK;
}
nsCOMPtr<nsINode> selEndNode = range->GetEndContainer();
if (selStartNode != selEndNode) {
- NS_ENSURE_STATE(mHTMLEditor);
- if (!mHTMLEditor->IsModifiableNode(selEndNode)) {
+ if (!HTMLEditorRef().IsModifiableNode(selEndNode)) {
*aCancel = true;
return NS_OK;
}
NS_ENSURE_STATE(mHTMLEditor);
- if (!mHTMLEditor->IsModifiableNode(range->GetCommonAncestor())) {
+ if (!HTMLEditorRef().IsModifiableNode(range->GetCommonAncestor())) {
*aCancel = true;
return NS_OK;
}
}
switch (aInfo->action) {
case EditAction::insertText:
case EditAction::insertIMEText:
- UndefineCaretBidiLevel(aSelection);
- return WillInsertText(aInfo->action, aSelection, aCancel, aHandled,
+ UndefineCaretBidiLevel(&SelectionRef());
+ return WillInsertText(aInfo->action, &SelectionRef(), aCancel, aHandled,
aInfo->inString, aInfo->outString,
aInfo->maxLength);
case EditAction::loadHTML:
- return WillLoadHTML(aSelection, aCancel);
+ return WillLoadHTML(&SelectionRef(), aCancel);
case EditAction::insertBreak:
- UndefineCaretBidiLevel(aSelection);
- return WillInsertBreak(*aSelection, aCancel, aHandled);
+ UndefineCaretBidiLevel(&SelectionRef());
+ return WillInsertBreak(SelectionRef(), aCancel, aHandled);
case EditAction::deleteSelection:
- return WillDeleteSelection(aSelection, aInfo->collapsedAction,
+ return WillDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
aInfo->stripWrappers, aCancel, aHandled);
case EditAction::makeList:
- return WillMakeList(aSelection, aInfo->blockType, aInfo->entireList,
+ return WillMakeList(&SelectionRef(), aInfo->blockType, aInfo->entireList,
aInfo->bulletType, aCancel, aHandled);
case EditAction::indent:
- return WillIndent(aSelection, aCancel, aHandled);
+ return WillIndent(&SelectionRef(), aCancel, aHandled);
case EditAction::outdent:
- return WillOutdent(*aSelection, aCancel, aHandled);
+ return WillOutdent(SelectionRef(), aCancel, aHandled);
case EditAction::setAbsolutePosition:
- return WillAbsolutePosition(*aSelection, aCancel, aHandled);
+ return WillAbsolutePosition(SelectionRef(), aCancel, aHandled);
case EditAction::removeAbsolutePosition:
- return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
+ return WillRemoveAbsolutePosition(&SelectionRef(), aCancel, aHandled);
case EditAction::align:
- return WillAlign(*aSelection, *aInfo->alignType, aCancel, aHandled);
+ return WillAlign(SelectionRef(), *aInfo->alignType, aCancel, aHandled);
case EditAction::makeBasicBlock:
- return WillMakeBasicBlock(*aSelection, *aInfo->blockType, aCancel,
+ return WillMakeBasicBlock(SelectionRef(), *aInfo->blockType, aCancel,
aHandled);
case EditAction::removeList:
- return WillRemoveList(aSelection, aInfo->bOrdered, aCancel, aHandled);
+ return WillRemoveList(&SelectionRef(), aInfo->bOrdered,
+ aCancel, aHandled);
case EditAction::makeDefListItem:
- return WillMakeDefListItem(aSelection, aInfo->blockType,
+ return WillMakeDefListItem(&SelectionRef(), aInfo->blockType,
aInfo->entireList, aCancel, aHandled);
case EditAction::insertElement:
- WillInsert(*aSelection, aCancel);
+ WillInsert(SelectionRef(), aCancel);
return NS_OK;
case EditAction::decreaseZIndex:
- return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
+ return WillRelativeChangeZIndex(&SelectionRef(), -1, aCancel, aHandled);
case EditAction::increaseZIndex:
- return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
+ return WillRelativeChangeZIndex(&SelectionRef(), 1, aCancel, aHandled);
default:
- return TextEditRules::WillDoAction(aSelection, aInfo,
+ return TextEditRules::WillDoAction(&SelectionRef(), aInfo,
aCancel, aHandled);
}
}
nsresult
HTMLEditRules::DidDoAction(Selection* aSelection,
RulesInfo* aInfo,
nsresult aResult)
{
+ if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
+
switch (aInfo->action) {
case EditAction::insertBreak:
- return DidInsertBreak(aSelection, aResult);
+ return DidInsertBreak(&SelectionRef(), aResult);
case EditAction::deleteSelection:
- return DidDeleteSelection(aSelection, aInfo->collapsedAction, aResult);
+ return DidDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
+ aResult);
case EditAction::makeBasicBlock:
case EditAction::indent:
case EditAction::outdent:
case EditAction::align:
- return DidMakeBasicBlock(aSelection, aInfo, aResult);
+ return DidMakeBasicBlock(&SelectionRef(), aInfo, aResult);
case EditAction::setAbsolutePosition: {
- nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
- NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rv = DidMakeBasicBlock(&SelectionRef(), aInfo, aResult);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
return DidAbsolutePosition();
}
default:
- // pass through to TextEditRules
return TextEditRules::DidDoAction(aSelection, aInfo, aResult);
}
}
bool
HTMLEditRules::DocumentIsEmpty()
{
return !!mBogusNode;
@@ -711,20 +757,32 @@ HTMLEditRules::GetListState(bool* aMixed
{
NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
*aMixed = false;
*aOL = false;
*aUL = false;
*aDL = false;
bool bNonList = false;
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
TouchContent::no);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// Examine list type for nodes in selection.
for (const auto& curNode : arrayOfNodes) {
if (!curNode->IsElement()) {
bNonList = true;
} else if (curNode->IsHTMLElement(nsGkAtoms::ul)) {
*aUL = true;
} else if (curNode->IsHTMLElement(nsGkAtoms::ol)) {
@@ -762,20 +820,32 @@ HTMLEditRules::GetListItemState(bool* aM
{
NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
*aMixed = false;
*aLI = false;
*aDT = false;
*aDD = false;
bool bNonList = false;
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
TouchContent::no);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// examine list type for nodes in selection
for (const auto& node : arrayOfNodes) {
if (!node->IsElement()) {
bNonList = true;
} else if (node->IsAnyOfHTMLElements(nsGkAtoms::ul,
nsGkAtoms::ol,
nsGkAtoms::li)) {
@@ -804,82 +874,97 @@ HTMLEditRules::GetListItemState(bool* aM
}
nsresult
HTMLEditRules::GetAlignment(bool* aMixed,
nsIHTMLEditor::EAlignment* aAlign)
{
MOZ_ASSERT(aMixed && aAlign);
- NS_ENSURE_STATE(mHTMLEditor);
- RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
// For now, just return first alignment. We'll lie about if it's mixed.
// This is for efficiency given that our current ui doesn't care if it's
// mixed.
// cmanske: NOT TRUE! We would like to pay attention to mixed state in Format
// | Align submenu!
// This routine assumes that alignment is done ONLY via divs
// Default alignment is left
*aMixed = false;
*aAlign = nsIHTMLEditor::eLeft;
- // Get selection
- NS_ENSURE_STATE(htmlEditor->GetSelection());
- OwningNonNull<Selection> selection = *htmlEditor->GetSelection();
-
// Get selection location
- NS_ENSURE_TRUE(htmlEditor->GetRoot(), NS_ERROR_FAILURE);
- OwningNonNull<Element> root = *htmlEditor->GetRoot();
+ if (NS_WARN_IF(!HTMLEditorRef().GetRoot())) {
+ return NS_ERROR_FAILURE;
+ }
+ OwningNonNull<Element> root = *HTMLEditorRef().GetRoot();
int32_t rootOffset = root->GetParentNode() ?
root->GetParentNode()->ComputeIndexOf(root) : -1;
- nsRange* firstRange = selection->GetRangeAt(0);
+ nsRange* firstRange = SelectionRef().GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
return NS_ERROR_FAILURE;
}
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
// Is the selection collapsed?
nsCOMPtr<nsINode> nodeToExamine;
if (selection->IsCollapsed() || atStartOfSelection.GetContainerAsText()) {
// If selection is collapsed, we want to look at the container of selection
// start and its ancestors for divs with alignment on them. If we are in a
// text node, then that is the node of interest.
nodeToExamine = atStartOfSelection.GetContainer();
+ if (NS_WARN_IF(!nodeToExamine)) {
+ return NS_ERROR_FAILURE;
+ }
} else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
atStartOfSelection.Offset() == static_cast<uint32_t>(rootOffset)) {
// If we have selected the body, let's look at the first editable node
- nodeToExamine = htmlEditor->GetNextEditableNode(atStartOfSelection);
+ nodeToExamine = HTMLEditorRef().GetNextEditableNode(atStartOfSelection);
+ if (NS_WARN_IF(!nodeToExamine)) {
+ return NS_ERROR_FAILURE;
+ }
} else {
nsTArray<RefPtr<nsRange>> arrayOfRanges;
- GetPromotedRanges(selection, arrayOfRanges, EditAction::align);
+ GetPromotedRanges(SelectionRef(), arrayOfRanges, EditAction::align);
// Use these ranges to construct a list of nodes to act on.
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
EditAction::align, TouchContent::no);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
nodeToExamine = arrayOfNodes.SafeElementAt(0);
- }
-
- NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
-
- nsCOMPtr<Element> blockParent = htmlEditor->GetBlock(*nodeToExamine);
-
- NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
-
- if (htmlEditor->IsCSSEnabled() &&
+ if (NS_WARN_IF(!nodeToExamine)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ RefPtr<Element> blockParent = HTMLEditorRef().GetBlock(*nodeToExamine);
+ if (NS_WARN_IF(!blockParent)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (HTMLEditorRef().IsCSSEnabled() &&
CSSEditUtils::IsCSSEditableProperty(blockParent, nullptr,
nsGkAtoms::align)) {
// We are in CSS mode and we know how to align this element with CSS
nsAutoString value;
// Let's get the value(s) of text-align or margin-left/margin-right
CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSet(
blockParent, nullptr, nsGkAtoms::align, value, CSSEditUtils::eComputed);
if (value.EqualsLiteral("center") ||
@@ -973,38 +1058,48 @@ MarginPropertyAtomForIndent(nsINode& aNo
return direction.EqualsLiteral("rtl") ?
*nsGkAtoms::marginRight : *nsGkAtoms::marginLeft;
}
nsresult
HTMLEditRules::GetIndentState(bool* aCanIndent,
bool* aCanOutdent)
{
+ if (NS_WARN_IF(!aCanIndent) || NS_WARN_IF(!aCanOutdent)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
// XXX Looks like that this is implementation of
// nsIHTMLEditor::getIndentState() however nobody calls this method
// even with the interface method.
- NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_FAILURE);
*aCanIndent = true;
*aCanOutdent = false;
- // get selection
- NS_ENSURE_STATE(mHTMLEditor && mHTMLEditor->GetSelection());
- OwningNonNull<Selection> selection = *mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
// contruct a list of nodes to act on.
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
- nsresult rv = GetNodesFromSelection(*selection, EditAction::indent,
+ nsresult rv = GetNodesFromSelection(SelectionRef(), EditAction::indent,
arrayOfNodes, TouchContent::no);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// examine nodes in selection for blockquotes or list elements;
// these we can outdent. Note that we return true for canOutdent
// if *any* of the selection is outdentable, rather than all of it.
- NS_ENSURE_STATE(mHTMLEditor);
- bool useCSS = mHTMLEditor->IsCSSEnabled();
+ bool useCSS = HTMLEditorRef().IsCSSEnabled();
for (auto& curNode : Reversed(arrayOfNodes)) {
if (HTMLEditUtils::IsNodeThatCanOutdent(curNode)) {
*aCanOutdent = true;
break;
} else if (useCSS) {
// we are in CSS mode, indentation is done using the margin-left (or margin-right) property
nsAtom& marginProperty = MarginPropertyAtomForIndent(curNode);
nsAutoString value;
@@ -1025,41 +1120,38 @@ HTMLEditRules::GetIndentState(bool* aCan
if (*aCanOutdent) {
return NS_OK;
}
// if we haven't found something to outdent yet, also check the parents
// of selection endpoints. We might have a blockquote or list item
// in the parent hierarchy.
- if (NS_WARN_IF(!mHTMLEditor)) {
- return NS_ERROR_FAILURE;
- }
-
- Element* rootElement = mHTMLEditor->GetRoot();
+ Element* rootElement = HTMLEditorRef().GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_FAILURE;
}
// Test selection start container hierarchy.
- EditorRawDOMPoint selectionStartPoint(EditorBase::GetStartPoint(selection));
+ EditorRawDOMPoint selectionStartPoint(
+ EditorBase::GetStartPoint(&SelectionRef()));
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
return NS_ERROR_FAILURE;
}
for (nsINode* node = selectionStartPoint.GetContainer();
node && node != rootElement;
node = node->GetParentNode()) {
if (HTMLEditUtils::IsNodeThatCanOutdent(node)) {
*aCanOutdent = true;
return NS_OK;
}
}
// Test selection end container hierarchy.
- EditorRawDOMPoint selectionEndPoint(EditorBase::GetEndPoint(selection));
+ EditorRawDOMPoint selectionEndPoint(EditorBase::GetEndPoint(&SelectionRef()));
if (NS_WARN_IF(!selectionEndPoint.IsSet())) {
return NS_ERROR_FAILURE;
}
for (nsINode* node = selectionEndPoint.GetContainer();
node && node != rootElement;
node = node->GetParentNode()) {
if (HTMLEditUtils::IsNodeThatCanOutdent(node)) {
*aCanOutdent = true;
@@ -1069,82 +1161,94 @@ HTMLEditRules::GetIndentState(bool* aCan
return NS_OK;
}
nsresult
HTMLEditRules::GetParagraphState(bool* aMixed,
nsAString& outFormat)
{
+ if (NS_WARN_IF(!aMixed)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
// This routine is *heavily* tied to our ui choices in the paragraph
// style popup. I can't see a way around that.
- NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
*aMixed = true;
outFormat.Truncate(0);
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
bool bMixed = false;
// using "x" as an uninitialized value, since "" is meaningful
nsAutoString formatStr(NS_LITERAL_STRING("x"));
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
nsresult rv = GetParagraphFormatNodes(arrayOfNodes, TouchContent::no);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// post process list. We need to replace any block nodes that are not format
// nodes with their content. This is so we only have to look "up" the hierarchy
// to find format nodes, instead of both up and down.
for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
auto& curNode = arrayOfNodes[i];
nsAutoString format;
// if it is a known format node we have it easy
if (IsBlockNode(curNode) && !HTMLEditUtils::IsFormatNode(curNode)) {
// arrayOfNodes.RemoveObject(curNode);
rv = AppendInnerFormatNodes(arrayOfNodes, curNode);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
}
// we might have an empty node list. if so, find selection parent
// and put that on the list
if (arrayOfNodes.IsEmpty()) {
- if (NS_WARN_IF(!mHTMLEditor)) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- Selection* selection = mHTMLEditor->GetSelection();
- if (NS_WARN_IF(!selection)) {
- return NS_ERROR_FAILURE;
- }
- EditorRawDOMPoint selectionStartPoint(EditorBase::GetStartPoint(selection));
+ EditorRawDOMPoint selectionStartPoint(
+ EditorBase::GetStartPoint(&SelectionRef()));
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
return NS_ERROR_FAILURE;
}
arrayOfNodes.AppendElement(*selectionStartPoint.GetContainer());
}
// remember root node
- NS_ENSURE_STATE(mHTMLEditor);
- nsCOMPtr<Element> rootElem = mHTMLEditor->GetRoot();
- NS_ENSURE_TRUE(rootElem, NS_ERROR_NULL_POINTER);
+ Element* rootElement = HTMLEditorRef().GetRoot();
+ if (NS_WARN_IF(!rootElement)) {
+ return NS_ERROR_FAILURE;
+ }
// loop through the nodes in selection and examine their paragraph format
for (auto& curNode : Reversed(arrayOfNodes)) {
nsAutoString format;
// if it is a known format node we have it easy
if (HTMLEditUtils::IsFormatNode(curNode)) {
GetFormatString(curNode, format);
} else if (IsBlockNode(curNode)) {
// this is a div or some other non-format block.
// we should ignore it. Its children were appended to this list
// by AppendInnerFormatNodes() call above. We will get needed
// info when we examine them instead.
continue;
} else {
nsINode* node = curNode->GetParentNode();
while (node) {
- if (node == rootElem) {
+ if (node == rootElement) {
format.Truncate(0);
break;
} else if (HTMLEditUtils::IsFormatNode(node)) {
GetFormatString(node, format);
break;
}
// else keep looking up
node = node->GetParentNode();
@@ -9391,17 +9495,17 @@ HTMLEditRules::RemoveAlignment(nsINode&
if (child->IsHTMLElement(nsGkAtoms::center)) {
// the current node is a CENTER element
// first remove children's alignment
nsresult rv = RemoveAlignment(*child, aAlignType, true);
NS_ENSURE_SUCCESS(rv, rv);
// we may have to insert BRs in first and last position of element's children
// if the nodes before/after are not blocks and not BRs
- rv = MakeSureElemStartsOrEndsOnCR(*child);
+ rv = MakeSureElemStartsAndEndsOnCR(*child);
NS_ENSURE_SUCCESS(rv, rv);
// now remove the CENTER container
NS_ENSURE_STATE(mHTMLEditor);
rv = mHTMLEditor->RemoveContainerWithTransaction(*child->AsElement());
NS_ENSURE_SUCCESS(rv, rv);
} else if (IsBlockNode(*child) || child->IsHTMLElement(nsGkAtoms::hr)) {
// the current node is a block element
@@ -9497,21 +9601,37 @@ HTMLEditRules::MakeSureElemStartsOrEndsO
if (NS_WARN_IF(!brElement)) {
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
nsresult
-HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode)
-{
+HTMLEditRules::MakeSureElemStartsAndEndsOnCR(nsINode& aNode)
+{
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
nsresult rv = MakeSureElemStartsOrEndsOnCR(aNode, false);
- NS_ENSURE_SUCCESS(rv, rv);
- return MakeSureElemStartsOrEndsOnCR(aNode, true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ rv = MakeSureElemStartsOrEndsOnCR(aNode, true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
}
nsresult
HTMLEditRules::AlignBlock(Element& aElement,
const nsAString& aAlignType,
ContentsOnly aContentsOnly)
{
if (!IsBlockNode(aElement) && !aElement.IsHTMLElement(nsGkAtoms::hr)) {
@@ -9943,32 +10063,34 @@ HTMLEditRules::DocumentModified()
void
HTMLEditRules::DocumentModifiedWorker()
{
if (!mHTMLEditor) {
return;
}
+ Selection* selection = mHTMLEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return;
+ }
+
+ AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
// DeleteNodeWithTransaction() below may cause a flush, which could destroy
// the editor
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
- RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
- RefPtr<Selection> selection = htmlEditor->GetSelection();
- if (!selection) {
- return;
- }
-
// Delete our bogus node, if we have one, since the document might not be
// empty any more.
if (mBogusNode) {
- DebugOnly<nsresult> rv = htmlEditor->DeleteNodeWithTransaction(*mBogusNode);
+ DebugOnly<nsresult> rv =
+ HTMLEditorRef().DeleteNodeWithTransaction(*mBogusNode);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to remove the bogus node");
mBogusNode = nullptr;
}
// Try to recreate the bogus node if needed.
- CreateBogusNodeIfNeeded(selection);
+ CreateBogusNodeIfNeeded(&SelectionRef());
}
} // namespace mozilla
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -97,38 +97,42 @@ public:
virtual bool DocumentIsEmpty() override;
virtual nsresult DocumentModified() override;
nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
nsresult GetIndentState(bool* aCanIndent, bool* aCanOutdent);
nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
- nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode);
+ nsresult MakeSureElemStartsAndEndsOnCR(nsINode& aNode);
void DidCreateNode(Element* aNewElement);
void DidInsertNode(nsIContent& aNode);
void WillDeleteNode(nsINode* aChild);
void DidSplitNode(nsINode* aExistingRightNode,
nsINode* aNewLeftNode);
void WillJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
void DidJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
void DidInsertText(nsINode* aTextNode, int32_t aOffset,
const nsAString& aString);
void DidDeleteText(nsINode* aTextNode, int32_t aOffset, int32_t aLength);
void WillDeleteSelection(Selection* aSelection);
- void DeleteNodeIfCollapsedText(nsINode& aNode);
-
void StartToListenToEditActions() { mListenerEnabled = true; }
void EndListeningToEditActions() { mListenerEnabled = false; }
protected:
virtual ~HTMLEditRules();
+ HTMLEditor& HTMLEditorRef() const
+ {
+ MOZ_ASSERT(mData);
+ return mData->HTMLEditorRef();
+ }
+
enum RulesEndpoint
{
kStart,
kEnd
};
void InitFields();
@@ -139,16 +143,18 @@ protected:
bool* aHandled,
const nsAString* inString,
nsAString* outString,
int32_t aMaxLength);
nsresult WillLoadHTML(Selection* aSelection, bool* aCancel);
nsresult WillInsertBreak(Selection& aSelection, bool* aCancel,
bool* aHandled);
+ void DeleteNodeIfCollapsedText(nsINode& aNode);
+
/**
* InsertBRElement() inserts a <br> element into aInsertToBreak.
*
* @param aSelection The selection.
* @param aInsertToBreak The point where new <br> element will be
* inserted before.
*/
nsresult InsertBRElement(Selection& aSelection,
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -7,17 +7,16 @@
#define mozilla_HTMLEditor_h
#include "mozilla/Attributes.h"
#include "mozilla/ComposerCommandsUpdater.h"
#include "mozilla/CSSEditUtils.h"
#include "mozilla/ManualNAC.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/TextEditor.h"
-#include "mozilla/TextEditRules.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/File.h"
#include "nsAttrName.h"
#include "nsCOMPtr.h"
#include "nsICSSLoaderObserver.h"
#include "nsIDocumentObserver.h"
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -52,16 +52,17 @@ using namespace dom;
};
/********************************************************
* mozilla::TextEditRules
********************************************************/
TextEditRules::TextEditRules()
: mTextEditor(nullptr)
+ , mData(nullptr)
, mPasswordIMEIndex(0)
, mCachedSelectionOffset(0)
, mActionNesting(0)
, mLockRulesSniffing(false)
, mDidExplicitlySetInterline(false)
, mDeleteBidiImmediately(false)
, mIsHTMLEditRules(false)
, mTheAction(EditAction::none)
@@ -122,45 +123,56 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
nsresult
TextEditRules::Init(TextEditor* aTextEditor)
{
- if (!aTextEditor) {
- return NS_ERROR_NULL_POINTER;
+ if (NS_WARN_IF(!aTextEditor)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ Selection* selection = aTextEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
}
InitFields();
// We hold a non-refcounted reference back to our editor.
mTextEditor = aTextEditor;
- RefPtr<Selection> selection = mTextEditor->GetSelection();
- NS_WARNING_ASSERTION(selection, "editor cannot get selection");
+ AutoSafeEditorData setData(*this, *mTextEditor, *selection);
- // Put in a magic br if needed. This method handles null selection,
+ // Put in a magic <br> if needed. This method handles null selection,
// which should never happen anyway
- nsresult rv = CreateBogusNodeIfNeeded(selection);
- NS_ENSURE_SUCCESS(rv, rv);
+ nsresult rv = CreateBogusNodeIfNeeded(&SelectionRef());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// If the selection hasn't been set up yet, set it up collapsed to the end of
// our editable content.
- if (!selection->RangeCount()) {
- rv = mTextEditor->CollapseSelectionToEnd(selection);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (!SelectionRef().RangeCount()) {
+ rv = TextEditorRef().CollapseSelectionToEnd(&SelectionRef());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
if (IsPlaintextEditor()) {
// ensure trailing br node
rv = CreateTrailingBRIfNeeded();
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
+ // XXX We should use AddBoolVarCache and use "current" value at initializing.
mDeleteBidiImmediately =
Preferences::GetBool("bidi.edit.delete_immediately", false);
return NS_OK;
}
nsresult
TextEditRules::SetInitialValue(const nsAString& aValue)
@@ -194,30 +206,31 @@ TextEditRules::BeforeEdit(EditAction aAc
if (!mActionNesting) {
// let rules remember the top level action
mTheAction = aAction;
}
mActionNesting++;
// get the selection and cache the position before editing
if (NS_WARN_IF(!mTextEditor)) {
- return NS_ERROR_FAILURE;
+ return NS_ERROR_NOT_AVAILABLE;
}
- RefPtr<TextEditor> textEditor = mTextEditor;
- RefPtr<Selection> selection = textEditor->GetSelection();
- NS_ENSURE_STATE(selection);
if (aAction == EditAction::setText) {
// setText replaces all text, so mCachedSelectionNode might be invalid on
// AfterEdit.
// Since this will be used as start position of spellchecker, we should
// use root instead.
- mCachedSelectionNode = textEditor->GetRoot();
+ mCachedSelectionNode = mTextEditor->GetRoot();
mCachedSelectionOffset = 0;
} else {
+ Selection* selection = mTextEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
mCachedSelectionNode = selection->GetAnchorNode();
mCachedSelectionOffset = selection->AnchorOffset();
}
return NS_OK;
}
nsresult
@@ -227,134 +240,162 @@ TextEditRules::AfterEdit(EditAction aAct
if (mLockRulesSniffing) {
return NS_OK;
}
AutoLockRulesSniffing lockIt(this);
MOZ_ASSERT(mActionNesting>0, "bad action nesting!");
if (!--mActionNesting) {
- NS_ENSURE_STATE(mTextEditor);
- RefPtr<Selection> selection = mTextEditor->GetSelection();
- NS_ENSURE_STATE(selection);
+ if (NS_WARN_IF(!mTextEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ Selection* selection = mTextEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
- NS_ENSURE_STATE(mTextEditor);
+ AutoSafeEditorData setData(*this, *mTextEditor, *selection);
+
nsresult rv =
- mTextEditor->HandleInlineSpellCheck(aAction, *selection,
- mCachedSelectionNode,
- mCachedSelectionOffset,
- nullptr, 0, nullptr, 0);
- NS_ENSURE_SUCCESS(rv, rv);
+ TextEditorRef().HandleInlineSpellCheck(aAction, *selection,
+ mCachedSelectionNode,
+ mCachedSelectionOffset,
+ nullptr, 0, nullptr, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// no longer uses mCachedSelectionNode, so release it.
mCachedSelectionNode = nullptr;
// if only trailing <br> remaining remove it
rv = RemoveRedundantTrailingBR();
if (NS_FAILED(rv)) {
return rv;
}
// detect empty doc
- rv = CreateBogusNodeIfNeeded(selection);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = CreateBogusNodeIfNeeded(&SelectionRef());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// ensure trailing br node
rv = CreateTrailingBRIfNeeded();
- NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
// collapse the selection to the trailing BR if it's at the end of our text node
- CollapseSelectionToTrailingBRIfNeeded(selection);
+ CollapseSelectionToTrailingBRIfNeeded(&SelectionRef());
}
return NS_OK;
}
nsresult
TextEditRules::WillDoAction(Selection* aSelection,
RulesInfo* aInfo,
bool* aCancel,
bool* aHandled)
{
- // null selection is legal
- MOZ_ASSERT(aInfo && aCancel && aHandled);
+ if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!mTextEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ MOZ_ASSERT(aCancel);
+ MOZ_ASSERT(aHandled);
*aCancel = false;
*aHandled = false;
+ AutoSafeEditorData setData(*this, *mTextEditor, *aSelection);
+
// my kingdom for dynamic cast
switch (aInfo->action) {
case EditAction::insertBreak:
- UndefineCaretBidiLevel(aSelection);
- return WillInsertBreak(aSelection, aCancel, aHandled, aInfo->maxLength);
+ UndefineCaretBidiLevel(&SelectionRef());
+ return WillInsertBreak(&SelectionRef(), aCancel, aHandled,
+ aInfo->maxLength);
case EditAction::insertText:
case EditAction::insertIMEText:
- UndefineCaretBidiLevel(aSelection);
- return WillInsertText(aInfo->action, aSelection, aCancel, aHandled,
+ UndefineCaretBidiLevel(&SelectionRef());
+ return WillInsertText(aInfo->action, &SelectionRef(), aCancel, aHandled,
aInfo->inString, aInfo->outString,
aInfo->maxLength);
case EditAction::setText:
- UndefineCaretBidiLevel(aSelection);
- return WillSetText(*aSelection, aCancel, aHandled, aInfo->inString,
+ UndefineCaretBidiLevel(&SelectionRef());
+ return WillSetText(SelectionRef(), aCancel, aHandled, aInfo->inString,
aInfo->maxLength);
case EditAction::deleteSelection:
- return WillDeleteSelection(aSelection, aInfo->collapsedAction,
+ return WillDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
aCancel, aHandled);
case EditAction::undo:
- return WillUndo(aSelection, aCancel, aHandled);
+ return WillUndo(&SelectionRef(), aCancel, aHandled);
case EditAction::redo:
- return WillRedo(aSelection, aCancel, aHandled);
+ return WillRedo(&SelectionRef(), aCancel, aHandled);
case EditAction::setTextProperty:
- return WillSetTextProperty(aSelection, aCancel, aHandled);
+ return WillSetTextProperty(&SelectionRef(), aCancel, aHandled);
case EditAction::removeTextProperty:
- return WillRemoveTextProperty(aSelection, aCancel, aHandled);
+ return WillRemoveTextProperty(&SelectionRef(), aCancel, aHandled);
case EditAction::outputText:
- return WillOutputText(aSelection, aInfo->outputFormat, aInfo->outString,
- aInfo->flags, aCancel, aHandled);
+ return WillOutputText(&SelectionRef(), aInfo->outputFormat,
+ aInfo->outString, aInfo->flags, aCancel, aHandled);
case EditAction::insertElement:
// i had thought this would be html rules only. but we put pre elements
// into plaintext mail when doing quoting for reply! doh!
- WillInsert(*aSelection, aCancel);
+ WillInsert(SelectionRef(), aCancel);
return NS_OK;
default:
return NS_ERROR_FAILURE;
}
}
nsresult
TextEditRules::DidDoAction(Selection* aSelection,
RulesInfo* aInfo,
nsresult aResult)
{
- NS_ENSURE_STATE(mTextEditor);
+ if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ if (NS_WARN_IF(!mTextEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ AutoSafeEditorData setData(*this, *mTextEditor, *aSelection);
+
// don't let any txns in here move the selection around behind our back.
// Note that this won't prevent explicit selection setting from working.
- AutoTransactionsConserveSelection dontChangeMySelection(mTextEditor);
-
- NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
+ AutoTransactionsConserveSelection dontChangeMySelection(&TextEditorRef());
switch (aInfo->action) {
case EditAction::insertBreak:
- return DidInsertBreak(aSelection, aResult);
+ return DidInsertBreak(&SelectionRef(), aResult);
case EditAction::insertText:
case EditAction::insertIMEText:
- return DidInsertText(aSelection, aResult);
+ return DidInsertText(&SelectionRef(), aResult);
case EditAction::setText:
- return DidSetText(*aSelection, aResult);
+ return DidSetText(SelectionRef(), aResult);
case EditAction::deleteSelection:
- return DidDeleteSelection(aSelection, aInfo->collapsedAction, aResult);
+ return DidDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
+ aResult);
case EditAction::undo:
- return DidUndo(aSelection, aResult);
+ return DidUndo(&SelectionRef(), aResult);
case EditAction::redo:
- return DidRedo(aSelection, aResult);
+ return DidRedo(&SelectionRef(), aResult);
case EditAction::setTextProperty:
- return DidSetTextProperty(aSelection, aResult);
+ return DidSetTextProperty(&SelectionRef(), aResult);
case EditAction::removeTextProperty:
- return DidRemoveTextProperty(aSelection, aResult);
+ return DidRemoveTextProperty(&SelectionRef(), aResult);
case EditAction::outputText:
- return DidOutputText(aSelection, aResult);
+ return DidOutputText(&SelectionRef(), aResult);
default:
// Don't fail on transactions we don't handle here!
return NS_OK;
}
}
/**
* Return false if the editor has non-empty text nodes or non-text
@@ -1597,16 +1638,27 @@ TextEditRules::RemoveIMETextFromPWBuf(ui
mPasswordIMEText.Assign(*aIMEString);
}
NS_IMETHODIMP
TextEditRules::Notify(nsITimer* aTimer)
{
MOZ_ASSERT(mTimer);
+ if (NS_WARN_IF(!mTextEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ Selection* selection = mTextEditor->GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AutoSafeEditorData setData(*this, *mTextEditor, *selection);
+
// Check whether our text editor's password flag was changed before this
// "hide password character" timer actually fires.
nsresult rv = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
ASSERT_PASSWORD_LENGTHS_EQUAL();
mLastLength = 0;
return rv;
}
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -3,31 +3,33 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_TextEditRules_h
#define mozilla_TextEditRules_h
#include "mozilla/EditAction.h"
#include "mozilla/EditorDOMPoint.h"
+#include "mozilla/HTMLEditor.h" // for nsIEditor::AsHTMLEditor()
+#include "mozilla/TextEditor.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIEditor.h"
#include "nsINamed.h"
#include "nsISupportsImpl.h"
#include "nsITimer.h"
#include "nsString.h"
#include "nscore.h"
namespace mozilla {
class AutoLockRulesSniffing;
+class HTMLEditor;
class HTMLEditRules;
class RulesInfo;
-class TextEditor;
namespace dom {
class Selection;
} // namespace dom
/**
* Object that encapsulates HTML text-specific editing rules.
*
* To be a good citizen, edit rules must live by these restrictions:
@@ -276,20 +278,86 @@ protected:
bool IsSingleLineEditor() const;
bool IsPlaintextEditor() const;
bool IsReadonly() const;
bool IsDisabled() const;
bool IsMailEditor() const;
bool DontEchoPassword() const;
private:
- // Note that we do not refcount the editor.
- TextEditor* mTextEditor;
+ TextEditor* MOZ_NON_OWNING_REF mTextEditor;
protected:
+ /**
+ * AutoSafeEditorData grabs editor instance and related instances during
+ * handling an edit action. When this is created, its pointer is set to
+ * the mSafeData, and this guarantees the lifetime of grabbing objects
+ * until it's destroyed.
+ */
+ class MOZ_STACK_CLASS AutoSafeEditorData
+ {
+ public:
+ AutoSafeEditorData(TextEditRules& aTextEditRules,
+ TextEditor& aTextEditor,
+ Selection& aSelection)
+ : mTextEditRules(aTextEditRules)
+ , mHTMLEditor(nullptr)
+ {
+ // mTextEditRules may have AutoSafeEditorData instance since in some
+ // cases. E.g., while public methods of *EditRules are called, it
+ // calls attaching editor's method, then, editor will call public
+ // methods of *EditRules again.
+ if (mTextEditRules.mData) {
+ return;
+ }
+ mTextEditor = &aTextEditor;
+ mHTMLEditor = aTextEditor.AsHTMLEditor();
+ mSelection = &aSelection;
+ mTextEditRules.mData = this;
+ }
+
+ ~AutoSafeEditorData()
+ {
+ if (mTextEditRules.mData != this) {
+ return;
+ }
+ mTextEditRules.mData = nullptr;
+ }
+
+ TextEditor& TextEditorRef() const { return *mTextEditor; }
+ HTMLEditor& HTMLEditorRef() const
+ {
+ MOZ_ASSERT(mHTMLEditor);
+ return *mHTMLEditor;
+ }
+ Selection& SelectionRef() const { return *mSelection; }
+
+ private:
+ // This class should be created by public methods TextEditRules and
+ // HTMLEditRules and in the stack. So, the lifetime of this should
+ // be guaranteed the callers of the public methods.
+ TextEditRules& MOZ_NON_OWNING_REF mTextEditRules;
+ RefPtr<TextEditor> mTextEditor;
+ // Shortcut for HTMLEditorRef(). So, not necessary to use RefPtr.
+ HTMLEditor* MOZ_NON_OWNING_REF mHTMLEditor;
+ RefPtr<Selection> mSelection;
+ };
+ AutoSafeEditorData* mData;
+
+ TextEditor& TextEditorRef() const
+ {
+ MOZ_ASSERT(mData);
+ return mData->TextEditorRef();
+ }
+ Selection& SelectionRef() const
+ {
+ MOZ_ASSERT(mData);
+ return mData->SelectionRef();
+ }
+
// A buffer we use to store the real value of password editors.
nsString mPasswordText;
// A buffer we use to track the IME composition string.
nsString mPasswordIMEText;
uint32_t mPasswordIMEIndex;
// Magic node acts as placeholder in empty doc.
nsCOMPtr<nsIContent> mBogusNode;
// Cached selected node.
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1738,18 +1738,22 @@ TextEditor::OutputToString(const nsAStri
nsString resultString;
RulesInfo ruleInfo(EditAction::outputText);
ruleInfo.outString = &resultString;
ruleInfo.flags = aFlags;
// XXX Struct should store a nsAReadable*
nsAutoString str(aFormatType);
ruleInfo.outputFormat = &str;
+ Selection* selection = GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
bool cancel, handled;
- nsresult rv = rules->WillDoAction(nullptr, &ruleInfo, &cancel, &handled);
+ nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || NS_FAILED(rv)) {
return rv;
}
if (handled) {
// This case will get triggered by password fields.
aOutputString.Assign(*(ruleInfo.outString));
return rv;
}