зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
72d50388f6
Коммит
4cc133b568
|
@ -4,6 +4,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DeleteNodeTransaction.h"
|
||||
|
||||
#include "HTMLEditUtils.h"
|
||||
#include "mozilla/EditorBase.h"
|
||||
#include "mozilla/SelectionState.h" // RangeUpdater
|
||||
#include "nsDebug.h"
|
||||
|
@ -40,10 +42,11 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
|||
|
||||
bool DeleteNodeTransaction::CanDoIt() const {
|
||||
if (NS_WARN_IF(!mContentToDelete) || NS_WARN_IF(!mEditorBase) ||
|
||||
!mParentNode || !mEditorBase->IsModifiableNode(*mParentNode)) {
|
||||
!mParentNode) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return mEditorBase->IsTextEditor() ||
|
||||
HTMLEditUtils::IsSimplyEditableNode(*mParentNode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() {
|
||||
|
@ -51,7 +54,7 @@ NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mEditorBase->AsHTMLEditor() && mContentToDelete->IsText()) {
|
||||
if (mEditorBase->IsTextEditor() && mContentToDelete->IsText()) {
|
||||
uint32_t length = mContentToDelete->AsText()->TextLength();
|
||||
if (length > 0) {
|
||||
mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length);
|
||||
|
@ -93,7 +96,7 @@ DeleteNodeTransaction::UndoTransaction() {
|
|||
NS_WARNING("nsINode::InsertBefore() failed");
|
||||
return error.StealNSResult();
|
||||
}
|
||||
if (!editorBase->AsHTMLEditor() && contentToDelete->IsText()) {
|
||||
if (editorBase->IsTextEditor() && contentToDelete->IsText()) {
|
||||
uint32_t length = contentToDelete->AsText()->TextLength();
|
||||
if (length > 0) {
|
||||
nsresult rv = MOZ_KnownLive(editorBase->AsTextEditor())
|
||||
|
@ -113,7 +116,7 @@ NS_IMETHODIMP DeleteNodeTransaction::RedoTransaction() {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mEditorBase->AsHTMLEditor() && mContentToDelete->IsText()) {
|
||||
if (mEditorBase->IsTextEditor() && mContentToDelete->IsText()) {
|
||||
uint32_t length = mContentToDelete->AsText()->TextLength();
|
||||
if (length > 0) {
|
||||
mEditorBase->AsTextEditor()->WillDeleteText(length, 0, length);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "DeleteTextTransaction.h"
|
||||
|
||||
#include "HTMLEditUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/EditorBase.h"
|
||||
#include "mozilla/EditorDOMPoint.h"
|
||||
|
@ -94,7 +95,8 @@ bool DeleteTextTransaction::CanDoIt() const {
|
|||
if (NS_WARN_IF(!mTextNode) || NS_WARN_IF(!mEditorBase)) {
|
||||
return false;
|
||||
}
|
||||
return mEditorBase->IsModifiableNode(*mTextNode);
|
||||
return mEditorBase->IsTextEditor() ||
|
||||
HTMLEditUtils::IsSimplyEditableNode(*mTextNode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP DeleteTextTransaction::DoTransaction() {
|
||||
|
|
|
@ -639,11 +639,13 @@ bool EditorBase::IsSelectionEditable() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!mIsHTMLEditorClass) {
|
||||
if (IsTextEditor()) {
|
||||
// XXX we just check that the anchor node is editable at the moment
|
||||
// we should check that all nodes in the selection are editable
|
||||
nsCOMPtr<nsINode> anchorNode = SelectionRefPtr()->GetAnchorNode();
|
||||
return anchorNode && IsEditable(anchorNode);
|
||||
return anchorNode && anchorNode->IsContent() &&
|
||||
EditorUtils::IsEditableContent(*anchorNode->AsContent(),
|
||||
GetEditorType());
|
||||
}
|
||||
|
||||
nsINode* anchorNode = SelectionRefPtr()->GetAnchorNode();
|
||||
|
@ -1788,7 +1790,7 @@ nsresult EditorBase::JoinNodesWithTransaction(nsINode& aLeftNode,
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1800,15 +1802,14 @@ NS_IMETHODIMP EditorBase::DeleteNode(nsINode* aNode) {
|
|||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
|
||||
rv = DeleteNodeWithTransaction(*aNode);
|
||||
rv = DeleteNodeWithTransaction(MOZ_KnownLive(*aNode->AsContent()));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"EditorBase::DeleteNodeWithTransaction() failed");
|
||||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
|
||||
nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) {
|
||||
nsresult EditorBase::DeleteNodeWithTransaction(nsIContent& aContent) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
MOZ_ASSERT(aNode.IsContent());
|
||||
|
||||
IgnoredErrorResult ignoredError;
|
||||
AutoEditSubActionNotifier startToHandleEditSubAction(
|
||||
|
@ -1821,13 +1822,13 @@ nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) {
|
|||
"TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
|
||||
|
||||
if (AsHTMLEditor()) {
|
||||
TopLevelEditSubActionDataRef().WillDeleteContent(*this, *aNode.AsContent());
|
||||
TopLevelEditSubActionDataRef().WillDeleteContent(*this, aContent);
|
||||
}
|
||||
|
||||
// FYI: DeleteNodeTransaction grabs aNode while it's alive. So, it's safe
|
||||
// to refer aNode even after calling DoTransaction().
|
||||
// FYI: DeleteNodeTransaction grabs aContent while it's alive. So, it's safe
|
||||
// to refer aContent even after calling DoTransaction().
|
||||
RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
|
||||
DeleteNodeTransaction::MaybeCreate(*this, *aNode.AsContent());
|
||||
DeleteNodeTransaction::MaybeCreate(*this, aContent);
|
||||
NS_WARNING_ASSERTION(deleteNodeTransaction,
|
||||
"DeleteNodeTransaction::MaybeCreate() failed");
|
||||
nsresult rv;
|
||||
|
@ -1838,7 +1839,7 @@ nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) {
|
|||
|
||||
if (mTextServicesDocument && NS_SUCCEEDED(rv)) {
|
||||
RefPtr<TextServicesDocument> textServicesDocument = mTextServicesDocument;
|
||||
textServicesDocument->DidDeleteNode(&aNode);
|
||||
textServicesDocument->DidDeleteNode(&aContent);
|
||||
}
|
||||
} else {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
|
@ -1847,7 +1848,7 @@ nsresult EditorBase::DeleteNodeWithTransaction(nsINode& aNode) {
|
|||
if (!mActionListeners.IsEmpty()) {
|
||||
AutoActionListenerArray listeners(mActionListeners);
|
||||
for (auto& listener : listeners) {
|
||||
DebugOnly<nsresult> rvIgnored = listener->DidDeleteNode(&aNode, rv);
|
||||
DebugOnly<nsresult> rvIgnored = listener->DidDeleteNode(&aContent, rv);
|
||||
NS_WARNING_ASSERTION(
|
||||
NS_SUCCEEDED(rvIgnored),
|
||||
"nsIEditActionListener::DidDeleteNode() failed, but ignored");
|
||||
|
@ -3095,12 +3096,13 @@ nsresult EditorBase::InsertTextIntoTextNodeWithTransaction(
|
|||
nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) {
|
||||
MOZ_ASSERT(aRoot);
|
||||
|
||||
nsIContent* node = GetLeftmostChild(aRoot);
|
||||
if (node && !IsEditable(node)) {
|
||||
node = GetNextEditableNode(*node);
|
||||
EditorType editorType = GetEditorType();
|
||||
nsIContent* content = GetLeftmostChild(aRoot);
|
||||
if (content && !EditorUtils::IsEditableContent(*content, editorType)) {
|
||||
content = GetNextEditableNode(*content);
|
||||
}
|
||||
|
||||
return (node != aRoot) ? node : nullptr;
|
||||
return (content != aRoot) ? content : nullptr;
|
||||
}
|
||||
|
||||
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
|
||||
// and want the deep-right child.
|
||||
nsIContent* rightMostNode =
|
||||
nsIContent* rightMostContent =
|
||||
GetRightmostChild(aPoint.GetContainer(), aNoBlockCrossing);
|
||||
if (!rightMostNode) {
|
||||
if (!rightMostContent) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((!aFindEditableNode || IsEditable(rightMostNode)) &&
|
||||
(aFindAnyDataNode || IsElementOrText(*rightMostNode))) {
|
||||
return rightMostNode;
|
||||
if ((!aFindEditableNode ||
|
||||
EditorUtils::IsEditableContent(*rightMostContent, GetEditorType())) &&
|
||||
(aFindAnyDataNode || EditorUtils::IsElementOrText(*rightMostContent))) {
|
||||
return rightMostContent;
|
||||
}
|
||||
|
||||
// restart the search from the non-editable node we just found
|
||||
return GetPreviousNodeInternal(*rightMostNode, aFindEditableNode,
|
||||
return GetPreviousNodeInternal(*rightMostContent, aFindEditableNode,
|
||||
aFindAnyDataNode, aNoBlockCrossing);
|
||||
}
|
||||
|
||||
|
@ -3848,23 +3851,24 @@ nsIContent* EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint,
|
|||
return point.GetChild();
|
||||
}
|
||||
|
||||
nsIContent* leftMostNode =
|
||||
nsIContent* leftMostContent =
|
||||
GetLeftmostChild(point.GetChild(), aNoBlockCrossing);
|
||||
if (!leftMostNode) {
|
||||
if (!leftMostContent) {
|
||||
return point.GetChild();
|
||||
}
|
||||
|
||||
if (!IsDescendantOfEditorRoot(leftMostNode)) {
|
||||
if (!IsDescendantOfEditorRoot(leftMostContent)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((!aFindEditableNode || IsEditable(leftMostNode)) &&
|
||||
(aFindAnyDataNode || IsElementOrText(*leftMostNode))) {
|
||||
return leftMostNode;
|
||||
if ((!aFindEditableNode ||
|
||||
EditorUtils::IsEditableContent(*leftMostContent, GetEditorType())) &&
|
||||
(aFindAnyDataNode || EditorUtils::IsElementOrText(*leftMostContent))) {
|
||||
return leftMostContent;
|
||||
}
|
||||
|
||||
// restart the search from the non-editable node we just found
|
||||
return GetNextNodeInternal(*leftMostNode, aFindEditableNode,
|
||||
return GetNextNodeInternal(*leftMostContent, aFindEditableNode,
|
||||
aFindAnyDataNode, aNoBlockCrossing);
|
||||
}
|
||||
|
||||
|
@ -3941,15 +3945,16 @@ nsIContent* EditorBase::FindNode(nsINode* aCurrentNode, bool aGoForward,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> candidate =
|
||||
nsIContent* candidate =
|
||||
FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing);
|
||||
|
||||
if (!candidate) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((!aEditableNode || IsEditable(candidate)) &&
|
||||
(aFindAnyDataNode || IsElementOrText(*candidate))) {
|
||||
if ((!aEditableNode ||
|
||||
EditorUtils::IsEditableContent(*candidate, GetEditorType())) &&
|
||||
(aFindAnyDataNode || EditorUtils::IsElementOrText(*candidate))) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
|
@ -4084,9 +4089,10 @@ bool EditorBase::IsContainer(nsINode* aNode) const {
|
|||
uint32_t EditorBase::CountEditableChildren(nsINode* aNode) {
|
||||
MOZ_ASSERT(aNode);
|
||||
uint32_t count = 0;
|
||||
EditorType editorType = GetEditorType();
|
||||
for (nsIContent* child = aNode->GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
if (IsEditable(child)) {
|
||||
if (EditorUtils::IsEditableContent(*child, editorType)) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
@ -4342,6 +4348,7 @@ EditorDOMPoint EditorBase::JoinNodesDeepWithTransaction(
|
|||
nsCOMPtr<nsINode> parentNode = aRightNode.GetParentNode();
|
||||
|
||||
EditorDOMPoint ret;
|
||||
EditorType editorType = GetEditorType();
|
||||
while (leftNodeToJoin && rightNodeToJoin && parentNode &&
|
||||
AreNodesSameType(*leftNodeToJoin, *rightNodeToJoin)) {
|
||||
uint32_t length = leftNodeToJoin->Length();
|
||||
|
@ -4370,14 +4377,16 @@ EditorDOMPoint EditorBase::JoinNodesDeepWithTransaction(
|
|||
}
|
||||
|
||||
// Skip over non-editable nodes
|
||||
while (leftNodeToJoin && !IsEditable(leftNodeToJoin)) {
|
||||
while (leftNodeToJoin &&
|
||||
!EditorUtils::IsEditableContent(*leftNodeToJoin, editorType)) {
|
||||
leftNodeToJoin = leftNodeToJoin->GetPreviousSibling();
|
||||
}
|
||||
if (!leftNodeToJoin) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (rightNodeToJoin && !IsEditable(rightNodeToJoin)) {
|
||||
while (rightNodeToJoin &&
|
||||
!EditorUtils::IsEditableContent(*rightNodeToJoin, editorType)) {
|
||||
rightNodeToJoin = rightNodeToJoin->GetNextSibling();
|
||||
}
|
||||
if (!rightNodeToJoin) {
|
||||
|
@ -4440,11 +4449,14 @@ nsresult EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() {
|
|||
// 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
|
||||
// padding <br> element.
|
||||
bool isRootEditable = IsEditable(rootElement);
|
||||
EditorType editorType = GetEditorType();
|
||||
bool isRootEditable =
|
||||
EditorUtils::IsEditableContent(*rootElement, editorType);
|
||||
for (nsIContent* rootChild = rootElement->GetFirstChild(); rootChild;
|
||||
rootChild = rootChild->GetNextSibling()) {
|
||||
if (EditorBase::IsPaddingBRElementForEmptyEditor(*rootChild) ||
|
||||
!isRootEditable || IsEditable(rootChild) ||
|
||||
if (EditorUtils::IsPaddingBRElementForEmptyEditor(*rootChild) ||
|
||||
!isRootEditable ||
|
||||
EditorUtils::IsEditableContent(*rootChild, editorType) ||
|
||||
HTMLEditUtils::IsBlockElement(*rootChild)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -4452,7 +4464,7 @@ nsresult EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() {
|
|||
|
||||
// Skip adding the padding <br> element for empty editor if body
|
||||
// is read-only.
|
||||
if (!IsModifiableNode(*rootElement)) {
|
||||
if (IsHTMLEditor() && !HTMLEditUtils::IsSimplyEditableNode(*rootElement)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -5380,10 +5392,6 @@ nsresult EditorBase::SetTextDirectionTo(TextDirection aTextDirection) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool EditorBase::IsModifiableNode(const nsINode& aNode) const {
|
||||
return !AsHTMLEditor() || aNode.IsEditable();
|
||||
}
|
||||
|
||||
nsIContent* EditorBase::GetFocusedContent() const {
|
||||
EventTarget* piTarget = GetDOMEventTarget();
|
||||
if (!piTarget) {
|
||||
|
@ -6017,7 +6025,7 @@ EditorBase::AutoEditActionDataSetter::AutoEditActionDataSetter(
|
|||
|
||||
mEditAction = aEditAction;
|
||||
mDirectionOfTopLevelEditSubAction = eNone;
|
||||
if (mEditorBase.mIsHTMLEditorClass) {
|
||||
if (mEditorBase.IsHTMLEditor()) {
|
||||
mTopLevelEditSubActionData.mSelectedRange =
|
||||
mEditorBase.AsHTMLEditor()
|
||||
->GetSelectedRangeItemForTopLevelEditSubAction();
|
||||
|
|
|
@ -154,6 +154,8 @@ class EditorBase : public nsIEditor,
|
|||
typedef dom::Selection Selection;
|
||||
typedef dom::Text Text;
|
||||
|
||||
enum class EditorType { Text, HTML };
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor)
|
||||
|
||||
|
@ -169,6 +171,9 @@ class EditorBase : public nsIEditor,
|
|||
*/
|
||||
EditorBase();
|
||||
|
||||
bool IsTextEditor() const { return !mIsHTMLEditorClass; }
|
||||
bool IsHTMLEditor() const { return mIsHTMLEditorClass; }
|
||||
|
||||
/**
|
||||
* Init is to tell the implementation of nsIEditor to begin its services
|
||||
* @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.
|
||||
bool MaybeHasMutationEventListeners(
|
||||
uint32_t aMutationEventType = 0xFFFFFFFF) const {
|
||||
if (!mIsHTMLEditorClass) {
|
||||
if (IsTextEditor()) {
|
||||
// DOM mutation event listeners cannot catch the changes of
|
||||
// <input type="text"> nor <textarea>.
|
||||
return false;
|
||||
|
@ -1410,11 +1415,11 @@ class EditorBase : public nsIEditor,
|
|||
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
|
||||
|
@ -2097,54 +2102,6 @@ class EditorBase : public nsIEditor,
|
|||
*/
|
||||
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
|
||||
* empty last line.
|
||||
|
@ -2181,11 +2138,6 @@ class EditorBase : public nsIEditor,
|
|||
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,
|
||||
* 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();
|
||||
|
||||
MOZ_ALWAYS_INLINE EditorType GetEditorType() const {
|
||||
return mIsHTMLEditorClass ? EditorType::HTML : EditorType::Text;
|
||||
}
|
||||
|
||||
int32_t WrapWidth() const { return mWrapColumn; }
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "mozilla/EditorDOMPoint.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
#include "mozilla/RangeBoundary.h"
|
||||
#include "mozilla/dom/HTMLBRElement.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/StaticRange.h"
|
||||
#include "nsAtom.h"
|
||||
|
@ -783,6 +784,8 @@ class MOZ_RAII DOMSubtreeIterator final : public DOMIterator {
|
|||
|
||||
class EditorUtils final {
|
||||
public:
|
||||
using EditorType = EditorBase::EditorType;
|
||||
|
||||
/**
|
||||
* IsDescendantOf() checks if aNode is a child or a descendant 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,
|
||||
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
|
||||
* be called only when `aText` is in a password field. This method masks
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "HTMLEditor.h"
|
||||
|
||||
#include "HTMLEditUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/PresShellInlines.h"
|
||||
|
@ -436,7 +437,8 @@ nsresult HTMLEditor::RefreshEditingUI() {
|
|||
nsIContent* hostContent = GetActiveEditingHost();
|
||||
|
||||
if (IsObjectResizerEnabled() && focusElement &&
|
||||
IsModifiableNode(*focusElement) && focusElement != hostContent) {
|
||||
HTMLEditUtils::IsSimplyEditableNode(*focusElement) &&
|
||||
focusElement != hostContent) {
|
||||
if (nsGkAtoms::img == focusTagAtom) {
|
||||
mResizedObjectIsAnImage = true;
|
||||
}
|
||||
|
@ -456,7 +458,8 @@ nsresult HTMLEditor::RefreshEditingUI() {
|
|||
}
|
||||
|
||||
if (IsAbsolutePositionEditorEnabled() && absPosElement &&
|
||||
IsModifiableNode(*absPosElement) && absPosElement != hostContent) {
|
||||
HTMLEditUtils::IsSimplyEditableNode(*absPosElement) &&
|
||||
absPosElement != hostContent) {
|
||||
if (mAbsolutelyPositionedObject) {
|
||||
nsresult rv = RefreshGrabberInternal();
|
||||
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 &&
|
||||
IsModifiableNode(*cellElement) && cellElement != hostContent) {
|
||||
HTMLEditUtils::IsSimplyEditableNode(*cellElement) &&
|
||||
cellElement != hostContent) {
|
||||
if (mInlineEditedCell) {
|
||||
nsresult rv = RefreshInlineTableEditingUIInternal();
|
||||
if (NS_FAILED(rv)) {
|
||||
|
|
|
@ -688,7 +688,7 @@ EditActionResult HTMLEditor::CanHandleHTMLEditSubAction() const {
|
|||
return EditActionResult(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
if (!IsModifiableNode(*selStartNode)) {
|
||||
if (!HTMLEditUtils::IsSimplyEditableNode(*selStartNode)) {
|
||||
return EditActionCanceled();
|
||||
}
|
||||
|
||||
|
@ -701,21 +701,22 @@ EditActionResult HTMLEditor::CanHandleHTMLEditSubAction() const {
|
|||
return EditActionIgnored();
|
||||
}
|
||||
|
||||
if (!IsModifiableNode(*selEndNode)) {
|
||||
if (!HTMLEditUtils::IsSimplyEditableNode(*selEndNode)) {
|
||||
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();
|
||||
if (!commonAncestor) {
|
||||
NS_WARNING(
|
||||
"AbstractRange::GetClosestCommonInclusiveAncestor() returned nullptr");
|
||||
return EditActionResult(NS_ERROR_FAILURE);
|
||||
}
|
||||
if (!IsModifiableNode(*commonAncestor)) {
|
||||
return EditActionCanceled();
|
||||
}
|
||||
|
||||
return EditActionIgnored();
|
||||
return HTMLEditUtils::IsSimplyEditableNode(*commonAncestor)
|
||||
? EditActionIgnored()
|
||||
: EditActionCanceled();
|
||||
}
|
||||
|
||||
ListElementSelectionState::ListElementSelectionState(HTMLEditor& aHTMLEditor,
|
||||
|
@ -1255,7 +1256,7 @@ nsresult ParagraphStateAtSelection::CollectEditableFormatNodesInSelection(
|
|||
OwningNonNull<nsIContent> content = aArrayOfContents[i];
|
||||
|
||||
// Remove all non-editable nodes. Leave them be.
|
||||
if (!aHTMLEditor.IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
aArrayOfContents.RemoveElementAt(i);
|
||||
continue;
|
||||
}
|
||||
|
@ -1823,7 +1824,8 @@ EditActionResult HTMLEditor::InsertParagraphSeparatorAsSubAction() {
|
|||
MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
|
||||
|
||||
// Do nothing if the node is read-only
|
||||
if (!IsModifiableNode(*atStartOfSelection.GetContainer())) {
|
||||
if (!HTMLEditUtils::IsSimplyEditableNode(
|
||||
*atStartOfSelection.GetContainer())) {
|
||||
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
|
||||
{
|
||||
AutoTrackDOMPoint startTracker(RangeUpdaterRef(), &atCaret);
|
||||
AutoTrackDOMPoint endTracker(RangeUpdaterRef(), &selectionEndPoint);
|
||||
|
||||
nsresult rv = DeleteNodeIfInvisibleAndEditableTextNode(
|
||||
MOZ_KnownLive(*atCaret.GetContainer()));
|
||||
MOZ_KnownLive(*atCaret.ContainerAsContent()));
|
||||
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -3495,7 +3502,7 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
|
|||
"HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode("
|
||||
") failed to remove start node, but ignored");
|
||||
rv = DeleteNodeIfInvisibleAndEditableTextNode(
|
||||
MOZ_KnownLive(*selectionEndPoint.GetContainer()));
|
||||
MOZ_KnownLive(*selectionEndPoint.ContainerAsContent()));
|
||||
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -3512,19 +3519,20 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsresult HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode(nsINode& aNode) {
|
||||
nsresult HTMLEditor::DeleteNodeIfInvisibleAndEditableTextNode(
|
||||
nsIContent& aContent) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
Text* text = aNode.GetAsText();
|
||||
Text* text = aContent.GetAsText();
|
||||
if (!text) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (IsVisibleTextNode(*text) || !IsModifiableNode(*text)) {
|
||||
if (IsVisibleTextNode(*text) || !HTMLEditUtils::IsSimplyEditableNode(*text)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = DeleteNodeWithTransaction(aNode);
|
||||
nsresult rv = DeleteNodeWithTransaction(aContent);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -4177,7 +4185,7 @@ nsresult HTMLEditor::DeleteElementsExceptTableRelatedElements(nsINode& aNode) {
|
|||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
if (!HTMLEditUtils::IsTableElementButNotTable(&aNode)) {
|
||||
nsresult rv = DeleteNodeWithTransaction(aNode);
|
||||
nsresult rv = DeleteNodeWithTransaction(MOZ_KnownLive(*aNode.AsContent()));
|
||||
if (NS_WARN_IF(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
|
||||
// list item element.
|
||||
// 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))) {
|
||||
nsresult rv = DeleteNodeWithTransaction(*content);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
|
@ -4929,7 +4937,7 @@ nsresult HTMLEditor::RemoveListAtSelectionAsSubAction() {
|
|||
// CollectNonEditableNodes::No.
|
||||
for (int32_t i = arrayOfContents.Length() - 1; i >= 0; i--) {
|
||||
OwningNonNull<nsIContent>& content = arrayOfContents[i];
|
||||
if (!IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
arrayOfContents.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
|
@ -5498,7 +5506,7 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
|
|||
|
||||
// Ignore all non-editable nodes. Leave them be.
|
||||
// XXX We ignore non-editable nodes here, but not so in the above block.
|
||||
if (!IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -5706,7 +5714,7 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
|
|||
|
||||
// Ignore all non-editable nodes. Leave them be.
|
||||
// XXX We ignore non-editable nodes here, but not so in the above block.
|
||||
if (!IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -6908,7 +6916,7 @@ nsresult HTMLEditor::AlignNodesAndDescendants(
|
|||
++indexOfTransitionList;
|
||||
|
||||
// Ignore all non-editable nodes. Leave them be.
|
||||
if (!IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -7350,7 +7358,7 @@ size_t HTMLEditor::CollectChildren(
|
|||
aIndexToInsertChildren + numberOfFoundChildren, aCollectListChildren,
|
||||
aCollectTableChildren, aCollectNonEditableNodes);
|
||||
} else if (aCollectNonEditableNodes == CollectNonEditableNodes::Yes ||
|
||||
IsEditable(content)) {
|
||||
EditorUtils::IsEditableContent(*content, EditorType::HTML)) {
|
||||
aOutArrayOfContents.InsertElementAt(
|
||||
aIndexToInsertChildren + numberOfFoundChildren++, *content);
|
||||
}
|
||||
|
@ -8155,7 +8163,8 @@ nsresult HTMLEditor::CollectEditTargetNodes(
|
|||
}
|
||||
if (aCollectNonEditableNodes == CollectNonEditableNodes::No) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -9303,7 +9312,7 @@ nsresult HTMLEditor::RemoveBlockContainerElements(
|
|||
}
|
||||
firstContent = lastContent = curBlock = nullptr;
|
||||
}
|
||||
if (!IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
// Remove current block
|
||||
|
@ -9337,7 +9346,7 @@ nsresult HTMLEditor::RemoveBlockContainerElements(
|
|||
}
|
||||
firstContent = lastContent = curBlock = nullptr;
|
||||
}
|
||||
if (!IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
// Recursion time
|
||||
|
@ -9376,7 +9385,7 @@ nsresult HTMLEditor::RemoveBlockContainerElements(
|
|||
}
|
||||
curBlock = GetBlockNodeParent(content);
|
||||
if (!curBlock || !HTMLEditUtils::IsFormatNode(curBlock) ||
|
||||
!IsEditable(curBlock)) {
|
||||
!EditorUtils::IsEditableContent(*curBlock, EditorType::HTML)) {
|
||||
// Not a block kind that we care about.
|
||||
curBlock = nullptr;
|
||||
} else {
|
||||
|
@ -9439,7 +9448,8 @@ nsresult HTMLEditor::CreateOrChangeBlockContainerElement(
|
|||
|
||||
// Is it already the right kind of block, or an uneditable block?
|
||||
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
|
||||
curBlock = nullptr;
|
||||
// Do nothing to this block
|
||||
|
@ -9605,7 +9615,8 @@ nsresult HTMLEditor::CreateOrChangeBlockContainerElement(
|
|||
// added here if that should change
|
||||
//
|
||||
// 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
|
||||
continue;
|
||||
}
|
||||
|
@ -9999,7 +10010,7 @@ nsresult HTMLEditor::InsertBRElementToEmptyListItemsAndTableCellsInRange(
|
|||
MOZ_ASSERT(Element::FromNode(&aNode));
|
||||
MOZ_ASSERT(aSelf);
|
||||
Element* element = aNode.AsElement();
|
||||
if (!static_cast<HTMLEditor*>(aSelf)->IsEditable(element) ||
|
||||
if (!EditorUtils::IsEditableContent(*element, EditorType::HTML) ||
|
||||
(!HTMLEditUtils::IsListItem(element) &&
|
||||
!HTMLEditUtils::IsTableCellOrCaption(*element))) {
|
||||
return false;
|
||||
|
@ -10175,14 +10186,15 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
|
|||
MOZ_ASSERT(SelectionRefPtr()->IsCollapsed());
|
||||
|
||||
EditorDOMPoint point(EditorBase::GetStartPoint(*SelectionRefPtr()));
|
||||
if (NS_WARN_IF(!point.IsSet())) {
|
||||
if (NS_WARN_IF(!point.IsInContentNode())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// 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());
|
||||
if (NS_WARN_IF(!point.IsSet())) {
|
||||
if (NS_WARN_IF(!point.IsInContentNode())) {
|
||||
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
|
||||
// because the block should have one-line height.
|
||||
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)) {
|
||||
Element* bodyOrDocumentElement = GetRoot();
|
||||
if (NS_WARN_IF(!bodyOrDocumentElement)) {
|
||||
|
@ -10501,7 +10515,8 @@ nsresult HTMLEditor::RemoveEmptyNodesIn(nsRange& aRange) {
|
|||
|
||||
// now delete the empty nodes
|
||||
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
|
||||
// alive.
|
||||
rv = DeleteNodeWithTransaction(MOZ_KnownLive(emptyContent));
|
||||
|
@ -11380,7 +11395,7 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
|
|||
}
|
||||
|
||||
// Ignore all non-editable nodes. Leave them be.
|
||||
if (!IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,31 @@ class nsAtom;
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
enum class EditAction;
|
||||
|
||||
class HTMLEditUtils final {
|
||||
using Element = dom::Element;
|
||||
using Selection = dom::Selection;
|
||||
|
||||
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
|
||||
* be treated as a block. (This does not refer style information.)
|
||||
|
|
|
@ -1841,7 +1841,9 @@ EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
|
|||
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
|
||||
// someone is trying to put block content in a span. So just put it
|
||||
// where we were originally asked.
|
||||
|
@ -3365,18 +3367,15 @@ nsresult HTMLEditor::DeleteSelectionWithTransaction(
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsresult HTMLEditor::DeleteNodeWithTransaction(nsINode& aNode) {
|
||||
if (NS_WARN_IF(!aNode.IsContent())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
nsresult HTMLEditor::DeleteNodeWithTransaction(nsIContent& aContent) {
|
||||
// Do nothing if the node is read-only.
|
||||
// XXX This is not a override method of EditorBase's method. This might
|
||||
// cause not called accidentally. We need to investigate this issue.
|
||||
if (NS_WARN_IF(!IsModifiableNode(*aNode.AsContent()) &&
|
||||
!EditorBase::IsPaddingBRElementForEmptyEditor(aNode))) {
|
||||
if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aContent) &&
|
||||
!EditorUtils::IsPaddingBRElementForEmptyEditor(aContent))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv = EditorBase::DeleteNodeWithTransaction(aNode);
|
||||
nsresult rv = EditorBase::DeleteNodeWithTransaction(aContent);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"EditorBase::DeleteNodeWithTransaction() failed");
|
||||
return rv;
|
||||
|
@ -3396,7 +3395,7 @@ nsresult HTMLEditor::DeleteAllChildrenWithTransaction(Element& aElement) {
|
|||
!ignoredError.Failed(),
|
||||
"OnStartToHandleTopLevelEditSubAction() failed, but ignored");
|
||||
|
||||
while (nsCOMPtr<nsINode> child = aElement.GetLastChild()) {
|
||||
while (nsCOMPtr<nsIContent> child = aElement.GetLastChild()) {
|
||||
nsresult rv = DeleteNodeWithTransaction(*child);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("HTMLEditor::DeleteNodeWithTransaction() failed");
|
||||
|
@ -3510,7 +3509,7 @@ nsresult HTMLEditor::DeleteParentBlocksWithTransactionIfEmpty(
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3522,7 +3521,7 @@ NS_IMETHODIMP HTMLEditor::DeleteNode(nsINode* aNode) {
|
|||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
|
||||
rv = DeleteNodeWithTransaction(*aNode);
|
||||
rv = DeleteNodeWithTransaction(MOZ_KnownLive(*aNode->AsContent()));
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"HTMLEditor::DeleteNodeWithTransaction() failed");
|
||||
return rv;
|
||||
|
@ -3531,7 +3530,7 @@ NS_IMETHODIMP HTMLEditor::DeleteNode(nsINode* aNode) {
|
|||
nsresult HTMLEditor::DeleteTextWithTransaction(Text& aTextNode,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength) {
|
||||
if (NS_WARN_IF(!IsModifiableNode(aTextNode))) {
|
||||
if (NS_WARN_IF(!HTMLEditUtils::IsSimplyEditableNode(aTextNode))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -3551,7 +3550,8 @@ nsresult HTMLEditor::InsertTextWithTransaction(
|
|||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -3694,7 +3694,7 @@ void HTMLEditor::DoContentInserted(nsIContent* aChild,
|
|||
}
|
||||
// We don't need to handle our own modifications
|
||||
else if (!GetTopLevelEditSubAction() && container->IsEditable()) {
|
||||
if (EditorBase::IsPaddingBRElementForEmptyEditor(*aChild)) {
|
||||
if (EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) {
|
||||
// Ignore insertion of the padding <br> element.
|
||||
return;
|
||||
}
|
||||
|
@ -3747,7 +3747,7 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY void HTMLEditor::ContentRemoved(
|
|||
// We don't need to handle our own modifications
|
||||
} else if (!GetTopLevelEditSubAction() &&
|
||||
aChild->GetParentNode()->IsEditable()) {
|
||||
if (aChild && EditorBase::IsPaddingBRElementForEmptyEditor(*aChild)) {
|
||||
if (aChild && EditorUtils::IsPaddingBRElementForEmptyEditor(*aChild)) {
|
||||
// Ignore removal of the padding <br> element for empty editor.
|
||||
return;
|
||||
}
|
||||
|
@ -3958,10 +3958,11 @@ nsresult HTMLEditor::CollapseAdjacentTextNodes(nsRange& aInRange) {
|
|||
}
|
||||
AutoTArray<OwningNonNull<Text>, 8> textNodes;
|
||||
subtreeIter.AppendNodesToArray(
|
||||
+[](nsINode& aNode, void* aSelf) -> bool {
|
||||
return static_cast<HTMLEditor*>(aSelf)->IsEditable(&aNode);
|
||||
+[](nsINode& aNode, void*) -> bool {
|
||||
return EditorUtils::IsEditableContent(*aNode.AsText(),
|
||||
EditorType::HTML);
|
||||
},
|
||||
textNodes, this);
|
||||
textNodes);
|
||||
|
||||
// now that I have a list of text nodes, collapse adjacent text nodes
|
||||
// NOTE: assumption that JoinNodes keeps the righthand node
|
||||
|
@ -4108,24 +4109,28 @@ nsIContent* HTMLEditor::GetPriorHTMLSibling(nsINode* aNode,
|
|||
SkipWhitespace aSkipWS) const {
|
||||
MOZ_ASSERT(aNode);
|
||||
|
||||
nsIContent* node = aNode->GetPreviousSibling();
|
||||
while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) {
|
||||
node = node->GetPreviousSibling();
|
||||
nsIContent* content = aNode->GetPreviousSibling();
|
||||
while (content &&
|
||||
(!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
|
||||
SkippableWhitespace(content, aSkipWS))) {
|
||||
content = content->GetPreviousSibling();
|
||||
}
|
||||
|
||||
return node;
|
||||
return content;
|
||||
}
|
||||
|
||||
nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode,
|
||||
SkipWhitespace aSkipWS) const {
|
||||
MOZ_ASSERT(aNode);
|
||||
|
||||
nsIContent* node = aNode->GetNextSibling();
|
||||
while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) {
|
||||
node = node->GetNextSibling();
|
||||
nsIContent* content = aNode->GetNextSibling();
|
||||
while (content &&
|
||||
(!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
|
||||
SkippableWhitespace(content, aSkipWS))) {
|
||||
content = content->GetNextSibling();
|
||||
}
|
||||
|
||||
return node;
|
||||
return content;
|
||||
}
|
||||
|
||||
nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal(
|
||||
|
@ -4225,28 +4230,25 @@ bool HTMLEditor::IsLastEditableChild(nsINode* aNode) const {
|
|||
}
|
||||
|
||||
nsIContent* HTMLEditor::GetFirstEditableChild(nsINode& aNode) const {
|
||||
nsCOMPtr<nsIContent> child = aNode.GetFirstChild();
|
||||
|
||||
while (child && !IsEditable(child)) {
|
||||
nsIContent* child = aNode.GetFirstChild();
|
||||
while (child && !EditorUtils::IsEditableContent(*child, EditorType::HTML)) {
|
||||
child = child->GetNextSibling();
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
nsIContent* HTMLEditor::GetLastEditableChild(nsINode& aNode) const {
|
||||
nsCOMPtr<nsIContent> child = aNode.GetLastChild();
|
||||
|
||||
while (child && !IsEditable(child)) {
|
||||
nsIContent* child = aNode.GetLastChild();
|
||||
while (child && !EditorUtils::IsEditableContent(*child, EditorType::HTML)) {
|
||||
child = child->GetPreviousSibling();
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const {
|
||||
nsCOMPtr<nsIContent> child = GetLeftmostChild(&aNode);
|
||||
while (child && (!IsEditable(child) || child->HasChildren())) {
|
||||
nsIContent* child = GetLeftmostChild(&aNode);
|
||||
while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) ||
|
||||
child->HasChildren())) {
|
||||
child = GetNextEditableHTMLNode(*child);
|
||||
|
||||
// Only accept nodes that are descendants of aNode
|
||||
|
@ -4260,7 +4262,8 @@ nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const {
|
|||
|
||||
nsIContent* HTMLEditor::GetLastEditableLeaf(nsINode& aNode) const {
|
||||
nsCOMPtr<nsIContent> child = GetRightmostChild(&aNode, false);
|
||||
while (child && (!IsEditable(child) || child->HasChildren())) {
|
||||
while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) ||
|
||||
child->HasChildren())) {
|
||||
child = GetPreviousEditableHTMLNode(*child);
|
||||
|
||||
// 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;
|
||||
child = child->GetNextSibling()) {
|
||||
// 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()) {
|
||||
// break out if we find we aren't empty
|
||||
if (!(aSafeToAskFrames ? !IsInVisibleTextFrames(*text)
|
||||
|
@ -4690,7 +4693,8 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
|
|||
if (NS_WARN_IF(!node)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (node->IsContent() && IsEditable(node)) {
|
||||
if (node->IsContent() && EditorUtils::IsEditableContent(
|
||||
*node->AsContent(), EditorType::HTML)) {
|
||||
arrayOfContents.AppendElement(*node->AsContent());
|
||||
}
|
||||
}
|
||||
|
@ -4702,7 +4706,8 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
|
|||
// If start node is a text node, set background color of its parent
|
||||
// block.
|
||||
if (startOfRange.IsInTextNode() &&
|
||||
IsEditable(startOfRange.GetContainer())) {
|
||||
EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
RefPtr<Element> blockParent =
|
||||
GetBlockNodeParent(startOfRange.GetContainer());
|
||||
if (blockParent && handledBlockParent != blockParent) {
|
||||
|
@ -4731,7 +4736,9 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
|
|||
|
||||
// Finally, if end node is a text node, set background color of its
|
||||
// parent block.
|
||||
if (endOfRange.IsInTextNode() && IsEditable(endOfRange.GetContainer())) {
|
||||
if (endOfRange.IsInTextNode() &&
|
||||
EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
RefPtr<Element> blockParent =
|
||||
GetBlockNodeParent(endOfRange.GetContainer());
|
||||
if (blockParent && handledBlockParent != blockParent) {
|
||||
|
@ -4801,7 +4808,7 @@ nsresult HTMLEditor::CopyLastEditableChildStylesWithTransaction(
|
|||
|
||||
// First, clear out aNewBlock. Contract is that we want only the styles
|
||||
// from aPreviousBlock.
|
||||
for (nsCOMPtr<nsINode> child = newBlock->GetFirstChild(); child;
|
||||
for (nsCOMPtr<nsIContent> child = newBlock->GetFirstChild(); child;
|
||||
child = newBlock->GetFirstChild()) {
|
||||
nsresult rv = DeleteNodeWithTransaction(*child);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ComposerCommandsUpdater.h"
|
||||
#include "mozilla/CSSEditUtils.h"
|
||||
#include "mozilla/EditorUtils.h"
|
||||
#include "mozilla/ManualNAC.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
|
@ -685,13 +686,13 @@ class HTMLEditor final : public TextEditor,
|
|||
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
|
||||
* 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
|
||||
|
@ -759,7 +760,6 @@ class HTMLEditor final : public TextEditor,
|
|||
RemoveBlockContainerWithTransaction(Element& aElement);
|
||||
|
||||
virtual Element* GetEditorRoot() const override;
|
||||
using EditorBase::IsEditable;
|
||||
MOZ_CAN_RUN_SCRIPT virtual nsresult RemoveAttributeOrEquivalent(
|
||||
Element* aElement, nsAtom* aAttribute,
|
||||
bool aSuppressTransaction) override;
|
||||
|
@ -1685,8 +1685,8 @@ class HTMLEditor final : public TextEditor,
|
|||
}
|
||||
|
||||
bool brElementHasFound = false;
|
||||
for (auto& content : aArrayOfContents) {
|
||||
if (!IsEditable(content)) {
|
||||
for (OwningNonNull<nsIContent>& content : aArrayOfContents) {
|
||||
if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
if (content->IsHTMLElement(nsGkAtoms::br)) {
|
||||
|
@ -2015,11 +2015,11 @@ class HTMLEditor final : public TextEditor,
|
|||
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.
|
||||
*/
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
|
||||
DeleteNodeIfInvisibleAndEditableTextNode(nsINode& aNode);
|
||||
DeleteNodeIfInvisibleAndEditableTextNode(nsIContent& aContent);
|
||||
|
||||
/**
|
||||
* 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; }
|
||||
|
||||
private:
|
||||
using EditorType = EditorBase::EditorType;
|
||||
|
||||
/**
|
||||
* AppendDescendantFormatNodesAndFirstInlineNode() appends descendant
|
||||
* format blocks and first inline child node in aNonFormatBlockElement to
|
||||
|
@ -4708,13 +4710,13 @@ class MOZ_STACK_CLASS ParagraphStateAtSelection final {
|
|||
} // namespace mozilla
|
||||
|
||||
mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() {
|
||||
return static_cast<mozilla::EditorBase*>(this)->mIsHTMLEditorClass
|
||||
return static_cast<mozilla::EditorBase*>(this)->IsHTMLEditor()
|
||||
? static_cast<mozilla::HTMLEditor*>(this)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
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)
|
||||
: nullptr;
|
||||
}
|
||||
|
|
|
@ -262,7 +262,8 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
|
|||
if (NS_WARN_IF(!node)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (node->IsContent() && IsEditable(node)) {
|
||||
if (node->IsContent() && EditorUtils::IsEditableContent(
|
||||
*node->AsContent(), EditorType::HTML)) {
|
||||
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 (startOfRange.IsInTextNode() &&
|
||||
IsEditable(startOfRange.GetContainer())) {
|
||||
EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
nsresult rv = SetInlinePropertyOnTextNode(
|
||||
MOZ_KnownLive(*startOfRange.GetContainerAsText()),
|
||||
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.
|
||||
if (endOfRange.IsInTextNode() && IsEditable(endOfRange.GetContainer())) {
|
||||
if (endOfRange.IsInTextNode() &&
|
||||
EditorUtils::IsEditableContent(*endOfRange.GetContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
nsresult rv = SetInlinePropertyOnTextNode(
|
||||
MOZ_KnownLive(*endOfRange.GetContainerAsText()), 0,
|
||||
endOfRange.Offset(), aProperty, aAttribute, aAttributeValue);
|
||||
|
@ -497,7 +501,8 @@ nsresult HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aContent,
|
|||
// Populate the list.
|
||||
for (nsCOMPtr<nsIContent> child = aContent.GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
if (IsEditable(child) && !IsEmptyTextNode(*child)) {
|
||||
if (EditorUtils::IsEditableContent(*child, EditorType::HTML) &&
|
||||
!IsEmptyTextNode(*child)) {
|
||||
arrayOfNodes.AppendElement(*child);
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +654,7 @@ nsresult HTMLEditor::SetInlinePropertyOnNode(nsIContent& aNode,
|
|||
for (nsIContent* content = previousSibling ? previousSibling->GetNextSibling()
|
||||
: parent->GetFirstChild();
|
||||
content && content != nextSibling; content = content->GetNextSibling()) {
|
||||
if (IsEditable(content)) {
|
||||
if (EditorUtils::IsEditableContent(*content, EditorType::HTML)) {
|
||||
nodesToSet.AppendElement(*content);
|
||||
}
|
||||
}
|
||||
|
@ -751,7 +756,8 @@ SplitNodeResult HTMLEditor::SplitAncestorStyledInlineElementsAt(
|
|||
for (nsIContent* content :
|
||||
InclusiveAncestorsOfType<nsIContent>(*aPointToSplit.GetContainer())) {
|
||||
if (HTMLEditUtils::IsBlockElement(*content) || !content->GetParent() ||
|
||||
!IsEditable(content->GetParent())) {
|
||||
!EditorUtils::IsEditableContent(*content->GetParent(),
|
||||
EditorType::HTML)) {
|
||||
break;
|
||||
}
|
||||
arrayOfParents.AppendElement(*content);
|
||||
|
@ -787,7 +793,7 @@ SplitNodeResult HTMLEditor::SplitAncestorStyledInlineElementsAt(
|
|||
}
|
||||
}
|
||||
// If aProperty is nullptr, we need to split any style.
|
||||
else if (!IsEditable(content) ||
|
||||
else if (!EditorUtils::IsEditableContent(content, EditorType::HTML) ||
|
||||
!HTMLEditUtils::IsRemovableInlineStyleElement(
|
||||
*content->AsElement())) {
|
||||
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
|
||||
// nullptr?
|
||||
else if (IsEditable(&aElement)) {
|
||||
else if (EditorUtils::IsEditableContent(aElement, EditorType::HTML)) {
|
||||
// or removing all styles and the element is a presentation element.
|
||||
removeHTMLStyle = HTMLEditUtils::IsRemovableInlineStyleElement(aElement);
|
||||
}
|
||||
|
@ -1231,7 +1237,8 @@ nsresult HTMLEditor::PromoteInlineRange(nsRange& aRange) {
|
|||
for (nsIContent* content :
|
||||
InclusiveAncestorsOfType<nsIContent>(*aRange.GetStartContainer())) {
|
||||
MOZ_ASSERT(newRangeStart.GetContainer() == content);
|
||||
if (content->IsHTMLElement(nsGkAtoms::body) || !IsEditable(content) ||
|
||||
if (content->IsHTMLElement(nsGkAtoms::body) ||
|
||||
!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
|
||||
!IsStartOfContainerOrBeforeFirstEditableChild(newRangeStart)) {
|
||||
break;
|
||||
}
|
||||
|
@ -1248,7 +1255,8 @@ nsresult HTMLEditor::PromoteInlineRange(nsRange& aRange) {
|
|||
for (nsIContent* content :
|
||||
InclusiveAncestorsOfType<nsIContent>(*aRange.GetEndContainer())) {
|
||||
MOZ_ASSERT(newRangeEnd.GetContainer() == content);
|
||||
if (content->IsHTMLElement(nsGkAtoms::body) || !IsEditable(content) ||
|
||||
if (content->IsHTMLElement(nsGkAtoms::body) ||
|
||||
!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
|
||||
!IsEndOfContainerOrEqualsOrAfterLastEditableChild(newRangeEnd)) {
|
||||
break;
|
||||
}
|
||||
|
@ -1399,8 +1407,9 @@ nsresult HTMLEditor::GetInlinePropertyBase(nsAtom& aHTMLProperty,
|
|||
}
|
||||
|
||||
// just ignore any non-editable nodes
|
||||
if (content->GetAsText() &&
|
||||
(!IsEditable(content) || IsEmptyTextNode(*content))) {
|
||||
if (content->IsText() &&
|
||||
(!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
|
||||
IsEmptyTextNode(*content))) {
|
||||
continue;
|
||||
}
|
||||
if (content->GetAsText()) {
|
||||
|
@ -1837,18 +1846,21 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
|
|||
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
|
||||
if (startOfRange.GetContainer() == endOfRange.GetContainer() &&
|
||||
startOfRange.IsInTextNode()) {
|
||||
if (!IsEditable(startOfRange.GetContainer())) {
|
||||
if (!EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
continue;
|
||||
}
|
||||
arrayOfContents.AppendElement(*startOfRange.GetContainerAsText());
|
||||
arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
|
||||
} else if (startOfRange.IsInTextNode() && endOfRange.IsInTextNode() &&
|
||||
startOfRange.GetContainer()->GetNextSibling() ==
|
||||
endOfRange.GetContainer()) {
|
||||
if (IsEditable(startOfRange.GetContainer())) {
|
||||
arrayOfContents.AppendElement(*startOfRange.GetContainerAsText());
|
||||
if (EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
|
||||
}
|
||||
if (IsEditable(endOfRange.GetContainer())) {
|
||||
arrayOfContents.AppendElement(*endOfRange.GetContainerAsText());
|
||||
if (EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
arrayOfContents.AppendElement(*endOfRange.ContainerAsText());
|
||||
}
|
||||
if (arrayOfContents.IsEmpty()) {
|
||||
continue;
|
||||
|
@ -1857,8 +1869,9 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
|
|||
// Append first node if it's a text node but selected not entirely.
|
||||
if (startOfRange.IsInTextNode() &&
|
||||
!startOfRange.IsStartOfContainer() &&
|
||||
IsEditable(startOfRange.GetContainer())) {
|
||||
arrayOfContents.AppendElement(*startOfRange.GetContainerAsText());
|
||||
EditorUtils::IsEditableContent(*startOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
arrayOfContents.AppendElement(*startOfRange.ContainerAsText());
|
||||
}
|
||||
// Append all entirely selected nodes.
|
||||
ContentSubtreeIterator subtreeIter;
|
||||
|
@ -1868,7 +1881,9 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
|
|||
if (NS_WARN_IF(!node)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (node->IsContent() && IsEditable(node)) {
|
||||
if (node->IsContent() &&
|
||||
EditorUtils::IsEditableContent(*node->AsContent(),
|
||||
EditorType::HTML)) {
|
||||
arrayOfContents.AppendElement(*node->AsContent());
|
||||
}
|
||||
}
|
||||
|
@ -1876,8 +1891,9 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(
|
|||
// Append last node if it's a text node but selected not entirely.
|
||||
if (startOfRange.GetContainer() != endOfRange.GetContainer() &&
|
||||
endOfRange.IsInTextNode() && !endOfRange.IsEndOfContainer() &&
|
||||
IsEditable(endOfRange.GetContainer())) {
|
||||
arrayOfContents.AppendElement(*endOfRange.GetContainerAsText());
|
||||
EditorUtils::IsEditableContent(*endOfRange.ContainerAsText(),
|
||||
EditorType::HTML)) {
|
||||
arrayOfContents.AppendElement(*endOfRange.ContainerAsText());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2156,25 +2172,25 @@ nsresult HTMLEditor::RelativeFontChange(FontSize aDir) {
|
|||
// Iterate range and build up array
|
||||
ContentSubtreeIterator subtreeIter;
|
||||
if (NS_SUCCEEDED(subtreeIter.Init(range))) {
|
||||
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
|
||||
nsTArray<OwningNonNull<nsIContent>> arrayOfContents;
|
||||
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
|
||||
if (NS_WARN_IF(!subtreeIter.GetCurrentNode()->IsContent())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
OwningNonNull<nsIContent> node =
|
||||
OwningNonNull<nsIContent> content =
|
||||
*subtreeIter.GetCurrentNode()->AsContent();
|
||||
|
||||
if (IsEditable(node)) {
|
||||
arrayOfNodes.AppendElement(node);
|
||||
if (EditorUtils::IsEditableContent(content, EditorType::HTML)) {
|
||||
arrayOfContents.AppendElement(content);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have the list, do the font size change on each node
|
||||
for (auto& node : arrayOfNodes) {
|
||||
// MOZ_KnownLive because 'arrayOfNodes' is guaranteed to keep it
|
||||
for (OwningNonNull<nsIContent>& content : arrayOfContents) {
|
||||
// MOZ_KnownLive because 'arrayOfContents' is guaranteed to keep it
|
||||
// alive.
|
||||
nsresult rv = RelativeFontChangeOnNode(
|
||||
aDir == FontSize::incr ? +1 : -1, MOZ_KnownLive(node));
|
||||
aDir == FontSize::incr ? +1 : -1, MOZ_KnownLive(content));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("HTMLEditor::RelativeFontChangeOnNode() failed");
|
||||
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
|
||||
// to be separately handled (they do if they are text nodes, due to how
|
||||
// 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(
|
||||
aDir, MOZ_KnownLive(*startNode->GetAsText()), range->StartOffset(),
|
||||
aDir, MOZ_KnownLive(*startNode->AsText()), range->StartOffset(),
|
||||
startNode->Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("HTMLEditor::RelativeFontChangeOnTextNode() failed");
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (IsTextNode(endNode) && IsEditable(endNode)) {
|
||||
if (IsTextNode(endNode) && EditorUtils::IsEditableContent(
|
||||
*endNode->AsText(), EditorType::HTML)) {
|
||||
nsresult rv = RelativeFontChangeOnTextNode(
|
||||
aDir, MOZ_KnownLive(*endNode->GetAsText()), 0, range->EndOffset());
|
||||
aDir, MOZ_KnownLive(*endNode->AsText()), 0, range->EndOffset());
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("HTMLEditor::RelativeFontChangeOnTextNode() failed");
|
||||
return rv;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "JoinNodeTransaction.h"
|
||||
|
||||
#include "HTMLEditUtils.h"
|
||||
#include "mozilla/EditorBase.h" // for EditorBase
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "nsAString.h"
|
||||
|
@ -49,7 +50,8 @@ bool JoinNodeTransaction::CanDoIt() const {
|
|||
NS_WARN_IF(!mEditorBase) || !mLeftContent->GetParentNode()) {
|
||||
return false;
|
||||
}
|
||||
return mEditorBase->IsModifiableNode(*mLeftContent->GetParentNode());
|
||||
return mEditorBase->IsTextEditor() ||
|
||||
HTMLEditUtils::IsRemovableFromParentNode(*mLeftContent);
|
||||
}
|
||||
|
||||
// After DoTransaction() and RedoTransaction(), the left node is removed from
|
||||
|
|
|
@ -755,7 +755,7 @@ nsresult TextEditor::DeleteSelectionAsSubAction(EDirection aDirectionAndAmount,
|
|||
if (atNewStartOfSelection.IsInTextNode() &&
|
||||
!atNewStartOfSelection.GetContainer()->Length()) {
|
||||
nsresult rv = DeleteNodeWithTransaction(
|
||||
MOZ_KnownLive(*atNewStartOfSelection.GetContainer()));
|
||||
MOZ_KnownLive(*atNewStartOfSelection.ContainerAsText()));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
|
||||
return rv;
|
||||
|
@ -1487,9 +1487,11 @@ NS_IMETHODIMP TextEditor::GetTextLength(int32_t* aCount) {
|
|||
DebugOnly<nsresult> rvIgnored = postOrderIter.Init(rootElement);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
|
||||
"PostContentIterator::Init() failed, but ignored");
|
||||
EditorType editorType = GetEditorType();
|
||||
for (; !postOrderIter.IsDone(); postOrderIter.Next()) {
|
||||
nsINode* currentNode = postOrderIter.GetCurrentNode();
|
||||
if (IsTextNode(currentNode) && IsEditable(currentNode)) {
|
||||
if (IsTextNode(currentNode) &&
|
||||
EditorUtils::IsEditableContent(*currentNode->AsText(), editorType)) {
|
||||
totalLength += currentNode->Length();
|
||||
}
|
||||
}
|
||||
|
@ -1566,7 +1568,7 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
|
|||
// performance hit here.
|
||||
nsIContent* leftMostChild = GetLeftmostChild(mRootElement);
|
||||
if (leftMostChild &&
|
||||
EditorBase::IsPaddingBRElementForEmptyEditor(*leftMostChild)) {
|
||||
EditorUtils::IsPaddingBRElementForEmptyEditor(*leftMostChild)) {
|
||||
mPaddingBRElementForEmptyEditor =
|
||||
static_cast<HTMLBRElement*>(leftMostChild);
|
||||
} else {
|
||||
|
@ -1649,7 +1651,7 @@ nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
|
|||
Element* brElement =
|
||||
nodeList->Length() == 1 ? nodeList->Item(0) : nullptr;
|
||||
if (brElement &&
|
||||
EditorBase::IsPaddingBRElementForEmptyEditor(*brElement)) {
|
||||
EditorUtils::IsPaddingBRElementForEmptyEditor(*brElement)) {
|
||||
mPaddingBRElementForEmptyEditor =
|
||||
static_cast<HTMLBRElement*>(brElement);
|
||||
} else {
|
||||
|
|
|
@ -669,14 +669,14 @@ nsIContent* WSRunScanner::GetEditableBlockParentOrTopmotEditableInlineContent(
|
|||
if (NS_WARN_IF(!aContent)) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ASSERTION(mHTMLEditor->IsEditable(aContent),
|
||||
NS_ASSERTION(EditorUtils::IsEditableContent(*aContent, EditorType::HTML),
|
||||
"Given content is not editable");
|
||||
// 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 possible but shouldn't occur actually.
|
||||
nsIContent* editableBlockParentOrTopmotEditableInlineContent = nullptr;
|
||||
for (nsIContent* content : InclusiveAncestorsOfType<nsIContent>(*aContent)) {
|
||||
if (!mHTMLEditor->IsEditable(content)) {
|
||||
if (!EditorUtils::IsEditableContent(*content, EditorType::HTML)) {
|
||||
break;
|
||||
}
|
||||
editableBlockParentOrTopmotEditableInlineContent = content;
|
||||
|
|
|
@ -412,6 +412,8 @@ class MOZ_STACK_CLASS WSRunScanner {
|
|||
Element* GetEditingHost() const { return mEditingHost; }
|
||||
|
||||
protected:
|
||||
using EditorType = EditorBase::EditorType;
|
||||
|
||||
// WSFragment represents a single run of ws (all leadingws, or all normalws,
|
||||
// or all trailingws, or all leading+trailingws). Note that this single run
|
||||
// may still span multiple nodes.
|
||||
|
|
Загрузка…
Ссылка в новой задаче