Bug 1460509 - part 82: Refine comments of TextEditRules.h and HTMLEditRules.h r?m_kato
This patch adds new rules of TextEditRules and HTMLEditRules about
NS_ERROR_EDITOR_DESTROYED.
Additionally, this patch moves some method explanation in each .cpp file to .h
file.
MozReview-Commit-ID: JqZFrWyrND8
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -3385,26 +3385,16 @@ HTMLEditRules::InsertBRIfNeeded()
return NS_ERROR_FAILURE;
}
return NS_OK;
}
}
return NS_OK;
}
-/**
- * GetGoodSelPointForNode() finds where at a node you would want to set the
- * selection if you were trying to have a caret next to it. Always returns a
- * valid value (unless mHTMLEditor has gone away).
- *
- * @param aNode The node
- * @param aAction Which edge to find:
- * eNext/eNextWord/eToEndOfLine indicates beginning,
- * ePrevious/PreviousWord/eToBeginningOfLine ending.
- */
EditorDOMPoint
HTMLEditRules::GetGoodSelPointForNode(nsINode& aNode,
nsIEditor::EDirection aAction)
{
MOZ_ASSERT(IsEditorDataAvailable());
MOZ_ASSERT(aAction == nsIEditor::eNext ||
aAction == nsIEditor::eNextWord ||
aAction == nsIEditor::ePrevious ||
@@ -6785,23 +6775,16 @@ HTMLEditRules::CheckForInvisibleBR(Eleme
WSRunObject wsTester(&HTMLEditorRef(), testNode, testOffset);
if (WSType::br == wsTester.mStartReason) {
return wsTester.mStartReasonNode->AsElement();
}
return nullptr;
}
-/**
- * aLists and aTables allow the caller to specify what kind of content to
- * "look inside". If aTables is Tables::yes, look inside any table content,
- * and insert the inner content into the supplied nsTArray at offset
- * aIndex. Similarly with aLists and list content. aIndex is updated to
- * point past inserted elements.
- */
void
HTMLEditRules::GetInnerContent(
nsINode& aNode,
nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
int32_t* aIndex,
Lists aLists,
Tables aTables)
{
@@ -7145,20 +7128,16 @@ HTMLEditRules::NormalizeSelection()
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to extend selection");
return NS_OK;
}
-/**
- * GetPromotedPoint() figures out where a start or end point for a block
- * operation really is.
- */
EditorDOMPoint
HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere,
nsINode& aNode,
int32_t aOffset,
EditAction actionID)
{
MOZ_ASSERT(IsEditorDataAvailable());
@@ -7343,20 +7322,16 @@ HTMLEditRules::GetPromotedPoint(RulesEnd
if (NS_WARN_IF(!point.AdvanceOffset())) {
break;
}
nearNode = HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
}
return point;
}
-/**
- * GetPromotedRanges() runs all the selection range endpoint through
- * GetPromotedPoint().
- */
void
HTMLEditRules::GetPromotedRanges(nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
EditAction inOperationType)
{
MOZ_ASSERT(IsEditorDataAvailable());
uint32_t rangeCount = SelectionRef().RangeCount();
for (uint32_t i = 0; i < rangeCount; i++) {
@@ -7371,20 +7346,16 @@ HTMLEditRules::GetPromotedRanges(nsTArra
// blocks that we will affect. This call alters opRange.
PromoteRange(*opRange, inOperationType);
// Stuff new opRange into array
outArrayOfRanges.AppendElement(opRange);
}
}
-/**
- * PromoteRange() expands a range to include any parents for which all editable
- * children are already in range.
- */
void
HTMLEditRules::PromoteRange(nsRange& aRange,
EditAction aOperationType)
{
MOZ_ASSERT(IsEditorDataAvailable());
MOZ_ASSERT(!aRange.IsInSelection());
if (!aRange.IsPositioned()) {
@@ -7473,23 +7444,16 @@ public:
{
return !mArray.Contains(aNode);
}
private:
nsTArray<OwningNonNull<nsINode>>& mArray;
};
-/**
- * GetNodesForOperation() runs through the ranges in the array and construct a
- * new array of nodes to be acted on.
- *
- * XXX This name stats with "Get" but actually this modifies the DOM tree with
- * transaction. We should rename this to making clearer what this does.
- */
nsresult
HTMLEditRules::GetNodesForOperation(
nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
EditAction aOperationType,
TouchContent aTouchContent)
{
MOZ_ASSERT(IsEditorDataAvailable());
@@ -7997,20 +7961,16 @@ HTMLEditRules::GetHighestInlineParent(ns
for (nsIContent* parent = content->GetParent();
parent && parent != host && IsInlineNode(*parent);
parent = parent->GetParent()) {
content = parent;
}
return content;
}
-/**
- * GetNodesFromPoint() constructs a list of nodes from a point that will be
- * operated on.
- */
nsresult
HTMLEditRules::GetNodesFromPoint(
const EditorDOMPoint& aPoint,
EditAction aOperation,
nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
TouchContent aTouchContent)
{
if (NS_WARN_IF(!aPoint.IsSet())) {
@@ -8038,20 +7998,16 @@ HTMLEditRules::GetNodesFromPoint(
aTouchContent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
-/**
- * GetNodesFromSelection() constructs a list of nodes from the selection that
- * will be operated on.
- */
nsresult
HTMLEditRules::GetNodesFromSelection(
EditAction aOperation,
nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
TouchContent aTouchContent)
{
MOZ_ASSERT(IsEditorDataAvailable());
@@ -8064,20 +8020,16 @@ HTMLEditRules::GetNodesFromSelection(
aOperation, aTouchContent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
-/**
- * MakeTransitionList() detects all the transitions in the array, where a
- * transition means that adjacent nodes in the array don't have the same parent.
- */
void
HTMLEditRules::MakeTransitionList(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
nsTArray<bool>& aTransitionArray)
{
nsCOMPtr<nsINode> prevParent;
aTransitionArray.EnsureLengthAtLeast(aNodeArray.Length());
for (uint32_t i = 0; i < aNodeArray.Length(); i++) {
@@ -8087,21 +8039,16 @@ HTMLEditRules::MakeTransitionList(nsTArr
} else {
// Same parents: these nodes grew up together
aTransitionArray[i] = false;
}
prevParent = aNodeArray[i]->GetParentNode();
}
}
-/**
- * If aNode is the descendant of a listitem, return that li. But table element
- * boundaries are stoppers on the search. Also stops on the active editor host
- * (contenteditable). Also test if aNode is an li itself.
- */
Element*
HTMLEditRules::IsInListItem(nsINode* aNode)
{
MOZ_ASSERT(IsEditorDataAvailable());
NS_ENSURE_TRUE(aNode, nullptr);
if (HTMLEditUtils::IsListItem(aNode)) {
return aNode->AsElement();
@@ -10192,19 +10139,16 @@ HTMLEditRules::SelectionEndpointInNode(n
*aResult = true;
return NS_OK;
}
}
}
return NS_OK;
}
-/**
- * IsEmptyInline: Return true if aNode is an empty inline container
- */
bool
HTMLEditRules::IsEmptyInline(nsINode& aNode)
{
MOZ_ASSERT(IsEditorDataAvailable());
if (IsInlineNode(aNode) && HTMLEditorRef().IsContainer(&aNode)) {
bool isEmpty = true;
HTMLEditorRef().IsEmptyNode(&aNode, &isEmpty);
@@ -10762,20 +10706,16 @@ HTMLEditRules::WillDeleteSelection(Selec
}
nsresult rv = mUtilRange->SetStartAndEnd(startPoint, endPoint);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
UpdateDocChangeRange(mUtilRange);
}
-// Let's remove all alignment hints in the children of aNode; it can
-// be an ALIGN attribute (in case we just remove it) or a CENTER
-// element (here we have to remove the container and keep its
-// children). We break on tables and don't look at their children.
nsresult
HTMLEditRules::RemoveAlignment(nsINode& aNode,
const nsAString& aAlignType,
bool aDescendantsOnly)
{
MOZ_ASSERT(IsEditorDataAvailable());
if (EditorBase::IsTextNode(&aNode) || HTMLEditUtils::IsTable(&aNode)) {
@@ -10786,16 +10726,21 @@ HTMLEditRules::RemoveAlignment(nsINode&
if (aDescendantsOnly) {
child = aNode.GetFirstChild();
} else {
child = &aNode;
}
bool useCSS = HTMLEditorRef().IsCSSEnabled();
+
+ // Let's remove all alignment hints in the children of aNode; it can
+ // be an ALIGN attribute (in case we just remove it) or a CENTER
+ // element (here we have to remove the container and keep its
+ // children). We break on tables and don't look at their children.
while (child) {
if (aDescendantsOnly) {
// get the next sibling right now because we could have to remove child
tmp = child->GetNextSibling();
} else {
tmp = nullptr;
}
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -65,16 +65,30 @@ struct StyleCache final : public PropIte
}
~StyleCache()
{
MOZ_COUNT_DTOR(StyleCache);
}
};
+/**
+ * Same as TextEditRules, any methods which may modify the DOM tree or
+ * Selection should be marked as MOZ_MUST_USE and return nsresult directly
+ * or with simple class like EditActionResult. And every caller of them
+ * has to check whether the result is NS_ERROR_EDITOR_DESTROYED and if it is,
+ * its callers should stop handling edit action since after mutation event
+ * listener or selectionchange event listener disables the editor, we should
+ * not modify the DOM tree nor Selection anymore. And also when methods of
+ * this class call methods of other classes like HTMLEditor and WSRunObject,
+ * they should check whether CanHandleEditAtion() returns false immediately
+ * after the calls. If it returns false, they should return
+ * NS_ERROR_EDITOR_DESTROYED.
+ */
+
#define SIZE_STYLE_TABLE 19
class HTMLEditRules : public TextEditRules
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditRules, TextEditRules)
@@ -279,16 +293,26 @@ protected:
* @param aNode Reference to a block parent.
* @param aInsertMozBR true if this should insert a moz-<br> element.
* Otherwise, i.e., this should insert a normal <br>
* element, false.
*/
MOZ_MUST_USE nsresult
InsertBRIfNeededInternal(nsINode& aNode, bool aInsertMozBR);
+ /**
+ * GetGoodSelPointForNode() finds where at a node you would want to set the
+ * selection if you were trying to have a caret next to it. Always returns a
+ * valid value (unless mHTMLEditor has gone away).
+ *
+ * @param aNode The node
+ * @param aAction Which edge to find:
+ * eNext/eNextWord/eToEndOfLine indicates beginning,
+ * ePrevious/PreviousWord/eToBeginningOfLine ending.
+ */
EditorDOMPoint GetGoodSelPointForNode(nsINode& aNode,
nsIEditor::EDirection aAction);
/**
* TryToJoinBlocksWithTransaction() tries to join two block elements. The
* right element is always joined to the left element. If the elements are
* the same type and not nested within each other,
* JoinEditableNodesWithTransaction() is called (example, joining two list
@@ -573,23 +597,38 @@ protected:
* should be aligned to.
*/
MOZ_MUST_USE nsresult
AlignContentsAtSelection(const nsAString& aAlignType);
nsresult AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
nsINode* aNode);
nsresult GetFormatString(nsINode* aNode, nsAString &outFormat);
+
+ /**
+ * aLists and aTables allow the caller to specify what kind of content to
+ * "look inside". If aTables is Tables::yes, look inside any table content,
+ * and insert the inner content into the supplied nsTArray at offset
+ * aIndex. Similarly with aLists and list content. aIndex is updated to
+ * point past inserted elements.
+ */
enum class Lists { no, yes };
enum class Tables { no, yes };
void GetInnerContent(nsINode& aNode,
nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
int32_t* aIndex, Lists aLists = Lists::yes,
Tables aTables = Tables::yes);
+
+ /**
+ * If aNode is the descendant of a listitem, return that li. But table
+ * element boundaries are stoppers on the search. Also stops on the active
+ * editor host (contenteditable). Also test if aNode is an li itself.
+ */
Element* IsInListItem(nsINode* aNode);
+
nsAtom& DefaultParagraphSeparator();
/**
* ReturnInHeader() handles insertParagraph command (i.e., handling Enter
* key press) in a heading element. This splits aHeader element at
* aOffset in aNode. Then, if right heading element is empty, it'll be
* removed and new paragraph is created (its type is decided with default
* paragraph separator).
@@ -840,37 +879,71 @@ protected:
/**
* NormalizeSelection() adjust Selection if it's not collapsed and there is
* only one range. If range start and/or end point is <br> node or something
* non-editable point, they should be moved to nearest text node or something
* where the other methods easier to handle edit action.
*/
MOZ_MUST_USE nsresult NormalizeSelection();
+ /**
+ * GetPromotedPoint() figures out where a start or end point for a block
+ * operation really is.
+ */
EditorDOMPoint GetPromotedPoint(RulesEndpoint aWhere, nsINode& aNode,
int32_t aOffset, EditAction actionID);
+
+ /**
+ * GetPromotedRanges() runs all the selection range endpoint through
+ * GetPromotedPoint().
+ */
void GetPromotedRanges(nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
EditAction inOperationType);
+
+ /**
+ * PromoteRange() expands a range to include any parents for which all
+ * editable children are already in range.
+ */
void PromoteRange(nsRange& aRange, EditAction inOperationType);
+
+ /**
+ * GetNodesForOperation() runs through the ranges in the array and construct a
+ * new array of nodes to be acted on.
+ *
+ * XXX This name stats with "Get" but actually this modifies the DOM tree with
+ * transaction. We should rename this to making clearer what this does.
+ */
enum class TouchContent { no, yes };
MOZ_MUST_USE nsresult
GetNodesForOperation(nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
EditAction aOperationType, TouchContent aTouchContent);
+
void GetChildNodesForOperation(
nsINode& aNode,
nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
+
+ /**
+ * GetNodesFromPoint() constructs a list of nodes from a point that will be
+ * operated on.
+ */
MOZ_MUST_USE nsresult
GetNodesFromPoint(const EditorDOMPoint& aPoint, EditAction aOperation,
nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
TouchContent aTouchContent);
+
+ /**
+ * GetNodesFromSelection() constructs a list of nodes from the selection that
+ * will be operated on.
+ */
MOZ_MUST_USE nsresult
GetNodesFromSelection(EditAction aOperation,
nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
TouchContent aTouchContent);
+
enum class EntireList { no, yes };
MOZ_MUST_USE nsresult
GetListActionNodes(nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
EntireList aEntireList, TouchContent aTouchContent);
void GetDefinitionListItemTypes(Element* aElement, bool* aDT, bool* aDD);
nsresult
GetParagraphFormatNodes(nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
void LookInsideDivBQandList(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
@@ -903,16 +976,22 @@ protected:
nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes);
/**
* GetHiestInlineParent() returns the highest inline node parent between
* aNode and the editing host. Even if the editing host is an inline
* element, this method never returns the editing host as the result.
*/
nsIContent* GetHighestInlineParent(nsINode& aNode);
+
+ /**
+ * MakeTransitionList() detects all the transitions in the array, where a
+ * transition means that adjacent nodes in the array don't have the same
+ * parent.
+ */
void MakeTransitionList(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
nsTArray<bool>& aTransitionArray);
/**
* RemoveBlockStyle() removes all format blocks, table related element,
* etc in aNodeArray.
* If aNodeArray has a format node, it will be removed and its contents
* will be moved to where it was.
@@ -1127,17 +1206,21 @@ protected:
* and only one Selection range.
* XXX This method is not necessary because even if selection is outside the
* <body> element, elements outside the <body> element should be
* editable, e.g., any element can be inserted siblings as <body> element
* and other browsers allow to edit such elements.
*/
MOZ_MUST_USE nsresult ConfirmSelectionInBody();
+ /**
+ * IsEmptyInline: Return true if aNode is an empty inline container
+ */
bool IsEmptyInline(nsINode& aNode);
+
bool ListIsEmptyLine(nsTArray<OwningNonNull<nsINode>>& arrayOfNodes);
/**
* RemoveAlignment() removes align attributes, text-align properties and
* <center> elements in aNode.
*
* @param aNode Alignment information of the node and/or its
* descendants will be removed.
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -388,21 +388,16 @@ TextEditRules::DidDoAction(Selection* aS
case EditAction::redo:
return DidRedo(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
- * nodes. Otherwise, i.e., there is no meaningful content,
- * return true.
- */
bool
TextEditRules::DocumentIsEmpty()
{
bool retVal = false;
if (!mTextEditor || NS_FAILED(mTextEditor->DocumentIsEmpty(&retVal))) {
retVal = true;
}
@@ -1191,37 +1186,36 @@ TextEditRules::WillUndo(bool* aCancel,
}
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
// initialize out param
*aCancel = false;
*aHandled = false;
return NS_OK;
}
-/**
- * The idea here is to see if the magic empty node has suddenly reappeared as
- * the result of the undo. If it has, set our state so we remember it.
- * There is a tradeoff between doing here and at redo, or doing it everywhere
- * else that might care. Since undo and redo are relatively rare, it makes
- * sense to take the (small) performance hit here.
- */
nsresult
TextEditRules::DidUndo(nsresult aResult)
{
MOZ_ASSERT(IsEditorDataAvailable());
// If aResult is an error, we return it.
if (NS_WARN_IF(NS_FAILED(aResult))) {
return aResult;
}
Element* rootElement = TextEditorRef().GetRoot();
if (NS_WARN_IF(!rootElement)) {
return NS_ERROR_FAILURE;
}
+
+ // The idea here is to see if the magic empty node has suddenly reappeared as
+ // the result of the undo. If it has, set our state so we remember it.
+ // There is a tradeoff between doing here and at redo, or doing it everywhere
+ // else that might care. Since undo and redo are relatively rare, it makes
+ // sense to take the (small) performance hit here.
nsIContent* node = TextEditorRef().GetLeftmostChild(rootElement);
if (node && TextEditorRef().IsMozEditorBogusNode(node)) {
mBogusNode = node;
} else {
mBogusNode = nullptr;
}
return aResult;
}
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -36,16 +36,28 @@ class Selection;
* To be a good citizen, edit rules must live by these restrictions:
* 1. All data manipulation is through the editor.
* Content nodes in the document tree must <B>not</B> be manipulated
* directly. Content nodes in document fragments that are not part of the
* document itself may be manipulated at will. Operations on document
* fragments must <B>not</B> go through the editor.
* 2. Selection must not be explicitly set by the rule method.
* Any manipulation of Selection must be done by the editor.
+ * 3. Stop handling edit action if method returns NS_ERROR_EDITOR_DESTROYED
+ * since if mutation event lister or selectionchange event listener disables
+ * the editor, we should not modify the DOM tree anymore.
+ * 4. Any method callers have to check nsresult return value (both directly or
+ * with simple class like EditActionResult) whether the value is
+ * NS_ERROR_EDITOR_DESTROYED at least.
+ * 5. Callers of methods of other classes such as TextEditor, have to check
+ * CanHandleEditAction() before checking its result and if the result is
+ * false, the method have to return NS_ERROR_EDITOR_DESTROYED. In other
+ * words, any methods which may change Selection or the DOM tree have to
+ * return nsresult directly or with simple class like EditActionResult.
+ * And such methods should be marked as MOZ_MUST_USE.
*/
class TextEditRules : public nsITimerCallback
, public nsINamed
{
public:
typedef dom::Element Element;
typedef dom::Selection Selection;
typedef dom::Text Text;
@@ -70,17 +82,24 @@ public:
nsIEditor::EDirection aDirection);
virtual nsresult WillDoAction(Selection* aSelection,
RulesInfo* aInfo,
bool* aCancel,
bool* aHandled);
virtual nsresult DidDoAction(Selection* aSelection,
RulesInfo* aInfo,
nsresult aResult);
+
+ /**
+ * Return false if the editor has non-empty text nodes or non-text
+ * nodes. Otherwise, i.e., there is no meaningful content,
+ * return true.
+ */
virtual bool DocumentIsEmpty();
+
virtual nsresult DocumentModified();
protected:
virtual ~TextEditRules();
public:
void ResetIMETextPWBuf();