Bug 1627175 - part 2: Move `EditorBase::IsModifiableNode()`, `EditorBase::IsEditable()`, `EditorBase::IsTextElementOrText()` and `EditorBase::IsPaddingBRElementForEmptyEditor()` to `EditorUtils` r=m_kato

Due to the include hell, `EditorBase.h` cannot include `EditorUtils.h`.
Therefore we need these 3 methods once.  Additionally, `IsModifiableNode()`
is really odd method and looks like that it's used for the following 2 purposes:
1. Simply can be editable.
2. Can be removed from parent.

For the former case, we should sort out it with
`EditorUtils::IsEditableContent()`, but for now, this patch moves it to
`HTMLEditUtils::IsSimplyEditable()`.  On the other hand, for the latter case,
we obviously has a bug.  Therefore, this patch creates
`HTMLEditUtils::IsRemovableFromParentNode()` and make it check whether the
removing node is also editable.

Unfortunately, `EditorUtils::IsEditableContent()` needs to take editor type.
But it's most callers are in `HTMLEditor` and most of remains are in
common methods of `EditorBase`.  I guess we could remove this ugly argument
in the future.

Depends on D70874

Differential Revision: https://phabricator.services.mozilla.com/D70875

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2020-04-15 15:27:38 +00:00
Родитель 72d50388f6
Коммит 4cc133b568
15 изменённых файлов: 331 добавлений и 238 удалений

Просмотреть файл

@ -4,6 +4,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DeleteNodeTransaction.h" #include "DeleteNodeTransaction.h"
#include "HTMLEditUtils.h"
#include "mozilla/EditorBase.h" #include "mozilla/EditorBase.h"
#include "mozilla/SelectionState.h" // RangeUpdater #include "mozilla/SelectionState.h" // RangeUpdater
#include "nsDebug.h" #include "nsDebug.h"
@ -40,10 +42,11 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
bool DeleteNodeTransaction::CanDoIt() const { bool DeleteNodeTransaction::CanDoIt() const {
if (NS_WARN_IF(!mContentToDelete) || NS_WARN_IF(!mEditorBase) || if (NS_WARN_IF(!mContentToDelete) || NS_WARN_IF(!mEditorBase) ||
!mParentNode || !mEditorBase->IsModifiableNode(*mParentNode)) { !mParentNode) {
return false; return false;
} }
return true; return mEditorBase->IsTextEditor() ||
HTMLEditUtils::IsSimplyEditableNode(*mParentNode);
} }
NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() { NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() {
@ -51,7 +54,7 @@ NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() {
return NS_OK; return NS_OK;
} }
if (!mEditorBase->AsHTMLEditor() && mContentToDelete->IsText()) { if (mEditorBase->IsTextEditor() && mContentToDelete->IsText()) {
uint32_t length = mContentToDelete->AsText()->TextLength(); uint32_t length = mContentToDelete->AsText()->TextLength();
if (length > 0) { if (length > 0) {
mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length); mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length);
@ -93,7 +96,7 @@ DeleteNodeTransaction::UndoTransaction() {
NS_WARNING("nsINode::InsertBefore() failed"); NS_WARNING("nsINode::InsertBefore() failed");
return error.StealNSResult(); return error.StealNSResult();
} }
if (!editorBase->AsHTMLEditor() && contentToDelete->IsText()) { if (editorBase->IsTextEditor() && contentToDelete->IsText()) {
uint32_t length = contentToDelete->AsText()->TextLength(); uint32_t length = contentToDelete->AsText()->TextLength();
if (length > 0) { if (length > 0) {
nsresult rv = MOZ_KnownLive(editorBase->AsTextEditor()) nsresult rv = MOZ_KnownLive(editorBase->AsTextEditor())
@ -113,7 +116,7 @@ NS_IMETHODIMP DeleteNodeTransaction::RedoTransaction() {
return NS_OK; return NS_OK;
} }
if (!mEditorBase->AsHTMLEditor() && mContentToDelete->IsText()) { if (mEditorBase->IsTextEditor() && mContentToDelete->IsText()) {
uint32_t length = mContentToDelete->AsText()->TextLength(); uint32_t length = mContentToDelete->AsText()->TextLength();
if (length > 0) { if (length > 0) {
mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length); mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length);

Просмотреть файл

@ -5,6 +5,7 @@
#include "DeleteTextTransaction.h" #include "DeleteTextTransaction.h"
#include "HTMLEditUtils.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/EditorBase.h" #include "mozilla/EditorBase.h"
#include "mozilla/EditorDOMPoint.h" #include "mozilla/EditorDOMPoint.h"
@ -94,7 +95,8 @@ bool DeleteTextTransaction::CanDoIt() const {
if (NS_WARN_IF(!mTextNode) || NS_WARN_IF(!mEditorBase)) { if (NS_WARN_IF(!mTextNode) || NS_WARN_IF(!mEditorBase)) {
return false; return false;
} }
return mEditorBase->IsModifiableNode(*mTextNode); return mEditorBase->IsTextEditor() ||
HTMLEditUtils::IsSimplyEditableNode(*mTextNode);
} }
NS_IMETHODIMP DeleteTextTransaction::DoTransaction() { NS_IMETHODIMP DeleteTextTransaction::DoTransaction() {

Просмотреть файл

@ -639,11 +639,13 @@ bool EditorBase::IsSelectionEditable() {
return false; return false;
} }
if (!mIsHTMLEditorClass) { if (IsTextEditor()) {
// XXX we just check that the anchor node is editable at the moment // XXX we just check that the anchor node is editable at the moment
// we should check that all nodes in the selection are editable // we should check that all nodes in the selection are editable
nsCOMPtr<nsINode> anchorNode = SelectionRefPtr()->GetAnchorNode(); nsCOMPtr<nsINode> anchorNode = SelectionRefPtr()->GetAnchorNode();
return anchorNode && IsEditable(anchorNode); return anchorNode && anchorNode->IsContent() &&
EditorUtils::IsEditableContent(*anchorNode->AsContent(),
GetEditorType());
} }
nsINode* anchorNode = SelectionRefPtr()->GetAnchorNode(); nsINode* anchorNode = SelectionRefPtr()->GetAnchorNode();
@ -1788,7 +1790,7 @@ nsresult EditorBase::JoinNodesWithTransaction(nsINode& aLeftNode,
} }
NS_IMETHODIMP EditorBase::DeleteNode(nsINode* aNode) { NS_IMETHODIMP EditorBase::DeleteNode(nsINode* aNode) {
if (NS_WARN_IF(!aNode)) { if (NS_WARN_IF(!aNode) || NS_WARN_IF(!aNode->IsContent())) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
@ -1800,15 +1802,14 @@ NS_IMETHODIMP EditorBase::DeleteNode(nsINode* aNode) {
return EditorBase::ToGenericNSResult(rv); return EditorBase::ToGenericNSResult(rv);
} }
rv = DeleteNodeWithTransaction(*aNode); rv = DeleteNodeWithTransaction(MOZ_KnownLive(*aNode->AsContent()));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::DeleteNodeWithTransaction() failed"); "EditorBase::DeleteNodeWithTransaction() failed");
return EditorBase::ToGenericNSResult(rv); return EditorBase::ToGenericNSResult(rv);
} }
nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) { nsresult EditorBase::DeleteNodeWithTransaction(nsIContent& aContent) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aNode.IsContent());
IgnoredErrorResult ignoredError; IgnoredErrorResult ignoredError;
AutoEditSubActionNotifier startToHandleEditSubAction( AutoEditSubActionNotifier startToHandleEditSubAction(
@ -1821,13 +1822,13 @@ nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) {
"TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored"); "TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
if (AsHTMLEditor()) { if (AsHTMLEditor()) {
TopLevelEditSubActionDataRef().WillDeleteContent(*this, *aNode.AsContent()); TopLevelEditSubActionDataRef().WillDeleteContent(*this, aContent);
} }
// FYI: DeleteNodeTransaction grabs aNode while it's alive. So, it's safe // FYI: DeleteNodeTransaction grabs aContent while it's alive. So, it's safe
// to refer aNode even after calling DoTransaction(). // to refer aContent even after calling DoTransaction().
RefPtr<DeleteNodeTransaction> deleteNodeTransaction = RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
DeleteNodeTransaction::MaybeCreate(*this, *aNode.AsContent()); DeleteNodeTransaction::MaybeCreate(*this, aContent);
NS_WARNING_ASSERTION(deleteNodeTransaction, NS_WARNING_ASSERTION(deleteNodeTransaction,
"DeleteNodeTransaction::MaybeCreate() failed"); "DeleteNodeTransaction::MaybeCreate() failed");
nsresult rv; nsresult rv;
@ -1838,7 +1839,7 @@ nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) {
if (mTextServicesDocument && NS_SUCCEEDED(rv)) { if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument; RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
textServicesDocument->DidDeleteNode(&aNode); textServicesDocument->DidDeleteNode(&aContent);
} }
} else { } else {
rv = NS_ERROR_FAILURE; rv = NS_ERROR_FAILURE;
@ -1847,7 +1848,7 @@ nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) {
if (!mActionListeners.IsEmpty()) { if (!mActionListeners.IsEmpty()) {
AutoActionListenerArray listeners(mActionListeners); AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) { for (auto& listener : listeners) {
DebugOnly<nsresult> rvIgnored = listener->DidDeleteNode(&aNode, rv); DebugOnly<nsresult> rvIgnored = listener->DidDeleteNode(&aContent, rv);
NS_WARNING_ASSERTION( NS_WARNING_ASSERTION(
NS_SUCCEEDED(rvIgnored), NS_SUCCEEDED(rvIgnored),
"nsIEditActionListener::DidDeleteNode() failed, but ignored"); "nsIEditActionListener::DidDeleteNode() failed, but ignored");
@ -3095,12 +3096,13 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction(
nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) { nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) {
MOZ_ASSERT(aRoot); MOZ_ASSERT(aRoot);
nsIContent* node = GetLeftmostChild(aRoot); EditorType editorType = GetEditorType();
if (node && !IsEditable(node)) { nsIContent* content = GetLeftmostChild(aRoot);
node = GetNextEditableNode(*node); if (content && !EditorUtils::IsEditableContent(*content, editorType)) {
content = GetNextEditableNode(*content);
} }
return (node != aRoot) ? node : nullptr; return (content != aRoot) ? content : nullptr;
} }
nsresult EditorBase::NotifyDocumentListeners( nsresult EditorBase::NotifyDocumentListeners(
@ -3796,19 +3798,20 @@ nsIContent* EditorBase::GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint,
// unless there isn't one, in which case we are at the end of the node // unless there isn't one, in which case we are at the end of the node
// and want the deep-right child. // and want the deep-right child.
nsIContent* rightMostNode = nsIContent* rightMostContent =
GetRightmostChild(aPoint.GetContainer(), aNoBlockCrossing); GetRightmostChild(aPoint.GetContainer(), aNoBlockCrossing);
if (!rightMostNode) { if (!rightMostContent) {
return nullptr; return nullptr;
} }
if ((!aFindEditableNode || IsEditable(rightMostNode)) && if ((!aFindEditableNode ||
(aFindAnyDataNode || IsElementOrText(*rightMostNode))) { EditorUtils::IsEditableContent(*rightMostContent, GetEditorType())) &&
return rightMostNode; (aFindAnyDataNode || EditorUtils::IsElementOrText(*rightMostContent))) {
return rightMostContent;
} }
// restart the search from the non-editable node we just found // restart the search from the non-editable node we just found
return GetPreviousNodeInternal(*rightMostNode, aFindEditableNode, return GetPreviousNodeInternal(*rightMostContent, aFindEditableNode,
aFindAnyDataNode, aNoBlockCrossing); aFindAnyDataNode, aNoBlockCrossing);
} }
@ -3848,23 +3851,24 @@ nsIContent* EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint,
return point.GetChild(); return point.GetChild();
} }
nsIContent* leftMostNode = nsIContent* leftMostContent =
GetLeftmostChild(point.GetChild(), aNoBlockCrossing); GetLeftmostChild(point.GetChild(), aNoBlockCrossing);
if (!leftMostNode) { if (!leftMostContent) {
return point.GetChild(); return point.GetChild();
} }
if (!IsDescendantOfEditorRoot(leftMostNode)) { if (!IsDescendantOfEditorRoot(leftMostContent)) {
return nullptr; return nullptr;
} }
if ((!aFindEditableNode || IsEditable(leftMostNode)) && if ((!aFindEditableNode ||
(aFindAnyDataNode || IsElementOrText(*leftMostNode))) { EditorUtils::IsEditableContent(*leftMostContent, GetEditorType())) &&
return leftMostNode; (aFindAnyDataNode || EditorUtils::IsElementOrText(*leftMostContent))) {
return leftMostContent;
} }
// restart the search from the non-editable node we just found // restart the search from the non-editable node we just found
return GetNextNodeInternal(*leftMostNode, aFindEditableNode, return GetNextNodeInternal(*leftMostContent, aFindEditableNode,
aFindAnyDataNode, aNoBlockCrossing); aFindAnyDataNode, aNoBlockCrossing);
} }
@ -3941,15 +3945,16 @@ nsIContent* EditorBase::FindNode(nsINode* aCurrentNode, bool aGoForward,
return nullptr; return nullptr;
} }
nsCOMPtr<nsIContent> candidate = nsIContent* candidate =
FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing); FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing);
if (!candidate) { if (!candidate) {
return nullptr; return nullptr;
} }
if ((!aEditableNode || IsEditable(candidate)) && if ((!aEditableNode ||
(aFindAnyDataNode || IsElementOrText(*candidate))) { EditorUtils::IsEditableContent(*candidate, GetEditorType())) &&
(aFindAnyDataNode || EditorUtils::IsElementOrText(*candidate))) {
return candidate; return candidate;
} }
@ -4084,9 +4089,10 @@ bool EditorBase::IsContainer(nsINode* aNode) const {
uint32_t EditorBase::CountEditableChildren(nsINode* aNode) { uint32_t EditorBase::CountEditableChildren(nsINode* aNode) {
MOZ_ASSERT(aNode); MOZ_ASSERT(aNode);
uint32_t count = 0; uint32_t count = 0;
EditorType editorType = GetEditorType();
for (nsIContent* child = aNode->GetFirstChild(); child; for (nsIContent* child = aNode->GetFirstChild(); child;
child = child->GetNextSibling()) { child = child->GetNextSibling()) {
if (IsEditable(child)) { if (EditorUtils::IsEditableContent(*child, editorType)) {
++count; ++count;
} }
} }
@ -4342,6 +4348,7 @@ EditorDOMPoint EditorBase::JoinNodesDeepWithTransaction(
nsCOMPtr<nsINode> parentNode = aRightNode.GetParentNode(); nsCOMPtr<nsINode> parentNode = aRightNode.GetParentNode();
EditorDOMPoint ret; EditorDOMPoint ret;
EditorType editorType = GetEditorType();
while (leftNodeToJoin && rightNodeToJoin && parentNode && while (leftNodeToJoin && rightNodeToJoin && parentNode &&
AreNodesSameType(*leftNodeToJoin, *rightNodeToJoin)) { AreNodesSameType(*leftNodeToJoin, *rightNodeToJoin)) {
uint32_t length = leftNodeToJoin->Length(); uint32_t length = leftNodeToJoin->Length();
@ -4370,14 +4377,16 @@ EditorDOMPoint EditorBase::JoinNodesDeepWithTransaction(
} }
// Skip over non-editable nodes // Skip over non-editable nodes
while (leftNodeToJoin && !IsEditable(leftNodeToJoin)) { while (leftNodeToJoin &&
!EditorUtils::IsEditableContent(*leftNodeToJoin, editorType)) {
leftNodeToJoin = leftNodeToJoin->GetPreviousSibling(); leftNodeToJoin = leftNodeToJoin->GetPreviousSibling();
} }
if (!leftNodeToJoin) { if (!leftNodeToJoin) {
return ret; return ret;
} }
while (rightNodeToJoin && !IsEditable(rightNodeToJoin)) { while (rightNodeToJoin &&
!EditorUtils::IsEditableContent(*rightNodeToJoin, editorType)) {
rightNodeToJoin = rightNodeToJoin->GetNextSibling(); rightNodeToJoin = rightNodeToJoin->GetNextSibling();
} }
if (!rightNodeToJoin) { if (!rightNodeToJoin) {
@ -4440,11 +4449,14 @@ nsresult EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() {
// Now we've got the body element. Iterate over the body element's children, // Now we've got the body element. Iterate over the body element's children,
// looking for editable content. If no editable content is found, insert the // looking for editable content. If no editable content is found, insert the
// padding <br> element. // padding <br> element.
bool isRootEditable = IsEditable(rootElement); EditorType editorType = GetEditorType();
bool isRootEditable =
EditorUtils::IsEditableContent(*rootElement, editorType);
for (nsIContent* rootChild = rootElement->GetFirstChild(); rootChild; for (nsIContent* rootChild = rootElement->GetFirstChild(); rootChild;
rootChild = rootChild->GetNextSibling()) { rootChild = rootChild->GetNextSibling()) {
if (EditorBase::IsPaddingBRElementForEmptyEditor(*rootChild) || if (EditorUtils::IsPaddingBRElementForEmptyEditor(*rootChild) ||
!isRootEditable || IsEditable(rootChild) || !isRootEditable ||
EditorUtils::IsEditableContent(*rootChild, editorType) ||
HTMLEditUtils::IsBlockElement(*rootChild)) { HTMLEditUtils::IsBlockElement(*rootChild)) {
return NS_OK; return NS_OK;
} }
@ -4452,7 +4464,7 @@ nsresult EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() {
// Skip adding the padding <br> element for empty editor if body // Skip adding the padding <br> element for empty editor if body
// is read-only. // is read-only.
if (!IsModifiableNode(*rootElement)) { if (IsHTMLEditor() && !HTMLEditUtils::IsSimplyEditableNode(*rootElement)) {
return NS_OK; return NS_OK;
} }
@ -5380,10 +5392,6 @@ nsresult EditorBase::SetTextDirectionTo(TextDirection aTextDirection) {
return NS_OK; return NS_OK;
} }
bool EditorBase::IsModifiableNode(const nsINode& aNode) const {
return !AsHTMLEditor() || aNode.IsEditable();
}
nsIContent* EditorBase::GetFocusedContent() const { nsIContent* EditorBase::GetFocusedContent() const {
EventTarget* piTarget = GetDOMEventTarget(); EventTarget* piTarget = GetDOMEventTarget();
if (!piTarget) { if (!piTarget) {
@ -6017,7 +6025,7 @@ EditorBase::AutoEditActionDataSetter::AutoEditActionDataSetter(
mEditAction = aEditAction; mEditAction = aEditAction;
mDirectionOfTopLevelEditSubAction = eNone; mDirectionOfTopLevelEditSubAction = eNone;
if (mEditorBase.mIsHTMLEditorClass) { if (mEditorBase.IsHTMLEditor()) {
mTopLevelEditSubActionData.mSelectedRange = mTopLevelEditSubActionData.mSelectedRange =
mEditorBase.AsHTMLEditor() mEditorBase.AsHTMLEditor()
->GetSelectedRangeItemForTopLevelEditSubAction(); ->GetSelectedRangeItemForTopLevelEditSubAction();

Просмотреть файл

@ -154,6 +154,8 @@ class EditorBase : public nsIEditor,
typedef dom::Selection Selection; typedef dom::Selection Selection;
typedef dom::Text Text; typedef dom::Text Text;
enum class EditorType { Text, HTML };
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor) NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor)
@ -169,6 +171,9 @@ class EditorBase : public nsIEditor,
*/ */
EditorBase(); EditorBase();
bool IsTextEditor() const { return !mIsHTMLEditorClass; }
bool IsHTMLEditor() const { return mIsHTMLEditorClass; }
/** /**
* Init is to tell the implementation of nsIEditor to begin its services * Init is to tell the implementation of nsIEditor to begin its services
* @param aDoc The dom document interface being observed * @param aDoc The dom document interface being observed
@ -214,7 +219,7 @@ class EditorBase : public nsIEditor,
// @return true, iff at least one of NS_EVENT_BITS_MUTATION_* is set. // @return true, iff at least one of NS_EVENT_BITS_MUTATION_* is set.
bool MaybeHasMutationEventListeners( bool MaybeHasMutationEventListeners(
uint32_t aMutationEventType = 0xFFFFFFFF) const { uint32_t aMutationEventType = 0xFFFFFFFF) const {
if (!mIsHTMLEditorClass) { if (IsTextEditor()) {
// DOM mutation event listeners cannot catch the changes of // DOM mutation event listeners cannot catch the changes of
// <input type="text"> nor <textarea>. // <input type="text"> nor <textarea>.
return false; return false;
@ -1410,11 +1415,11 @@ class EditorBase : public nsIEditor,
SetTextNodeWithoutTransaction(const nsAString& aString, Text& aTextNode); SetTextNodeWithoutTransaction(const nsAString& aString, Text& aTextNode);
/** /**
* DeleteNodeWithTransaction() removes aNode from the DOM tree. * DeleteNodeWithTransaction() removes aContent from the DOM tree.
* *
* @param aNode The node which will be removed form the DOM tree. * @param aContent The node which will be removed form the DOM tree.
*/ */
MOZ_CAN_RUN_SCRIPT nsresult DeleteNodeWithTransaction(nsINode& aNode); MOZ_CAN_RUN_SCRIPT nsresult DeleteNodeWithTransaction(nsIContent& aContent);
/** /**
* InsertNodeWithTransaction() inserts aContentToInsert before the child * InsertNodeWithTransaction() inserts aContentToInsert before the child
@ -2097,54 +2102,6 @@ class EditorBase : public nsIEditor,
*/ */
virtual bool IsContainer(nsINode* aNode) const; virtual bool IsContainer(nsINode* aNode) const;
/**
* returns true if aNode is an editable node.
*/
bool IsEditable(nsINode* aNode) const {
if (NS_WARN_IF(!aNode)) {
return false;
}
if (!aNode->IsContent() || !IsModifiableNode(*aNode) ||
EditorBase::IsPaddingBRElementForEmptyEditor(*aNode)) {
return false;
}
switch (aNode->NodeType()) {
case nsINode::ELEMENT_NODE:
// In HTML editors, if we're dealing with an element, then ask it
// whether it's editable.
return mIsHTMLEditorClass ? aNode->IsEditable() : true;
case nsINode::TEXT_NODE:
// Text nodes are considered to be editable by both typed of editors.
return true;
default:
return false;
}
}
/**
* Returns true if aNode is a usual element node (not padding <br> element
* for empty editor) or a text node. In other words, returns true if aNode
* is a usual element node or visible data node.
*/
bool IsElementOrText(const nsINode& aNode) const {
if (aNode.IsText()) {
return true;
}
return aNode.IsElement() &&
!EditorBase::IsPaddingBRElementForEmptyEditor(aNode);
}
/**
* Returns true if aNode is a <br> element and it's marked as padding for
* empty editor.
*/
static bool IsPaddingBRElementForEmptyEditor(const nsINode& aNode) {
const dom::HTMLBRElement* brElement = dom::HTMLBRElement::FromNode(&aNode);
return brElement && brElement->IsPaddingForEmptyEditor();
}
/** /**
* Returns true if aNode is a <br> element and it's marked as padding for * Returns true if aNode is a <br> element and it's marked as padding for
* empty last line. * empty last line.
@ -2181,11 +2138,6 @@ class EditorBase : public nsIEditor,
return aNode->NodeType() == nsINode::TEXT_NODE; return aNode->NodeType() == nsINode::TEXT_NODE;
} }
/**
* IsModifiableNode() checks whether the node is editable or not.
*/
bool IsModifiableNode(const nsINode& aNode) const;
/** /**
* GetNodeAtRangeOffsetPoint() returns the node at this position in a range, * GetNodeAtRangeOffsetPoint() returns the node at this position in a range,
* assuming that the container is the node itself if it's a text node, or * assuming that the container is the node itself if it's a text node, or
@ -2369,6 +2321,10 @@ class EditorBase : public nsIEditor,
*/ */
virtual ~EditorBase(); virtual ~EditorBase();
MOZ_ALWAYS_INLINE EditorType GetEditorType() const {
return mIsHTMLEditorClass ? EditorType::HTML : EditorType::Text;
}
int32_t WrapWidth() const { return mWrapColumn; } int32_t WrapWidth() const { return mWrapColumn; }
/** /**

Просмотреть файл

@ -12,6 +12,7 @@
#include "mozilla/EditorDOMPoint.h" #include "mozilla/EditorDOMPoint.h"
#include "mozilla/GuardObjects.h" #include "mozilla/GuardObjects.h"
#include "mozilla/RangeBoundary.h" #include "mozilla/RangeBoundary.h"
#include "mozilla/dom/HTMLBRElement.h"
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
#include "mozilla/dom/StaticRange.h" #include "mozilla/dom/StaticRange.h"
#include "nsAtom.h" #include "nsAtom.h"
@ -783,6 +784,8 @@ class MOZ_RAII DOMSubtreeIterator final : public DOMIterator {
class EditorUtils final { class EditorUtils final {
public: public:
using EditorType = EditorBase::EditorType;
/** /**
* IsDescendantOf() checks if aNode is a child or a descendant of aParent. * IsDescendantOf() checks if aNode is a child or a descendant of aParent.
* aOutPoint is set to the child of aParent. * aOutPoint is set to the child of aParent.
@ -794,6 +797,53 @@ class EditorUtils final {
static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent, static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
EditorDOMPoint* aOutPoint); EditorDOMPoint* aOutPoint);
/**
* Returns true if aContent is a <br> element and it's marked as padding for
* empty editor.
*/
static bool IsPaddingBRElementForEmptyEditor(const nsIContent& aContent) {
const dom::HTMLBRElement* brElement =
dom::HTMLBRElement::FromNode(&aContent);
return brElement && brElement->IsPaddingForEmptyEditor();
}
/**
* IsEditableContent() returns true if aContent's data or children is ediable
* for the given editor type. Be aware, returning true does NOT mean the
* node can be removed from its parent node, and returning false does NOT
* mean the node cannot be removed from the parent node.
* XXX May be the anonymous nodes in TextEditor not editable? If it's not
* so, we can get rid of aEditorType.
*/
static bool IsEditableContent(const nsIContent& aContent,
EditorType aEditorType) {
if ((aEditorType == EditorType::HTML && !aContent.IsEditable()) ||
EditorUtils::IsPaddingBRElementForEmptyEditor(aContent)) {
return false;
}
// In HTML editors, if we're dealing with an element, then ask it
// whether it's editable.
if (aContent.IsElement()) {
return aEditorType == EditorType::HTML ? aContent.IsEditable() : true;
}
// Text nodes are considered to be editable by both typed of editors.
return aContent.IsText();
}
/**
* Returns true if aContent is a usual element node (not padding <br> element
* for empty editor) or a text node. In other words, returns true if
* aContent is a usual element node or visible data node.
*/
static bool IsElementOrText(const nsIContent& aContent) {
if (aContent.IsText()) {
return true;
}
return aContent.IsElement() &&
!EditorUtils::IsPaddingBRElementForEmptyEditor(aContent);
}
/** /**
* Helper method for `AppendString()` and `AppendSubString()`. This should * Helper method for `AppendString()` and `AppendSubString()`. This should
* be called only when `aText` is in a password field. This method masks * be called only when `aText` is in a password field. This method masks

Просмотреть файл

@ -4,6 +4,7 @@
#include "HTMLEditor.h" #include "HTMLEditor.h"
#include "HTMLEditUtils.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/PresShell.h" #include "mozilla/PresShell.h"
#include "mozilla/PresShellInlines.h" #include "mozilla/PresShellInlines.h"
@ -436,7 +437,8 @@ nsresult HTMLEditor::RefreshEditingUI() {
nsIContent* hostContent = GetActiveEditingHost(); nsIContent* hostContent = GetActiveEditingHost();
if (IsObjectResizerEnabled() && focusElement && if (IsObjectResizerEnabled() && focusElement &&
IsModifiableNode(*focusElement) && focusElement != hostContent) { HTMLEditUtils::IsSimplyEditableNode(*focusElement) &&
focusElement != hostContent) {
if (nsGkAtoms::img == focusTagAtom) { if (nsGkAtoms::img == focusTagAtom) {
mResizedObjectIsAnImage = true; mResizedObjectIsAnImage = true;
} }
@ -456,7 +458,8 @@ nsresult HTMLEditor::RefreshEditingUI() {
} }
if (IsAbsolutePositionEditorEnabled() && absPosElement && if (IsAbsolutePositionEditorEnabled() && absPosElement &&
IsModifiableNode(*absPosElement) && absPosElement != hostContent) { HTMLEditUtils::IsSimplyEditableNode(*absPosElement) &&
absPosElement != hostContent) {
if (mAbsolutelyPositionedObject) { if (mAbsolutelyPositionedObject) {
nsresult rv = RefreshGrabberInternal(); nsresult rv = RefreshGrabberInternal();
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
@ -472,8 +475,10 @@ nsresult HTMLEditor::RefreshEditingUI() {
} }
} }
// XXX Shouldn't we check whether the `<table>` element is editable or not?
if (IsInlineTableEditorEnabled() && cellElement && if (IsInlineTableEditorEnabled() && cellElement &&
IsModifiableNode(*cellElement) && cellElement != hostContent) { HTMLEditUtils::IsSimplyEditableNode(*cellElement) &&
cellElement != hostContent) {
if (mInlineEditedCell) { if (mInlineEditedCell) {
nsresult rv = RefreshInlineTableEditingUIInternal(); nsresult rv = RefreshInlineTableEditingUIInternal();
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {

Просмотреть файл

@ -688,7 +688,7 @@ EditActionResult HTMLEditor::CanHandleHTMLEditSubAction() const {
return EditActionResult(NS_ERROR_FAILURE); return EditActionResult(NS_ERROR_FAILURE);
} }
if (!IsModifiableNode(*selStartNode)) { if (!HTMLEditUtils::IsSimplyEditableNode(*selStartNode)) {
return EditActionCanceled(); return EditActionCanceled();
} }
@ -701,21 +701,22 @@ EditActionResult HTMLEditor::CanHandleHTMLEditSubAction() const {
return EditActionIgnored(); return EditActionIgnored();
} }
if (!IsModifiableNode(*selEndNode)) { if (!HTMLEditUtils::IsSimplyEditableNode(*selEndNode)) {
return EditActionCanceled(); return EditActionCanceled();
} }
// XXX What does it mean the common ancestor is editable? I have no idea.
// It should be in same (active) editing host, and even if it's editable,
// there may be non-editable contents in the range.
nsINode* commonAncestor = range->GetClosestCommonInclusiveAncestor(); nsINode* commonAncestor = range->GetClosestCommonInclusiveAncestor();
if (!commonAncestor) { if (!commonAncestor) {
NS_WARNING( NS_WARNING(
"AbstractRange::GetClosestCommonInclusiveAncestor() returned nullptr"); "AbstractRange::GetClosestCommonInclusiveAncestor() returned nullptr");
return EditActionResult(NS_ERROR_FAILURE); return EditActionResult(NS_ERROR_FAILURE);
} }
if (!IsModifiableNode(*commonAncestor)) { return HTMLEditUtils::IsSimplyEditableNode(*commonAncestor)
return EditActionCanceled(); ? EditActionIgnored()
} : EditActionCanceled();
return EditActionIgnored();
} }
ListElementSelectionState::ListElementSelectionState(HTMLEditor& aHTMLEditor, ListElementSelectionState::ListElementSelectionState(HTMLEditor& aHTMLEditor,
@ -1255,7 +1256,7 @@ nsresult ParagraphStateAtSelection::CollectEditableFormatNodesInSelection(
OwningNonNull<nsIContent> content = aArrayOfContents[i]; OwningNonNull<nsIContent> content = aArrayOfContents[i];
// Remove all non-editable nodes. Leave them be. // Remove all non-editable nodes. Leave them be.
if (!aHTMLEditor.IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
aArrayOfContents.RemoveElementAt(i); aArrayOfContents.RemoveElementAt(i);
continue; continue;
} }
@ -1823,7 +1824,8 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
MOZ_ASSERT(atStartOfSelection.IsSetAndValid()); MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
// Do nothing if the node is read-only // Do nothing if the node is read-only
if (!IsModifiableNode(*atStartOfSelection.GetContainer())) { if (!HTMLEditUtils::IsSimplyEditableNode(
*atStartOfSelection.GetContainer())) {
return EditActionCanceled(); return EditActionCanceled();
} }
@ -3481,13 +3483,18 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
} }
} }
if (NS_WARN_IF(!atCaret.IsInContentNode()) ||
NS_WARN_IF(!selectionEndPoint.IsInContentNode())) {
return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
}
// We might have left only collapsed whitespace in the start/end nodes // We might have left only collapsed whitespace in the start/end nodes
{ {
AutoTrackDOMPoint startTracker(RangeUpdaterRef(), &atCaret); AutoTrackDOMPoint startTracker(RangeUpdaterRef(), &atCaret);
AutoTrackDOMPoint endTracker(RangeUpdaterRef(), &selectionEndPoint); AutoTrackDOMPoint endTracker(RangeUpdaterRef(), &selectionEndPoint);
nsresult rv = DeleteNodeIfInvisibleAndEditableTextNode( nsresult rv = DeleteNodeIfInvisibleAndEditableTextNode(
MOZ_KnownLive(*atCaret.GetContainer())); MOZ_KnownLive(*atCaret.ContainerAsContent()));
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -3495,7 +3502,7 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
"HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode(" "HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode("
") failed to remove start node, but ignored"); ") failed to remove start node, but ignored");
rv = DeleteNodeIfInvisibleAndEditableTextNode( rv = DeleteNodeIfInvisibleAndEditableTextNode(
MOZ_KnownLive(*selectionEndPoint.GetContainer())); MOZ_KnownLive(*selectionEndPoint.ContainerAsContent()));
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -3512,19 +3519,20 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
return rv; return rv;
} }
nsresult HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode(nsINode& aNode) { nsresult HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode(
nsIContent& aContent) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
Text* text = aNode.GetAsText(); Text* text = aContent.GetAsText();
if (!text) { if (!text) {
return NS_OK; return NS_OK;
} }
if (IsVisibleTextNode(*text) || !IsModifiableNode(*text)) { if (IsVisibleTextNode(*text) || !HTMLEditUtils::IsSimplyEditableNode(*text)) {
return NS_OK; return NS_OK;
} }
nsresult rv = DeleteNodeWithTransaction(aNode); nsresult rv = DeleteNodeWithTransaction(aContent);
if (NS_WARN_IF(Destroyed())) { if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4177,7 +4185,7 @@ nsresult HTMLEditor::DeleteElementsExceptTableRelatedElements(nsINode& aNode) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
if (!HTMLEditUtils::IsTableElementButNotTable(&aNode)) { if (!HTMLEditUtils::IsTableElementButNotTable(&aNode)) {
nsresult rv = DeleteNodeWithTransaction(aNode); nsresult rv = DeleteNodeWithTransaction(MOZ_KnownLive(*aNode.AsContent()));
if (NS_WARN_IF(Destroyed())) { if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4545,7 +4553,7 @@ EditActionResult HTMLEditor::ChangeSelectedHardLinesToList(
// If current node is a `<br>` element, delete it and forget previous // If current node is a `<br>` element, delete it and forget previous
// list item element. // list item element.
// If current node is an empty inline node, just delete it. // If current node is an empty inline node, just delete it.
if (IsEditable(content) && if (EditorUtils::IsEditableContent(content, EditorType::HTML) &&
(content->IsHTMLElement(nsGkAtoms::br) || IsEmptyInlineNode(content))) { (content->IsHTMLElement(nsGkAtoms::br) || IsEmptyInlineNode(content))) {
nsresult rv = DeleteNodeWithTransaction(*content); nsresult rv = DeleteNodeWithTransaction(*content);
if (NS_WARN_IF(Destroyed())) { if (NS_WARN_IF(Destroyed())) {
@ -4929,7 +4937,7 @@ nsresult HTMLEditor::RemoveListAtSelectionAsSubAction() {
// CollectNonEditableNodes::No. // CollectNonEditableNodes::No.
for (int32_t i = arrayOfContents.Length() - 1; i >= 0; i--) { for (int32_t i = arrayOfContents.Length() - 1; i >= 0; i--) {
OwningNonNull<nsIContent>& content = arrayOfContents[i]; OwningNonNull<nsIContent>& content = arrayOfContents[i];
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
arrayOfContents.RemoveElementAt(i); arrayOfContents.RemoveElementAt(i);
} }
} }
@ -5498,7 +5506,7 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
// Ignore all non-editable nodes. Leave them be. // Ignore all non-editable nodes. Leave them be.
// XXX We ignore non-editable nodes here, but not so in the above block. // XXX We ignore non-editable nodes here, but not so in the above block.
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue; continue;
} }
@ -5706,7 +5714,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
// Ignore all non-editable nodes. Leave them be. // Ignore all non-editable nodes. Leave them be.
// XXX We ignore non-editable nodes here, but not so in the above block. // XXX We ignore non-editable nodes here, but not so in the above block.
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue; continue;
} }
@ -6908,7 +6916,7 @@ nsresult HTMLEditor::AlignNodesAndDescendants(
++indexOfTransitionList; ++indexOfTransitionList;
// Ignore all non-editable nodes. Leave them be. // Ignore all non-editable nodes. Leave them be.
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue; continue;
} }
@ -7350,7 +7358,7 @@ size_t HTMLEditor::CollectChildren(
aIndexToInsertChildren + numberOfFoundChildren, aCollectListChildren, aIndexToInsertChildren + numberOfFoundChildren, aCollectListChildren,
aCollectTableChildren, aCollectNonEditableNodes); aCollectTableChildren, aCollectNonEditableNodes);
} else if (aCollectNonEditableNodes == CollectNonEditableNodes::Yes || } else if (aCollectNonEditableNodes == CollectNonEditableNodes::Yes ||
IsEditable(content)) { EditorUtils::IsEditableContent(*content, EditorType::HTML)) {
aOutArrayOfContents.InsertElementAt( aOutArrayOfContents.InsertElementAt(
aIndexToInsertChildren + numberOfFoundChildren++, *content); aIndexToInsertChildren + numberOfFoundChildren++, *content);
} }
@ -8155,7 +8163,8 @@ nsresult HTMLEditor::CollectEditTargetNodes(
} }
if (aCollectNonEditableNodes == CollectNonEditableNodes::No) { if (aCollectNonEditableNodes == CollectNonEditableNodes::No) {
for (size_t i = aOutArrayOfContents.Length(); i > 0; --i) { for (size_t i = aOutArrayOfContents.Length(); i > 0; --i) {
if (!IsEditable(aOutArrayOfContents[i - 1])) { if (!EditorUtils::IsEditableContent(aOutArrayOfContents[i - 1],
EditorType::HTML)) {
aOutArrayOfContents.RemoveElementAt(i - 1); aOutArrayOfContents.RemoveElementAt(i - 1);
} }
} }
@ -9303,7 +9312,7 @@ nsresult HTMLEditor::RemoveBlockContainerElements(
} }
firstContent = lastContent = curBlock = nullptr; firstContent = lastContent = curBlock = nullptr;
} }
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue; continue;
} }
// Remove current block // Remove current block
@ -9337,7 +9346,7 @@ nsresult HTMLEditor::RemoveBlockContainerElements(
} }
firstContent = lastContent = curBlock = nullptr; firstContent = lastContent = curBlock = nullptr;
} }
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue; continue;
} }
// Recursion time // Recursion time
@ -9376,7 +9385,7 @@ nsresult HTMLEditor::RemoveBlockContainerElements(
} }
curBlock = GetBlockNodeParent(content); curBlock = GetBlockNodeParent(content);
if (!curBlock || !HTMLEditUtils::IsFormatNode(curBlock) || if (!curBlock || !HTMLEditUtils::IsFormatNode(curBlock) ||
!IsEditable(curBlock)) { !EditorUtils::IsEditableContent(*curBlock, EditorType::HTML)) {
// Not a block kind that we care about. // Not a block kind that we care about.
curBlock = nullptr; curBlock = nullptr;
} else { } else {
@ -9439,7 +9448,8 @@ nsresult HTMLEditor::CreateOrChangeBlockContainerElement(
// Is it already the right kind of block, or an uneditable block? // Is it already the right kind of block, or an uneditable block?
if (content->IsHTMLElement(&aBlockTag) || if (content->IsHTMLElement(&aBlockTag) ||
(!IsEditable(content) && HTMLEditUtils::IsBlockElement(content))) { (!EditorUtils::IsEditableContent(content, EditorType::HTML) &&
HTMLEditUtils::IsBlockElement(content))) {
// Forget any previous block used for previous inline nodes // Forget any previous block used for previous inline nodes
curBlock = nullptr; curBlock = nullptr;
// Do nothing to this block // Do nothing to this block
@ -9605,7 +9615,8 @@ nsresult HTMLEditor::CreateOrChangeBlockContainerElement(
// added here if that should change // added here if that should change
// //
// If content is a non editable, drop it if we are going to <pre>. // If content is a non editable, drop it if we are going to <pre>.
if (&aBlockTag == nsGkAtoms::pre && !IsEditable(content)) { if (&aBlockTag == nsGkAtoms::pre &&
!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
// Do nothing to this block // Do nothing to this block
continue; continue;
} }
@ -9999,7 +10010,7 @@ nsresult HTMLEditor::InsertBRElementToEmptyListItemsAndTableCellsInRange(
MOZ_ASSERT(Element::FromNode(&aNode)); MOZ_ASSERT(Element::FromNode(&aNode));
MOZ_ASSERT(aSelf); MOZ_ASSERT(aSelf);
Element* element = aNode.AsElement(); Element* element = aNode.AsElement();
if (!static_cast<HTMLEditor*>(aSelf)->IsEditable(element) || if (!EditorUtils::IsEditableContent(*element, EditorType::HTML) ||
(!HTMLEditUtils::IsListItem(element) && (!HTMLEditUtils::IsListItem(element) &&
!HTMLEditUtils::IsTableCellOrCaption(*element))) { !HTMLEditUtils::IsTableCellOrCaption(*element))) {
return false; return false;
@ -10175,14 +10186,15 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
MOZ_ASSERT(SelectionRefPtr()->IsCollapsed()); MOZ_ASSERT(SelectionRefPtr()->IsCollapsed());
EditorDOMPoint point(EditorBase::GetStartPoint(*SelectionRefPtr())); EditorDOMPoint point(EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!point.IsSet())) { if (NS_WARN_IF(!point.IsInContentNode())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// If selection start is not editable, climb up the tree until editable one. // If selection start is not editable, climb up the tree until editable one.
while (!IsEditable(point.GetContainer())) { while (!EditorUtils::IsEditableContent(*point.ContainerAsContent(),
EditorType::HTML)) {
point.Set(point.GetContainer()); point.Set(point.GetContainer());
if (NS_WARN_IF(!point.IsSet())) { if (NS_WARN_IF(!point.IsInContentNode())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
} }
@ -10190,7 +10202,9 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
// If caret is in empty block element, we need to insert a `<br>` element // If caret is in empty block element, we need to insert a `<br>` element
// because the block should have one-line height. // because the block should have one-line height.
if (RefPtr<Element> blockElement = GetBlock(*point.GetContainer())) { if (RefPtr<Element> blockElement = GetBlock(*point.GetContainer())) {
if (IsEditable(blockElement) && IsEmptyNode(*blockElement, false, false) && if (blockElement &&
EditorUtils::IsEditableContent(*blockElement, EditorType::HTML) &&
IsEmptyNode(*blockElement, false, false) &&
CanContainTag(*point.GetContainer(), *nsGkAtoms::br)) { CanContainTag(*point.GetContainer(), *nsGkAtoms::br)) {
Element* bodyOrDocumentElement = GetRoot(); Element* bodyOrDocumentElement = GetRoot();
if (NS_WARN_IF(!bodyOrDocumentElement)) { if (NS_WARN_IF(!bodyOrDocumentElement)) {
@ -10501,7 +10515,8 @@ nsresult HTMLEditor::RemoveEmptyNodesIn(nsRange& aRange) {
// now delete the empty nodes // now delete the empty nodes
for (OwningNonNull<nsIContent>& emptyContent : arrayOfEmptyContents) { for (OwningNonNull<nsIContent>& emptyContent : arrayOfEmptyContents) {
if (IsModifiableNode(emptyContent)) { // XXX Shouldn't we check whether it's removable from its parent??
if (HTMLEditUtils::IsSimplyEditableNode(emptyContent)) {
// MOZ_KnownLive because 'arrayOfEmptyContents' is guaranteed to keep it // MOZ_KnownLive because 'arrayOfEmptyContents' is guaranteed to keep it
// alive. // alive.
rv = DeleteNodeWithTransaction(MOZ_KnownLive(emptyContent)); rv = DeleteNodeWithTransaction(MOZ_KnownLive(emptyContent));
@ -11380,7 +11395,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
} }
// Ignore all non-editable nodes. Leave them be. // Ignore all non-editable nodes. Leave them be.
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue; continue;
} }

Просмотреть файл

@ -14,11 +14,31 @@ class nsAtom;
namespace mozilla { namespace mozilla {
enum class EditAction;
class HTMLEditUtils final { class HTMLEditUtils final {
using Element = dom::Element; using Element = dom::Element;
using Selection = dom::Selection; using Selection = dom::Selection;
public: public:
/**
* IsSimplyEditableNode() returns true when aNode is simply editable.
* This does NOT means that aNode can be removed from current parent nor
* aNode's data is editable.
*/
static bool IsSimplyEditableNode(const nsINode& aNode) {
return aNode.IsEditable();
}
/**
* IsRemovableFromParentNode() returns true when aContent is editable, has a
* parent node and the parent node is also editable.
*/
static bool IsRemovableFromParentNode(const nsIContent& aContent) {
return aContent.IsEditable() && aContent.GetParentNode() &&
aContent.GetParentNode()->IsEditable();
}
/** /**
* IsBlockElement() returns true if aContent is an element and it should * IsBlockElement() returns true if aContent is an element and it should
* be treated as a block. (This does not refer style information.) * be treated as a block. (This does not refer style information.)

Просмотреть файл

@ -1841,7 +1841,9 @@ EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
return EditorDOMPoint(); return EditorDOMPoint();
} }
if (!IsEditable(pointToInsert.GetContainer())) { if (!pointToInsert.IsInContentNode() ||
!EditorUtils::IsEditableContent(*pointToInsert.ContainerAsContent(),
EditorType::HTML)) {
// There's no suitable place to put the node in this editing host. Maybe // There's no suitable place to put the node in this editing host. Maybe
// someone is trying to put block content in a span. So just put it // someone is trying to put block content in a span. So just put it
// where we were originally asked. // where we were originally asked.
@ -3365,18 +3367,15 @@ nsresult HTMLEditor::DeleteSelectionWithTransaction(
return rv; return rv;
} }
nsresult HTMLEditor::DeleteNodeWithTransaction(nsINode& aNode) { nsresult HTMLEditor::DeleteNodeWithTransaction(nsIContent& aContent) {
if (NS_WARN_IF(!aNode.IsContent())) {
return NS_ERROR_INVALID_ARG;
}
// Do nothing if the node is read-only. // Do nothing if the node is read-only.
// XXX This is not a override method of EditorBase's method. This might // XXX This is not a override method of EditorBase's method. This might
// cause not called accidentally. We need to investigate this issue. // cause not called accidentally. We need to investigate this issue.
if (NS_WARN_IF(!IsModifiableNode(*aNode.AsContent()) && if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aContent) &&
!EditorBase::IsPaddingBRElementForEmptyEditor(aNode))) { !EditorUtils::IsPaddingBRElementForEmptyEditor(aContent))) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsresult rv = EditorBase::DeleteNodeWithTransaction(aNode); nsresult rv = EditorBase::DeleteNodeWithTransaction(aContent);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::DeleteNodeWithTransaction() failed"); "EditorBase::DeleteNodeWithTransaction() failed");
return rv; return rv;
@ -3396,7 +3395,7 @@ nsresult HTMLEditor::DeleteAllChildrenWithTransaction(Element& aElement) {
!ignoredError.Failed(), !ignoredError.Failed(),
"OnStartToHandleTopLevelEditSubAction() failed, but ignored"); "OnStartToHandleTopLevelEditSubAction() failed, but ignored");
while (nsCOMPtr<nsINode> child = aElement.GetLastChild()) { while (nsCOMPtr<nsIContent> child = aElement.GetLastChild()) {
nsresult rv = DeleteNodeWithTransaction(*child); nsresult rv = DeleteNodeWithTransaction(*child);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::DeleteNodeWithTransaction() failed"); NS_WARNING("HTMLEditor::DeleteNodeWithTransaction() failed");
@ -3510,7 +3509,7 @@ nsresult HTMLEditor::DeleteParentBlocksWithTransactionIfEmpty(
} }
NS_IMETHODIMP HTMLEditor::DeleteNode(nsINode* aNode) { NS_IMETHODIMP HTMLEditor::DeleteNode(nsINode* aNode) {
if (NS_WARN_IF(!aNode)) { if (NS_WARN_IF(!aNode) || NS_WARN_IF(!aNode->IsContent())) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
@ -3522,7 +3521,7 @@ NS_IMETHODIMP HTMLEditor::DeleteNode(nsINode* aNode) {
return EditorBase::ToGenericNSResult(rv); return EditorBase::ToGenericNSResult(rv);
} }
rv = DeleteNodeWithTransaction(*aNode); rv = DeleteNodeWithTransaction(MOZ_KnownLive(*aNode->AsContent()));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::DeleteNodeWithTransaction() failed"); "HTMLEditor::DeleteNodeWithTransaction() failed");
return rv; return rv;
@ -3531,7 +3530,7 @@ NS_IMETHODIMP HTMLEditor::DeleteNode(nsINode* aNode) {
nsresult HTMLEditor::DeleteTextWithTransaction(Text& aTextNode, nsresult HTMLEditor::DeleteTextWithTransaction(Text& aTextNode,
uint32_t aOffset, uint32_t aOffset,
uint32_t aLength) { uint32_t aLength) {
if (NS_WARN_IF(!IsModifiableNode(aTextNode))) { if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aTextNode))) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -3551,7 +3550,8 @@ nsresult HTMLEditor::InsertTextWithTransaction(
} }
// Do nothing if the node is read-only // Do nothing if the node is read-only
if (NS_WARN_IF(!IsModifiableNode(*aPointToInsert.GetContainer()))) { if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(
*aPointToInsert.GetContainer()))) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -3694,7 +3694,7 @@ void HTMLEditor::DoContentInserted(nsIContent* aChild,
} }
// We don't need to handle our own modifications // We don't need to handle our own modifications
else if (!GetTopLevelEditSubAction() && container->IsEditable()) { else if (!GetTopLevelEditSubAction() && container->IsEditable()) {
if (EditorBase::IsPaddingBRElementForEmptyEditor(*aChild)) { if (EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) {
// Ignore insertion of the padding <br> element. // Ignore insertion of the padding <br> element.
return; return;
} }
@ -3747,7 +3747,7 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::ContentRemoved(
// We don't need to handle our own modifications // We don't need to handle our own modifications
} else if (!GetTopLevelEditSubAction() && } else if (!GetTopLevelEditSubAction() &&
aChild->GetParentNode()->IsEditable()) { aChild->GetParentNode()->IsEditable()) {
if (aChild && EditorBase::IsPaddingBRElementForEmptyEditor(*aChild)) { if (aChild && EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) {
// Ignore removal of the padding <br> element for empty editor. // Ignore removal of the padding <br> element for empty editor.
return; return;
} }
@ -3958,10 +3958,11 @@ nsresult HTMLEditor::CollapseAdjacentTextNodes(nsRange& aInRange) {
} }
AutoTArray<OwningNonNull<Text>, 8> textNodes; AutoTArray<OwningNonNull<Text>, 8> textNodes;
subtreeIter.AppendNodesToArray( subtreeIter.AppendNodesToArray(
+[](nsINode& aNode, void* aSelf) -> bool { +[](nsINode& aNode, void*) -> bool {
return static_cast<HTMLEditor*>(aSelf)->IsEditable(&aNode); return EditorUtils::IsEditableContent(*aNode.AsText(),
EditorType::HTML);
}, },
textNodes, this); textNodes);
// now that I have a list of text nodes, collapse adjacent text nodes // now that I have a list of text nodes, collapse adjacent text nodes
// NOTE: assumption that JoinNodes keeps the righthand node // NOTE: assumption that JoinNodes keeps the righthand node
@ -4108,24 +4109,28 @@ nsIContent* HTMLEditor::GetPriorHTMLSibling(nsINode* aNode,
SkipWhitespace aSkipWS) const { SkipWhitespace aSkipWS) const {
MOZ_ASSERT(aNode); MOZ_ASSERT(aNode);
nsIContent* node = aNode->GetPreviousSibling(); nsIContent* content = aNode->GetPreviousSibling();
while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) { while (content &&
node = node->GetPreviousSibling(); (!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
SkippableWhitespace(content, aSkipWS))) {
content = content->GetPreviousSibling();
} }
return node; return content;
} }
nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode, nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode,
SkipWhitespace aSkipWS) const { SkipWhitespace aSkipWS) const {
MOZ_ASSERT(aNode); MOZ_ASSERT(aNode);
nsIContent* node = aNode->GetNextSibling(); nsIContent* content = aNode->GetNextSibling();
while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) { while (content &&
node = node->GetNextSibling(); (!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
SkippableWhitespace(content, aSkipWS))) {
content = content->GetNextSibling();
} }
return node; return content;
} }
nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal( nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal(
@ -4225,28 +4230,25 @@ bool HTMLEditor::IsLastEditableChild(nsINode* aNode) const {
} }
nsIContent* HTMLEditor::GetFirstEditableChild(nsINode& aNode) const { nsIContent* HTMLEditor::GetFirstEditableChild(nsINode& aNode) const {
nsCOMPtr<nsIContent> child = aNode.GetFirstChild(); nsIContent* child = aNode.GetFirstChild();
while (child && !EditorUtils::IsEditableContent(*child, EditorType::HTML)) {
while (child && !IsEditable(child)) {
child = child->GetNextSibling(); child = child->GetNextSibling();
} }
return child; return child;
} }
nsIContent* HTMLEditor::GetLastEditableChild(nsINode& aNode) const { nsIContent* HTMLEditor::GetLastEditableChild(nsINode& aNode) const {
nsCOMPtr<nsIContent> child = aNode.GetLastChild(); nsIContent* child = aNode.GetLastChild();
while (child && !EditorUtils::IsEditableContent(*child, EditorType::HTML)) {
while (child && !IsEditable(child)) {
child = child->GetPreviousSibling(); child = child->GetPreviousSibling();
} }
return child; return child;
} }
nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const { nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const {
nsCOMPtr<nsIContent> child = GetLeftmostChild(&aNode); nsIContent* child = GetLeftmostChild(&aNode);
while (child && (!IsEditable(child) || child->HasChildren())) { while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) ||
child->HasChildren())) {
child = GetNextEditableHTMLNode(*child); child = GetNextEditableHTMLNode(*child);
// Only accept nodes that are descendants of aNode // Only accept nodes that are descendants of aNode
@ -4260,7 +4262,8 @@ nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const {
nsIContent* HTMLEditor::GetLastEditableLeaf(nsINode& aNode) const { nsIContent* HTMLEditor::GetLastEditableLeaf(nsINode& aNode) const {
nsCOMPtr<nsIContent> child = GetRightmostChild(&aNode, false); nsCOMPtr<nsIContent> child = GetRightmostChild(&aNode, false);
while (child && (!IsEditable(child) || child->HasChildren())) { while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) ||
child->HasChildren())) {
child = GetPreviousEditableHTMLNode(*child); child = GetPreviousEditableHTMLNode(*child);
// Only accept nodes that are descendants of aNode // Only accept nodes that are descendants of aNode
@ -4370,7 +4373,7 @@ bool HTMLEditor::IsEmptyNodeImpl(nsINode& aNode, bool aSingleBRDoesntCount,
for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild(); child; for (nsCOMPtr<nsIContent> child = aNode.GetFirstChild(); child;
child = child->GetNextSibling()) { child = child->GetNextSibling()) {
// Is the child editable and non-empty? if so, return false // Is the child editable and non-empty? if so, return false
if (EditorBase::IsEditable(child)) { if (EditorUtils::IsEditableContent(*child, EditorType::HTML)) {
if (Text* text = child->GetAsText()) { if (Text* text = child->GetAsText()) {
// break out if we find we aren't empty // break out if we find we aren't empty
if (!(aSafeToAskFrames ? !IsInVisibleTextFrames(*text) if (!(aSafeToAskFrames ? !IsInVisibleTextFrames(*text)
@ -4690,7 +4693,8 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
if (NS_WARN_IF(!node)) { if (NS_WARN_IF(!node)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (node->IsContent() && IsEditable(node)) { if (node->IsContent() && EditorUtils::IsEditableContent(
*node->AsContent(), EditorType::HTML)) {
arrayOfContents.AppendElement(*node->AsContent()); arrayOfContents.AppendElement(*node->AsContent());
} }
} }
@ -4702,7 +4706,8 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
// If start node is a text node, set background color of its parent // If start node is a text node, set background color of its parent
// block. // block.
if (startOfRange.IsInTextNode() && if (startOfRange.IsInTextNode() &&
IsEditable(startOfRange.GetContainer())) { EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
EditorType::HTML)) {
RefPtr<Element> blockParent = RefPtr<Element> blockParent =
GetBlockNodeParent(startOfRange.GetContainer()); GetBlockNodeParent(startOfRange.GetContainer());
if (blockParent && handledBlockParent != blockParent) { if (blockParent && handledBlockParent != blockParent) {
@ -4731,7 +4736,9 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
// Finally, if end node is a text node, set background color of its // Finally, if end node is a text node, set background color of its
// parent block. // parent block.
if (endOfRange.IsInTextNode() && IsEditable(endOfRange.GetContainer())) { if (endOfRange.IsInTextNode() &&
EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
EditorType::HTML)) {
RefPtr<Element> blockParent = RefPtr<Element> blockParent =
GetBlockNodeParent(endOfRange.GetContainer()); GetBlockNodeParent(endOfRange.GetContainer());
if (blockParent && handledBlockParent != blockParent) { if (blockParent && handledBlockParent != blockParent) {
@ -4801,7 +4808,7 @@ nsresult HTMLEditor::CopyLastEditableChildStylesWithTransaction(
// First, clear out aNewBlock. Contract is that we want only the styles // First, clear out aNewBlock. Contract is that we want only the styles
// from aPreviousBlock. // from aPreviousBlock.
for (nsCOMPtr<nsINode> child = newBlock->GetFirstChild(); child; for (nsCOMPtr<nsIContent> child = newBlock->GetFirstChild(); child;
child = newBlock->GetFirstChild()) { child = newBlock->GetFirstChild()) {
nsresult rv = DeleteNodeWithTransaction(*child); nsresult rv = DeleteNodeWithTransaction(*child);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/ComposerCommandsUpdater.h" #include "mozilla/ComposerCommandsUpdater.h"
#include "mozilla/CSSEditUtils.h" #include "mozilla/CSSEditUtils.h"
#include "mozilla/EditorUtils.h"
#include "mozilla/ManualNAC.h" #include "mozilla/ManualNAC.h"
#include "mozilla/StyleSheet.h" #include "mozilla/StyleSheet.h"
#include "mozilla/TextEditor.h" #include "mozilla/TextEditor.h"
@ -685,13 +686,13 @@ class HTMLEditor final : public TextEditor,
EDirection aAction, EStripWrappers aStripWrappers) override; EDirection aAction, EStripWrappers aStripWrappers) override;
/** /**
* DeleteNodeWithTransaction() removes aNode from the DOM tree if it's * DeleteNodeWithTransaction() removes aContent from the DOM tree if it's
* modifiable. Note that this is not an override of same method of * modifiable. Note that this is not an override of same method of
* EditorBase. * EditorBase.
* *
* @param aNode The node to be removed from the DOM tree. * @param aContent The node to be removed from the DOM tree.
*/ */
MOZ_CAN_RUN_SCRIPT nsresult DeleteNodeWithTransaction(nsINode& aNode); MOZ_CAN_RUN_SCRIPT nsresult DeleteNodeWithTransaction(nsIContent& aContent);
/** /**
* DeleteTextWithTransaction() removes text in the range from aTextNode if * DeleteTextWithTransaction() removes text in the range from aTextNode if
@ -759,7 +760,6 @@ class HTMLEditor final : public TextEditor,
RemoveBlockContainerWithTransaction(Element& aElement); RemoveBlockContainerWithTransaction(Element& aElement);
virtual Element* GetEditorRoot() const override; virtual Element* GetEditorRoot() const override;
using EditorBase::IsEditable;
MOZ_CAN_RUN_SCRIPT virtual nsresult RemoveAttributeOrEquivalent( MOZ_CAN_RUN_SCRIPT virtual nsresult RemoveAttributeOrEquivalent(
Element* aElement, nsAtom* aAttribute, Element* aElement, nsAtom* aAttribute,
bool aSuppressTransaction) override; bool aSuppressTransaction) override;
@ -1685,8 +1685,8 @@ class HTMLEditor final : public TextEditor,
} }
bool brElementHasFound = false; bool brElementHasFound = false;
for (auto& content : aArrayOfContents) { for (OwningNonNull<nsIContent>& content : aArrayOfContents) {
if (!IsEditable(content)) { if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
continue; continue;
} }
if (content->IsHTMLElement(nsGkAtoms::br)) { if (content->IsHTMLElement(nsGkAtoms::br)) {
@ -2015,11 +2015,11 @@ class HTMLEditor final : public TextEditor,
SelectAllOfCurrentList aSelectAllOfCurrentList); SelectAllOfCurrentList aSelectAllOfCurrentList);
/** /**
* If aNode is a text node that contains only collapsed whitespace or empty * If aContent is a text node that contains only collapsed whitespace or empty
* and editable. * and editable.
*/ */
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
DeleteNodeIfInvisibleAndEditableTextNode(nsINode& aNode); DeleteNodeIfInvisibleAndEditableTextNode(nsIContent& aContent);
/** /**
* If aPoint follows invisible `<br>` element, returns the invisible `<br>` * If aPoint follows invisible `<br>` element, returns the invisible `<br>`
@ -4675,6 +4675,8 @@ class MOZ_STACK_CLASS ParagraphStateAtSelection final {
bool IsMixed() const { return mIsMixed; } bool IsMixed() const { return mIsMixed; }
private: private:
using EditorType = EditorBase::EditorType;
/** /**
* AppendDescendantFormatNodesAndFirstInlineNode() appends descendant * AppendDescendantFormatNodesAndFirstInlineNode() appends descendant
* format blocks and first inline child node in aNonFormatBlockElement to * format blocks and first inline child node in aNonFormatBlockElement to
@ -4708,13 +4710,13 @@ class MOZ_STACK_CLASS ParagraphStateAtSelection final {
} // namespace mozilla } // namespace mozilla
mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() { mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() {
return static_cast<mozilla::EditorBase*>(this)->mIsHTMLEditorClass return static_cast<mozilla::EditorBase*>(this)->IsHTMLEditor()
? static_cast<mozilla::HTMLEditor*>(this) ? static_cast<mozilla::HTMLEditor*>(this)
: nullptr; : nullptr;
} }
const mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() const { const mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() const {
return static_cast<const mozilla::EditorBase*>(this)->mIsHTMLEditorClass return static_cast<const mozilla::EditorBase*>(this)->IsHTMLEditor()
? static_cast<const mozilla::HTMLEditor*>(this) ? static_cast<const mozilla::HTMLEditor*>(this)
: nullptr; : nullptr;
} }

Просмотреть файл

@ -262,7 +262,8 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
if (NS_WARN_IF(!node)) { if (NS_WARN_IF(!node)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (node->IsContent() && IsEditable(node)) { if (node->IsContent() && EditorUtils::IsEditableContent(
*node->AsContent(), EditorType::HTML)) {
arrayOfContents.AppendElement(*node->AsContent()); arrayOfContents.AppendElement(*node->AsContent());
} }
} }
@ -270,7 +271,8 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
// If start node is a text node, apply new style to a part of it. // If start node is a text node, apply new style to a part of it.
if (startOfRange.IsInTextNode() && if (startOfRange.IsInTextNode() &&
IsEditable(startOfRange.GetContainer())) { EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
EditorType::HTML)) {
nsresult rv = SetInlinePropertyOnTextNode( nsresult rv = SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startOfRange.GetContainerAsText()), MOZ_KnownLive(*startOfRange.GetContainerAsText()),
startOfRange.Offset(), startOfRange.GetContainer()->Length(), startOfRange.Offset(), startOfRange.GetContainer()->Length(),
@ -297,7 +299,9 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
} }
// Finally, if end node is a text node, apply new style to a part ot it. // Finally, if end node is a text node, apply new style to a part ot it.
if (endOfRange.IsInTextNode() && IsEditable(endOfRange.GetContainer())) { if (endOfRange.IsInTextNode() &&
EditorUtils::IsEditableContent(*endOfRange.GetContainerAsText(),
EditorType::HTML)) {
nsresult rv = SetInlinePropertyOnTextNode( nsresult rv = SetInlinePropertyOnTextNode(
MOZ_KnownLive(*endOfRange.GetContainerAsText()), 0, MOZ_KnownLive(*endOfRange.GetContainerAsText()), 0,
endOfRange.Offset(), aProperty, aAttribute, aAttributeValue); endOfRange.Offset(), aProperty, aAttribute, aAttributeValue);
@ -497,7 +501,8 @@ nsresult HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aContent,
// Populate the list. // Populate the list.
for (nsCOMPtr<nsIContent> child = aContent.GetFirstChild(); child; for (nsCOMPtr<nsIContent> child = aContent.GetFirstChild(); child;
child = child->GetNextSibling()) { child = child->GetNextSibling()) {
if (IsEditable(child) && !IsEmptyTextNode(*child)) { if (EditorUtils::IsEditableContent(*child, EditorType::HTML) &&
!IsEmptyTextNode(*child)) {
arrayOfNodes.AppendElement(*child); arrayOfNodes.AppendElement(*child);
} }
} }
@ -649,7 +654,7 @@ nsresult HTMLEditor::SetInlinePropertyOnNode(nsIContent& aNode,
for (nsIContent* content = previousSibling ? previousSibling->GetNextSibling() for (nsIContent* content = previousSibling ? previousSibling->GetNextSibling()
: parent->GetFirstChild(); : parent->GetFirstChild();
content && content != nextSibling; content = content->GetNextSibling()) { content && content != nextSibling; content = content->GetNextSibling()) {
if (IsEditable(content)) { if (EditorUtils::IsEditableContent(*content, EditorType::HTML)) {
nodesToSet.AppendElement(*content); nodesToSet.AppendElement(*content);
} }
} }
@ -751,7 +756,8 @@ SplitNodeResult HTMLEditor::SplitAncestorStyledInlineElementsAt(
for (nsIContent* content : for (nsIContent* content :
InclusiveAncestorsOfType<nsIContent>(*aPointToSplit.GetContainer())) { InclusiveAncestorsOfType<nsIContent>(*aPointToSplit.GetContainer())) {
if (HTMLEditUtils::IsBlockElement(*content) || !content->GetParent() || if (HTMLEditUtils::IsBlockElement(*content) || !content->GetParent() ||
!IsEditable(content->GetParent())) { !EditorUtils::IsEditableContent(*content->GetParent(),
EditorType::HTML)) {
break; break;
} }
arrayOfParents.AppendElement(*content); arrayOfParents.AppendElement(*content);
@ -787,7 +793,7 @@ SplitNodeResult HTMLEditor::SplitAncestorStyledInlineElementsAt(
} }
} }
// If aProperty is nullptr, we need to split any style. // If aProperty is nullptr, we need to split any style.
else if (!IsEditable(content) || else if (!EditorUtils::IsEditableContent(content, EditorType::HTML) ||
!HTMLEditUtils::IsRemovableInlineStyleElement( !HTMLEditUtils::IsRemovableInlineStyleElement(
*content->AsElement())) { *content->AsElement())) {
continue; continue;
@ -1009,7 +1015,7 @@ nsresult HTMLEditor::RemoveStyleInside(Element& aElement, nsAtom* aProperty,
} }
// XXX Why do we check if aElement is editable only when aProperty is // XXX Why do we check if aElement is editable only when aProperty is
// nullptr? // nullptr?
else if (IsEditable(&aElement)) { else if (EditorUtils::IsEditableContent(aElement, EditorType::HTML)) {
// or removing all styles and the element is a presentation element. // or removing all styles and the element is a presentation element.
removeHTMLStyle = HTMLEditUtils::IsRemovableInlineStyleElement(aElement); removeHTMLStyle = HTMLEditUtils::IsRemovableInlineStyleElement(aElement);
} }
@ -1231,7 +1237,8 @@ nsresult HTMLEditor::PromoteInlineRange(nsRange& aRange) {
for (nsIContent* content : for (nsIContent* content :
InclusiveAncestorsOfType<nsIContent>(*aRange.GetStartContainer())) { InclusiveAncestorsOfType<nsIContent>(*aRange.GetStartContainer())) {
MOZ_ASSERT(newRangeStart.GetContainer() == content); MOZ_ASSERT(newRangeStart.GetContainer() == content);
if (content->IsHTMLElement(nsGkAtoms::body) || !IsEditable(content) || if (content->IsHTMLElement(nsGkAtoms::body) ||
!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
!IsStartOfContainerOrBeforeFirstEditableChild(newRangeStart)) { !IsStartOfContainerOrBeforeFirstEditableChild(newRangeStart)) {
break; break;
} }
@ -1248,7 +1255,8 @@ nsresult HTMLEditor::PromoteInlineRange(nsRange& aRange) {
for (nsIContent* content : for (nsIContent* content :
InclusiveAncestorsOfType<nsIContent>(*aRange.GetEndContainer())) { InclusiveAncestorsOfType<nsIContent>(*aRange.GetEndContainer())) {
MOZ_ASSERT(newRangeEnd.GetContainer() == content); MOZ_ASSERT(newRangeEnd.GetContainer() == content);
if (content->IsHTMLElement(nsGkAtoms::body) || !IsEditable(content) || if (content->IsHTMLElement(nsGkAtoms::body) ||
!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
!IsEndOfContainerOrEqualsOrAfterLastEditableChild(newRangeEnd)) { !IsEndOfContainerOrEqualsOrAfterLastEditableChild(newRangeEnd)) {
break; break;
} }
@ -1399,8 +1407,9 @@ nsresult HTMLEditor::GetInlinePropertyBase(nsAtom& aHTMLProperty,
} }
// just ignore any non-editable nodes // just ignore any non-editable nodes
if (content->GetAsText() && if (content->IsText() &&
(!IsEditable(content) || IsEmptyTextNode(*content))) { (!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
IsEmptyTextNode(*content))) {
continue; continue;
} }
if (content->GetAsText()) { if (content->GetAsText()) {
@ -1837,18 +1846,21 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents; AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
if (startOfRange.GetContainer() == endOfRange.GetContainer() && if (startOfRange.GetContainer() == endOfRange.GetContainer() &&
startOfRange.IsInTextNode()) { startOfRange.IsInTextNode()) {
if (!IsEditable(startOfRange.GetContainer())) { if (!EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
EditorType::HTML)) {
continue; continue;
} }
arrayOfContents.AppendElement(*startOfRange.GetContainerAsText()); arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
} else if (startOfRange.IsInTextNode() && endOfRange.IsInTextNode() && } else if (startOfRange.IsInTextNode() && endOfRange.IsInTextNode() &&
startOfRange.GetContainer()->GetNextSibling() == startOfRange.GetContainer()->GetNextSibling() ==
endOfRange.GetContainer()) { endOfRange.GetContainer()) {
if (IsEditable(startOfRange.GetContainer())) { if (EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
arrayOfContents.AppendElement(*startOfRange.GetContainerAsText()); EditorType::HTML)) {
arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
} }
if (IsEditable(endOfRange.GetContainer())) { if (EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
arrayOfContents.AppendElement(*endOfRange.GetContainerAsText()); EditorType::HTML)) {
arrayOfContents.AppendElement(*endOfRange.ContainerAsText());
} }
if (arrayOfContents.IsEmpty()) { if (arrayOfContents.IsEmpty()) {
continue; continue;
@ -1857,8 +1869,9 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
// Append first node if it's a text node but selected not entirely. // Append first node if it's a text node but selected not entirely.
if (startOfRange.IsInTextNode() && if (startOfRange.IsInTextNode() &&
!startOfRange.IsStartOfContainer() && !startOfRange.IsStartOfContainer() &&
IsEditable(startOfRange.GetContainer())) { EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
arrayOfContents.AppendElement(*startOfRange.GetContainerAsText()); EditorType::HTML)) {
arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
} }
// Append all entirely selected nodes. // Append all entirely selected nodes.
ContentSubtreeIterator subtreeIter; ContentSubtreeIterator subtreeIter;
@ -1868,7 +1881,9 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
if (NS_WARN_IF(!node)) { if (NS_WARN_IF(!node)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (node->IsContent() && IsEditable(node)) { if (node->IsContent() &&
EditorUtils::IsEditableContent(*node->AsContent(),
EditorType::HTML)) {
arrayOfContents.AppendElement(*node->AsContent()); arrayOfContents.AppendElement(*node->AsContent());
} }
} }
@ -1876,8 +1891,9 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
// Append last node if it's a text node but selected not entirely. // Append last node if it's a text node but selected not entirely.
if (startOfRange.GetContainer() != endOfRange.GetContainer() && if (startOfRange.GetContainer() != endOfRange.GetContainer() &&
endOfRange.IsInTextNode() && !endOfRange.IsEndOfContainer() && endOfRange.IsInTextNode() && !endOfRange.IsEndOfContainer() &&
IsEditable(endOfRange.GetContainer())) { EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
arrayOfContents.AppendElement(*endOfRange.GetContainerAsText()); EditorType::HTML)) {
arrayOfContents.AppendElement(*endOfRange.ContainerAsText());
} }
} }
@ -2156,25 +2172,25 @@ nsresult HTMLEditor::RelativeFontChange(FontSize aDir) {
// Iterate range and build up array // Iterate range and build up array
ContentSubtreeIterator subtreeIter; ContentSubtreeIterator subtreeIter;
if (NS_SUCCEEDED(subtreeIter.Init(range))) { if (NS_SUCCEEDED(subtreeIter.Init(range))) {
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes; nsTArray<OwningNonNull<nsIContent>> arrayOfContents;
for (; !subtreeIter.IsDone(); subtreeIter.Next()) { for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
if (NS_WARN_IF(!subtreeIter.GetCurrentNode()->IsContent())) { if (NS_WARN_IF(!subtreeIter.GetCurrentNode()->IsContent())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
OwningNonNull<nsIContent> node = OwningNonNull<nsIContent> content =
*subtreeIter.GetCurrentNode()->AsContent(); *subtreeIter.GetCurrentNode()->AsContent();
if (IsEditable(node)) { if (EditorUtils::IsEditableContent(content, EditorType::HTML)) {
arrayOfNodes.AppendElement(node); arrayOfContents.AppendElement(content);
} }
} }
// Now that we have the list, do the font size change on each node // Now that we have the list, do the font size change on each node
for (auto& node : arrayOfNodes) { for (OwningNonNull<nsIContent>& content : arrayOfContents) {
// MOZ_KnownLive because 'arrayOfNodes' is guaranteed to keep it // MOZ_KnownLive because 'arrayOfContents' is guaranteed to keep it
// alive. // alive.
nsresult rv = RelativeFontChangeOnNode( nsresult rv = RelativeFontChangeOnNode(
aDir == FontSize::incr ? +1 : -1, MOZ_KnownLive(node)); aDir == FontSize::incr ? +1 : -1, MOZ_KnownLive(content));
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::RelativeFontChangeOnNode() failed"); NS_WARNING("HTMLEditor::RelativeFontChangeOnNode() failed");
return rv; return rv;
@ -2184,18 +2200,21 @@ nsresult HTMLEditor::RelativeFontChange(FontSize aDir) {
// Now check the start and end parents of the range to see if they need // Now check the start and end parents of the range to see if they need
// to be separately handled (they do if they are text nodes, due to how // to be separately handled (they do if they are text nodes, due to how
// the subtree iterator works - it will not have reported them). // the subtree iterator works - it will not have reported them).
if (IsTextNode(startNode) && IsEditable(startNode)) { if (IsTextNode(startNode) &&
EditorUtils::IsEditableContent(*startNode->AsText(),
EditorType::HTML)) {
nsresult rv = RelativeFontChangeOnTextNode( nsresult rv = RelativeFontChangeOnTextNode(
aDir, MOZ_KnownLive(*startNode->GetAsText()), range->StartOffset(), aDir, MOZ_KnownLive(*startNode->AsText()), range->StartOffset(),
startNode->Length()); startNode->Length());
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::RelativeFontChangeOnTextNode() failed"); NS_WARNING("HTMLEditor::RelativeFontChangeOnTextNode() failed");
return rv; return rv;
} }
} }
if (IsTextNode(endNode) && IsEditable(endNode)) { if (IsTextNode(endNode) && EditorUtils::IsEditableContent(
*endNode->AsText(), EditorType::HTML)) {
nsresult rv = RelativeFontChangeOnTextNode( nsresult rv = RelativeFontChangeOnTextNode(
aDir, MOZ_KnownLive(*endNode->GetAsText()), 0, range->EndOffset()); aDir, MOZ_KnownLive(*endNode->AsText()), 0, range->EndOffset());
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::RelativeFontChangeOnTextNode() failed"); NS_WARNING("HTMLEditor::RelativeFontChangeOnTextNode() failed");
return rv; return rv;

Просмотреть файл

@ -5,6 +5,7 @@
#include "JoinNodeTransaction.h" #include "JoinNodeTransaction.h"
#include "HTMLEditUtils.h"
#include "mozilla/EditorBase.h" // for EditorBase #include "mozilla/EditorBase.h" // for EditorBase
#include "mozilla/dom/Text.h" #include "mozilla/dom/Text.h"
#include "nsAString.h" #include "nsAString.h"
@ -49,7 +50,8 @@ bool JoinNodeTransaction::CanDoIt() const {
NS_WARN_IF(!mEditorBase) || !mLeftContent->GetParentNode()) { NS_WARN_IF(!mEditorBase) || !mLeftContent->GetParentNode()) {
return false; return false;
} }
return mEditorBase->IsModifiableNode(*mLeftContent->GetParentNode()); return mEditorBase->IsTextEditor() ||
HTMLEditUtils::IsRemovableFromParentNode(*mLeftContent);
} }
// After DoTransaction() and RedoTransaction(), the left node is removed from // After DoTransaction() and RedoTransaction(), the left node is removed from

Просмотреть файл

@ -755,7 +755,7 @@ nsresult TextEditor::DeleteSelectionAsSubAction(EDirection aDirectionAndAmount,
if (atNewStartOfSelection.IsInTextNode() && if (atNewStartOfSelection.IsInTextNode() &&
!atNewStartOfSelection.GetContainer()->Length()) { !atNewStartOfSelection.GetContainer()->Length()) {
nsresult rv = DeleteNodeWithTransaction( nsresult rv = DeleteNodeWithTransaction(
MOZ_KnownLive(*atNewStartOfSelection.GetContainer())); MOZ_KnownLive(*atNewStartOfSelection.ContainerAsText()));
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed"); NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
return rv; return rv;
@ -1487,9 +1487,11 @@ NS_IMETHODIMP TextEditor::GetTextLength(int32_t* aCount) {
DebugOnly<nsresult> rvIgnored = postOrderIter.Init(rootElement); DebugOnly<nsresult> rvIgnored = postOrderIter.Init(rootElement);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"PostContentIterator::Init() failed, but ignored"); "PostContentIterator::Init() failed, but ignored");
EditorType editorType = GetEditorType();
for (; !postOrderIter.IsDone(); postOrderIter.Next()) { for (; !postOrderIter.IsDone(); postOrderIter.Next()) {
nsINode* currentNode = postOrderIter.GetCurrentNode(); nsINode* currentNode = postOrderIter.GetCurrentNode();
if (IsTextNode(currentNode) && IsEditable(currentNode)) { if (IsTextNode(currentNode) &&
EditorUtils::IsEditableContent(*currentNode->AsText(), editorType)) {
totalLength += currentNode->Length(); totalLength += currentNode->Length();
} }
} }
@ -1566,7 +1568,7 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
// performance hit here. // performance hit here.
nsIContent* leftMostChild = GetLeftmostChild(mRootElement); nsIContent* leftMostChild = GetLeftmostChild(mRootElement);
if (leftMostChild && if (leftMostChild &&
EditorBase::IsPaddingBRElementForEmptyEditor(*leftMostChild)) { EditorUtils::IsPaddingBRElementForEmptyEditor(*leftMostChild)) {
mPaddingBRElementForEmptyEditor = mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(leftMostChild); static_cast<HTMLBRElement*>(leftMostChild);
} else { } else {
@ -1649,7 +1651,7 @@ nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
Element* brElement = Element* brElement =
nodeList->Length() == 1 ? nodeList->Item(0) : nullptr; nodeList->Length() == 1 ? nodeList->Item(0) : nullptr;
if (brElement && if (brElement &&
EditorBase::IsPaddingBRElementForEmptyEditor(*brElement)) { EditorUtils::IsPaddingBRElementForEmptyEditor(*brElement)) {
mPaddingBRElementForEmptyEditor = mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(brElement); static_cast<HTMLBRElement*>(brElement);
} else { } else {

Просмотреть файл

@ -669,14 +669,14 @@ nsIContent* WSRunScanner::GetEditableBlockParentOrTopmotEditableInlineContent(
if (NS_WARN_IF(!aContent)) { if (NS_WARN_IF(!aContent)) {
return nullptr; return nullptr;
} }
NS_ASSERTION(mHTMLEditor->IsEditable(aContent), NS_ASSERTION(EditorUtils::IsEditableContent(*aContent, EditorType::HTML),
"Given content is not editable"); "Given content is not editable");
// XXX What should we do if scan range crosses block boundary? Currently, // XXX What should we do if scan range crosses block boundary? Currently,
// it's not collapsed only when inserting composition string so that // it's not collapsed only when inserting composition string so that
// it's possible but shouldn't occur actually. // it's possible but shouldn't occur actually.
nsIContent* editableBlockParentOrTopmotEditableInlineContent = nullptr; nsIContent* editableBlockParentOrTopmotEditableInlineContent = nullptr;
for (nsIContent* content : InclusiveAncestorsOfType<nsIContent>(*aContent)) { for (nsIContent* content : InclusiveAncestorsOfType<nsIContent>(*aContent)) {
if (!mHTMLEditor->IsEditable(content)) { if (!EditorUtils::IsEditableContent(*content, EditorType::HTML)) {
break; break;
} }
editableBlockParentOrTopmotEditableInlineContent = content; editableBlockParentOrTopmotEditableInlineContent = content;

Просмотреть файл

@ -412,6 +412,8 @@ class MOZ_STACK_CLASS WSRunScanner {
Element* GetEditingHost() const { return mEditingHost; } Element* GetEditingHost() const { return mEditingHost; }
protected: protected:
using EditorType = EditorBase::EditorType;
// WSFragment represents a single run of ws (all leadingws, or all normalws, // WSFragment represents a single run of ws (all leadingws, or all normalws,
// or all trailingws, or all leading+trailingws). Note that this single run // or all trailingws, or all leading+trailingws). Note that this single run
// may still span multiple nodes. // may still span multiple nodes.