зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1627175 - part 38: Move `EditorBase::GetNextContent()` and `EditorBase::GetPreviousContent()` to `HTMLEditUtils` r=m_kato
Differential Revision: https://phabricator.services.mozilla.com/D113242
This commit is contained in:
Родитель
7da0836039
Коммит
3aef835f6c
|
@ -128,6 +128,7 @@ using namespace widget;
|
||||||
|
|
||||||
using LeafNodeType = HTMLEditUtils::LeafNodeType;
|
using LeafNodeType = HTMLEditUtils::LeafNodeType;
|
||||||
using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
|
using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
|
||||||
|
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* mozilla::EditorBase
|
* mozilla::EditorBase
|
||||||
|
@ -2802,231 +2803,6 @@ nsresult EditorBase::DeleteTextWithTransaction(Text& aTextNode,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
nsIContent* EditorBase::GetPreviousContent(
|
|
||||||
const nsINode& aNode, const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter /* = nullptr */) {
|
|
||||||
if (&aNode == aAncestorLimiter ||
|
|
||||||
(aAncestorLimiter && !aNode.IsInclusiveDescendantOf(aAncestorLimiter))) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return EditorBase::GetAdjacentContent(aNode, WalkTreeDirection::Backward,
|
|
||||||
aOptions, aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
nsIContent* EditorBase::GetPreviousContent(
|
|
||||||
const EditorRawDOMPoint& aPoint, const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter /* = nullptr */) {
|
|
||||||
MOZ_ASSERT(aPoint.IsSetAndValid());
|
|
||||||
NS_WARNING_ASSERTION(
|
|
||||||
!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
|
|
||||||
"GetPreviousContent() doesn't assume that the start point is a "
|
|
||||||
"data node except text node");
|
|
||||||
|
|
||||||
// If we are at the beginning of the node, or it is a text node, then just
|
|
||||||
// look before it.
|
|
||||||
if (aPoint.IsStartOfContainer() || aPoint.IsInTextNode()) {
|
|
||||||
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
|
||||||
aPoint.IsInContentNode() &&
|
|
||||||
HTMLEditUtils::IsBlockElement(*aPoint.ContainerAsContent())) {
|
|
||||||
// If we aren't allowed to cross blocks, don't look before this block.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return EditorBase::GetPreviousContent(*aPoint.GetContainer(), aOptions,
|
|
||||||
aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// else look before the child at 'aOffset'
|
|
||||||
if (aPoint.GetChild()) {
|
|
||||||
return EditorBase::GetPreviousContent(*aPoint.GetChild(), aOptions,
|
|
||||||
aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unless there isn't one, in which case we are at the end of the node
|
|
||||||
// and want the deep-right child.
|
|
||||||
nsIContent* lastLeafContent = HTMLEditUtils::GetLastLeafChild(
|
|
||||||
*aPoint.GetContainer(),
|
|
||||||
{aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
|
|
||||||
? LeafNodeType::LeafNodeOrChildBlock
|
|
||||||
: LeafNodeType::OnlyLeafNode});
|
|
||||||
if (!lastLeafContent) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
|
|
||||||
EditorUtils::IsEditableContent(*lastLeafContent, EditorType::HTML)) &&
|
|
||||||
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
|
|
||||||
EditorUtils::IsElementOrText(*lastLeafContent))) {
|
|
||||||
return lastLeafContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// restart the search from the non-editable node we just found
|
|
||||||
return EditorBase::GetPreviousContent(*lastLeafContent, aOptions,
|
|
||||||
aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
nsIContent* EditorBase::GetNextContent(
|
|
||||||
const nsINode& aNode, const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter /* = nullptr */) {
|
|
||||||
if (&aNode == aAncestorLimiter ||
|
|
||||||
(aAncestorLimiter && !aNode.IsInclusiveDescendantOf(aAncestorLimiter))) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return EditorBase::GetAdjacentContent(aNode, WalkTreeDirection::Forward,
|
|
||||||
aOptions, aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
nsIContent* EditorBase::GetNextContent(
|
|
||||||
const EditorRawDOMPoint& aPoint, const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter /* = nullptr */) {
|
|
||||||
MOZ_ASSERT(aPoint.IsSetAndValid());
|
|
||||||
NS_WARNING_ASSERTION(
|
|
||||||
!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
|
|
||||||
"GetNextContent() doesn't assume that the start point is a "
|
|
||||||
"data node except text node");
|
|
||||||
|
|
||||||
EditorRawDOMPoint point(aPoint);
|
|
||||||
|
|
||||||
// if the container is a text node, use its location instead
|
|
||||||
if (point.IsInTextNode()) {
|
|
||||||
point.SetAfter(point.GetContainer());
|
|
||||||
if (NS_WARN_IF(!point.IsSet())) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (point.GetChild()) {
|
|
||||||
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
|
||||||
HTMLEditUtils::IsBlockElement(*point.GetChild())) {
|
|
||||||
return point.GetChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIContent* firstLeafContent = HTMLEditUtils::GetFirstLeafChild(
|
|
||||||
*point.GetChild(),
|
|
||||||
{aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
|
|
||||||
? LeafNodeType::LeafNodeOrChildBlock
|
|
||||||
: LeafNodeType::OnlyLeafNode});
|
|
||||||
if (!firstLeafContent) {
|
|
||||||
return point.GetChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX Why do we need to do this check? The leaf node must be a descendant
|
|
||||||
// of `point.GetChild()`.
|
|
||||||
if (aAncestorLimiter &&
|
|
||||||
(firstLeafContent == aAncestorLimiter ||
|
|
||||||
!firstLeafContent->IsInclusiveDescendantOf(aAncestorLimiter))) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
|
|
||||||
EditorUtils::IsEditableContent(*firstLeafContent, EditorType::HTML)) &&
|
|
||||||
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
|
|
||||||
EditorUtils::IsElementOrText(*firstLeafContent))) {
|
|
||||||
return firstLeafContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// restart the search from the non-editable node we just found
|
|
||||||
return EditorBase::GetNextContent(*firstLeafContent, aOptions,
|
|
||||||
aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unless there isn't one, in which case we are at the end of the node
|
|
||||||
// and want the next one.
|
|
||||||
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
|
||||||
point.IsInContentNode() &&
|
|
||||||
HTMLEditUtils::IsBlockElement(*point.ContainerAsContent())) {
|
|
||||||
// don't cross out of parent block
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EditorBase::GetNextContent(*point.GetContainer(), aOptions,
|
|
||||||
aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
nsIContent* EditorBase::GetAdjacentLeafContent(
|
|
||||||
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
|
||||||
const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter /* = nullptr */) {
|
|
||||||
// called only by GetPriorNode so we don't need to check params.
|
|
||||||
MOZ_ASSERT(&aNode != aAncestorLimiter);
|
|
||||||
MOZ_ASSERT_IF(aAncestorLimiter,
|
|
||||||
aAncestorLimiter->IsInclusiveDescendantOf(aAncestorLimiter));
|
|
||||||
|
|
||||||
const nsINode* node = &aNode;
|
|
||||||
for (;;) {
|
|
||||||
// if aNode has a sibling in the right direction, return
|
|
||||||
// that sibling's closest child (or itself if it has no children)
|
|
||||||
nsIContent* sibling = aWalkTreeDirection == WalkTreeDirection::Forward
|
|
||||||
? node->GetNextSibling()
|
|
||||||
: node->GetPreviousSibling();
|
|
||||||
if (sibling) {
|
|
||||||
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
|
||||||
HTMLEditUtils::IsBlockElement(*sibling)) {
|
|
||||||
// don't look inside prevsib, since it is a block
|
|
||||||
return sibling;
|
|
||||||
}
|
|
||||||
const LeafNodeTypes leafNodeTypes = {
|
|
||||||
aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
|
|
||||||
? LeafNodeType::LeafNodeOrChildBlock
|
|
||||||
: LeafNodeType::OnlyLeafNode};
|
|
||||||
nsIContent* leafContent =
|
|
||||||
aWalkTreeDirection == WalkTreeDirection::Forward
|
|
||||||
? HTMLEditUtils::GetFirstLeafChild(*sibling, leafNodeTypes)
|
|
||||||
: HTMLEditUtils::GetLastLeafChild(*sibling, leafNodeTypes);
|
|
||||||
return leafContent ? leafContent : sibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIContent* parent = node->GetParent();
|
|
||||||
if (!parent) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent == aAncestorLimiter ||
|
|
||||||
(aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
|
||||||
HTMLEditUtils::IsBlockElement(*parent))) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT_UNREACHABLE("What part of for(;;) do you not understand?");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
nsIContent* EditorBase::GetAdjacentContent(
|
|
||||||
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
|
||||||
const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter /* = nullptr */) {
|
|
||||||
if (&aNode == aAncestorLimiter) {
|
|
||||||
// Don't allow traversal above the root node! This helps
|
|
||||||
// prevent us from accidentally editing browser content
|
|
||||||
// when the editor is in a text widget.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIContent* leafContent = EditorBase::GetAdjacentLeafContent(
|
|
||||||
aNode, aWalkTreeDirection, aOptions, aAncestorLimiter);
|
|
||||||
if (!leafContent) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
|
|
||||||
EditorUtils::IsEditableContent(*leafContent, EditorType::HTML)) &&
|
|
||||||
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
|
|
||||||
EditorUtils::IsElementOrText(*leafContent))) {
|
|
||||||
return leafContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EditorBase::GetAdjacentContent(*leafContent, aWalkTreeDirection,
|
|
||||||
aOptions, aAncestorLimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EditorBase::IsRoot(const nsINode* inNode) const {
|
bool EditorBase::IsRoot(const nsINode* inNode) const {
|
||||||
if (NS_WARN_IF(!inNode)) {
|
if (NS_WARN_IF(!inNode)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -3509,7 +3285,7 @@ EditorBase::CreateTransactionForCollapsedRange(
|
||||||
MOZ_ASSERT(IsHTMLEditor());
|
MOZ_ASSERT(IsHTMLEditor());
|
||||||
// We're backspacing from the beginning of a node. Delete the last thing
|
// We're backspacing from the beginning of a node. Delete the last thing
|
||||||
// of previous editable content.
|
// of previous editable content.
|
||||||
nsIContent* previousEditableContent = EditorBase::GetPreviousContent(
|
nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousContent(
|
||||||
*point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
|
*point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
GetEditorRoot());
|
GetEditorRoot());
|
||||||
if (!previousEditableContent) {
|
if (!previousEditableContent) {
|
||||||
|
@ -3553,7 +3329,7 @@ EditorBase::CreateTransactionForCollapsedRange(
|
||||||
MOZ_ASSERT(IsHTMLEditor());
|
MOZ_ASSERT(IsHTMLEditor());
|
||||||
// We're deleting from the end of a node. Delete the first thing of
|
// We're deleting from the end of a node. Delete the first thing of
|
||||||
// next editable content.
|
// next editable content.
|
||||||
nsIContent* nextEditableContent = EditorBase::GetNextContent(
|
nsIContent* nextEditableContent = HTMLEditUtils::GetNextContent(
|
||||||
*point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
|
*point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
GetEditorRoot());
|
GetEditorRoot());
|
||||||
if (!nextEditableContent) {
|
if (!nextEditableContent) {
|
||||||
|
@ -3616,10 +3392,10 @@ EditorBase::CreateTransactionForCollapsedRange(
|
||||||
if (IsHTMLEditor()) {
|
if (IsHTMLEditor()) {
|
||||||
editableContent =
|
editableContent =
|
||||||
aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward
|
aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward
|
||||||
? EditorBase::GetPreviousContent(
|
? HTMLEditUtils::GetPreviousContent(
|
||||||
point, {WalkTreeOption::IgnoreNonEditableNode},
|
point, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
GetEditorRoot())
|
GetEditorRoot())
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
point, {WalkTreeOption::IgnoreNonEditableNode},
|
point, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
GetEditorRoot());
|
GetEditorRoot());
|
||||||
if (!editableContent) {
|
if (!editableContent) {
|
||||||
|
@ -3632,10 +3408,10 @@ EditorBase::CreateTransactionForCollapsedRange(
|
||||||
editableContent =
|
editableContent =
|
||||||
aHowToHandleCollapsedRange ==
|
aHowToHandleCollapsedRange ==
|
||||||
HowToHandleCollapsedRange::ExtendBackward
|
HowToHandleCollapsedRange::ExtendBackward
|
||||||
? EditorBase::GetPreviousContent(
|
? HTMLEditUtils::GetPreviousContent(
|
||||||
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
GetEditorRoot())
|
GetEditorRoot())
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
GetEditorRoot());
|
GetEditorRoot());
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
|
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
|
||||||
#include "mozilla/EditAction.h" // for EditAction and EditSubAction
|
#include "mozilla/EditAction.h" // for EditAction and EditSubAction
|
||||||
#include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
|
#include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
|
||||||
#include "mozilla/EnumSet.h" // for EnumSet
|
|
||||||
#include "mozilla/EventForwards.h" // for InputEventTargetRanges
|
#include "mozilla/EventForwards.h" // for InputEventTargetRanges
|
||||||
#include "mozilla/Maybe.h" // for Maybe
|
#include "mozilla/Maybe.h" // for Maybe
|
||||||
#include "mozilla/OwningNonNull.h" // for OwningNonNull
|
#include "mozilla/OwningNonNull.h" // for OwningNonNull
|
||||||
|
@ -1723,73 +1722,6 @@ class EditorBase : public nsIEditor,
|
||||||
MOZ_CAN_RUN_SCRIPT nsresult
|
MOZ_CAN_RUN_SCRIPT nsresult
|
||||||
DoTransactionInternal(nsITransaction* aTransaction);
|
DoTransactionInternal(nsITransaction* aTransaction);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next node.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* On the other hand, the methods taking nsINode behavior must be what
|
|
||||||
* you want. They start to search the result from next node of the given
|
|
||||||
* node.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get previous content node of aNode if there is.
|
|
||||||
*
|
|
||||||
* @param aNode The node from which we start to walk the DOM tree.
|
|
||||||
*/
|
|
||||||
enum class WalkTreeOption {
|
|
||||||
IgnoreNonEditableNode, // Ignore non-editable nodes and their children.
|
|
||||||
IgnoreDataNodeExceptText, // Ignore data nodes which are not text node.
|
|
||||||
StopAtBlockBoundary, // Stop waking the tree at a block boundary.
|
|
||||||
};
|
|
||||||
using WalkTreeOptions = EnumSet<WalkTreeOption>;
|
|
||||||
static nsIContent* GetPreviousContent(
|
|
||||||
const nsINode& aNode, const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* And another version that takes a point in DOM tree rather than a node.
|
|
||||||
*/
|
|
||||||
static nsIContent* GetPreviousContent(
|
|
||||||
const EditorRawDOMPoint& aPoint, const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get next content node of aNode if there is.
|
|
||||||
*
|
|
||||||
* @param aNode The node from which we start to walk the DOM tree.
|
|
||||||
*/
|
|
||||||
static nsIContent* GetNextContent(const nsINode& aNode,
|
|
||||||
const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* And another version that takes a point in DOM tree rather than a node.
|
|
||||||
*
|
|
||||||
* Note that this may return the child at the offset. E.g., following code
|
|
||||||
* causes infinite loop.
|
|
||||||
*
|
|
||||||
* EditorRawDOMPoint point(aEditableNode);
|
|
||||||
* while (nsIContent* content =
|
|
||||||
* GetNextContent(point, {WalkTreeOption::IgnoreNonEditableNode})) {
|
|
||||||
* // Do something...
|
|
||||||
* point.Set(content);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* Following code must be you expected:
|
|
||||||
*
|
|
||||||
* while (nsIContent* content =
|
|
||||||
* GetNextContent(point, {WalkTreeOption::IgnoreNonEditableNode}) {
|
|
||||||
* // Do something...
|
|
||||||
* DebugOnly<bool> advanced = point.Advanced();
|
|
||||||
* MOZ_ASSERT(advanced);
|
|
||||||
* point.Set(point.GetChild());
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
static nsIContent* GetNextContent(const EditorRawDOMPoint& aPoint,
|
|
||||||
const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter = nullptr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if aNode is our root node.
|
* Returns true if aNode is our root node.
|
||||||
*/
|
*/
|
||||||
|
@ -2154,19 +2086,6 @@ class EditorBase : public nsIEditor,
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ScrollSelectionFocusIntoView();
|
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ScrollSelectionFocusIntoView();
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper for GetPreviousContent() and GetNextContent().
|
|
||||||
*/
|
|
||||||
enum class WalkTreeDirection { Forward, Backward };
|
|
||||||
static nsIContent* GetAdjacentLeafContent(
|
|
||||||
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
|
||||||
const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter = nullptr);
|
|
||||||
static nsIContent* GetAdjacentContent(
|
|
||||||
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
|
||||||
const WalkTreeOptions& aOptions,
|
|
||||||
const Element* aAncestorLimiter = nullptr);
|
|
||||||
|
|
||||||
virtual nsresult InstallEventListeners();
|
virtual nsresult InstallEventListeners();
|
||||||
virtual void CreateEventListeners();
|
virtual void CreateEventListeners();
|
||||||
virtual void RemoveEventListeners();
|
virtual void RemoveEventListeners();
|
||||||
|
|
|
@ -38,6 +38,31 @@ namespace mozilla {
|
||||||
using namespace dom;
|
using namespace dom;
|
||||||
using EditorType = EditorBase::EditorType;
|
using EditorType = EditorBase::EditorType;
|
||||||
|
|
||||||
|
template nsIContent* HTMLEditUtils::GetPreviousContent(
|
||||||
|
const EditorDOMPoint& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
template nsIContent* HTMLEditUtils::GetPreviousContent(
|
||||||
|
const EditorRawDOMPoint& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
template nsIContent* HTMLEditUtils::GetPreviousContent(
|
||||||
|
const EditorDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
template nsIContent* HTMLEditUtils::GetPreviousContent(
|
||||||
|
const EditorRawDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
template nsIContent* HTMLEditUtils::GetNextContent(
|
||||||
|
const EditorDOMPoint& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
template nsIContent* HTMLEditUtils::GetNextContent(
|
||||||
|
const EditorRawDOMPoint& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
template nsIContent* HTMLEditUtils::GetNextContent(
|
||||||
|
const EditorDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
template nsIContent* HTMLEditUtils::GetNextContent(
|
||||||
|
const EditorRawDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter);
|
||||||
|
|
||||||
template EditorDOMPoint HTMLEditUtils::GetPreviousEditablePoint(
|
template EditorDOMPoint HTMLEditUtils::GetPreviousEditablePoint(
|
||||||
nsIContent& aContent, const Element* aAncestorLimiter,
|
nsIContent& aContent, const Element* aAncestorLimiter,
|
||||||
InvisibleWhiteSpaces aInvisibleWhiteSpaces,
|
InvisibleWhiteSpaces aInvisibleWhiteSpaces,
|
||||||
|
@ -865,6 +890,209 @@ bool HTMLEditUtils::IsSingleLineContainer(nsINode& aNode) {
|
||||||
aNode.IsAnyOfHTMLElements(nsGkAtoms::li, nsGkAtoms::dt, nsGkAtoms::dd);
|
aNode.IsAnyOfHTMLElements(nsGkAtoms::li, nsGkAtoms::dt, nsGkAtoms::dd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
template <typename PT, typename CT>
|
||||||
|
nsIContent* HTMLEditUtils::GetPreviousContent(
|
||||||
|
const EditorDOMPointBase<PT, CT>& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter /* = nullptr */) {
|
||||||
|
MOZ_ASSERT(aPoint.IsSetAndValid());
|
||||||
|
NS_WARNING_ASSERTION(
|
||||||
|
!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
|
||||||
|
"GetPreviousContent() doesn't assume that the start point is a "
|
||||||
|
"data node except text node");
|
||||||
|
|
||||||
|
// If we are at the beginning of the node, or it is a text node, then just
|
||||||
|
// look before it.
|
||||||
|
if (aPoint.IsStartOfContainer() || aPoint.IsInTextNode()) {
|
||||||
|
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
||||||
|
aPoint.IsInContentNode() &&
|
||||||
|
HTMLEditUtils::IsBlockElement(*aPoint.ContainerAsContent())) {
|
||||||
|
// If we aren't allowed to cross blocks, don't look before this block.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return HTMLEditUtils::GetPreviousContent(*aPoint.GetContainer(), aOptions,
|
||||||
|
aAncestorLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// else look before the child at 'aOffset'
|
||||||
|
if (aPoint.GetChild()) {
|
||||||
|
return HTMLEditUtils::GetPreviousContent(*aPoint.GetChild(), aOptions,
|
||||||
|
aAncestorLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unless there isn't one, in which case we are at the end of the node
|
||||||
|
// and want the deep-right child.
|
||||||
|
nsIContent* lastLeafContent = HTMLEditUtils::GetLastLeafChild(
|
||||||
|
*aPoint.GetContainer(),
|
||||||
|
{aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
|
||||||
|
? LeafNodeType::LeafNodeOrChildBlock
|
||||||
|
: LeafNodeType::OnlyLeafNode});
|
||||||
|
if (!lastLeafContent) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
|
||||||
|
EditorUtils::IsEditableContent(*lastLeafContent, EditorType::HTML)) &&
|
||||||
|
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
|
||||||
|
EditorUtils::IsElementOrText(*lastLeafContent))) {
|
||||||
|
return lastLeafContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// restart the search from the non-editable node we just found
|
||||||
|
return HTMLEditUtils::GetPreviousContent(*lastLeafContent, aOptions,
|
||||||
|
aAncestorLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
template <typename PT, typename CT>
|
||||||
|
nsIContent* HTMLEditUtils::GetNextContent(
|
||||||
|
const EditorDOMPointBase<PT, CT>& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter /* = nullptr */) {
|
||||||
|
MOZ_ASSERT(aPoint.IsSetAndValid());
|
||||||
|
NS_WARNING_ASSERTION(
|
||||||
|
!aPoint.IsInDataNode() || aPoint.IsInTextNode(),
|
||||||
|
"GetNextContent() doesn't assume that the start point is a "
|
||||||
|
"data node except text node");
|
||||||
|
|
||||||
|
EditorRawDOMPoint point(aPoint);
|
||||||
|
|
||||||
|
// if the container is a text node, use its location instead
|
||||||
|
if (point.IsInTextNode()) {
|
||||||
|
point.SetAfter(point.GetContainer());
|
||||||
|
if (NS_WARN_IF(!point.IsSet())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point.GetChild()) {
|
||||||
|
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
||||||
|
HTMLEditUtils::IsBlockElement(*point.GetChild())) {
|
||||||
|
return point.GetChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIContent* firstLeafContent = HTMLEditUtils::GetFirstLeafChild(
|
||||||
|
*point.GetChild(),
|
||||||
|
{aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
|
||||||
|
? LeafNodeType::LeafNodeOrChildBlock
|
||||||
|
: LeafNodeType::OnlyLeafNode});
|
||||||
|
if (!firstLeafContent) {
|
||||||
|
return point.GetChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX Why do we need to do this check? The leaf node must be a descendant
|
||||||
|
// of `point.GetChild()`.
|
||||||
|
if (aAncestorLimiter &&
|
||||||
|
(firstLeafContent == aAncestorLimiter ||
|
||||||
|
!firstLeafContent->IsInclusiveDescendantOf(aAncestorLimiter))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
|
||||||
|
EditorUtils::IsEditableContent(*firstLeafContent, EditorType::HTML)) &&
|
||||||
|
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
|
||||||
|
EditorUtils::IsElementOrText(*firstLeafContent))) {
|
||||||
|
return firstLeafContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// restart the search from the non-editable node we just found
|
||||||
|
return HTMLEditUtils::GetNextContent(*firstLeafContent, aOptions,
|
||||||
|
aAncestorLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unless there isn't one, in which case we are at the end of the node
|
||||||
|
// and want the next one.
|
||||||
|
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
||||||
|
point.IsInContentNode() &&
|
||||||
|
HTMLEditUtils::IsBlockElement(*point.ContainerAsContent())) {
|
||||||
|
// don't cross out of parent block
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HTMLEditUtils::GetNextContent(*point.GetContainer(), aOptions,
|
||||||
|
aAncestorLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
nsIContent* HTMLEditUtils::GetAdjacentLeafContent(
|
||||||
|
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
||||||
|
const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter /* = nullptr */) {
|
||||||
|
// called only by GetPriorNode so we don't need to check params.
|
||||||
|
MOZ_ASSERT(&aNode != aAncestorLimiter);
|
||||||
|
MOZ_ASSERT_IF(aAncestorLimiter,
|
||||||
|
aAncestorLimiter->IsInclusiveDescendantOf(aAncestorLimiter));
|
||||||
|
|
||||||
|
const nsINode* node = &aNode;
|
||||||
|
for (;;) {
|
||||||
|
// if aNode has a sibling in the right direction, return
|
||||||
|
// that sibling's closest child (or itself if it has no children)
|
||||||
|
nsIContent* sibling = aWalkTreeDirection == WalkTreeDirection::Forward
|
||||||
|
? node->GetNextSibling()
|
||||||
|
: node->GetPreviousSibling();
|
||||||
|
if (sibling) {
|
||||||
|
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
||||||
|
HTMLEditUtils::IsBlockElement(*sibling)) {
|
||||||
|
// don't look inside prevsib, since it is a block
|
||||||
|
return sibling;
|
||||||
|
}
|
||||||
|
const LeafNodeTypes leafNodeTypes = {
|
||||||
|
aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
|
||||||
|
? LeafNodeType::LeafNodeOrChildBlock
|
||||||
|
: LeafNodeType::OnlyLeafNode};
|
||||||
|
nsIContent* leafContent =
|
||||||
|
aWalkTreeDirection == WalkTreeDirection::Forward
|
||||||
|
? HTMLEditUtils::GetFirstLeafChild(*sibling, leafNodeTypes)
|
||||||
|
: HTMLEditUtils::GetLastLeafChild(*sibling, leafNodeTypes);
|
||||||
|
return leafContent ? leafContent : sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIContent* parent = node->GetParent();
|
||||||
|
if (!parent) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent == aAncestorLimiter ||
|
||||||
|
(aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
|
||||||
|
HTMLEditUtils::IsBlockElement(*parent))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT_UNREACHABLE("What part of for(;;) do you not understand?");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
nsIContent* HTMLEditUtils::GetAdjacentContent(
|
||||||
|
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
||||||
|
const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter /* = nullptr */) {
|
||||||
|
if (&aNode == aAncestorLimiter) {
|
||||||
|
// Don't allow traversal above the root node! This helps
|
||||||
|
// prevent us from accidentally editing browser content
|
||||||
|
// when the editor is in a text widget.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIContent* leafContent = HTMLEditUtils::GetAdjacentLeafContent(
|
||||||
|
aNode, aWalkTreeDirection, aOptions, aAncestorLimiter);
|
||||||
|
if (!leafContent) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
|
||||||
|
EditorUtils::IsEditableContent(*leafContent, EditorType::HTML)) &&
|
||||||
|
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
|
||||||
|
EditorUtils::IsElementOrText(*leafContent))) {
|
||||||
|
return leafContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HTMLEditUtils::GetAdjacentContent(*leafContent, aWalkTreeDirection,
|
||||||
|
aOptions, aAncestorLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
template <typename EditorDOMPointType>
|
template <typename EditorDOMPointType>
|
||||||
EditorDOMPointType HTMLEditUtils::GetPreviousEditablePoint(
|
EditorDOMPointType HTMLEditUtils::GetPreviousEditablePoint(
|
||||||
|
|
|
@ -370,6 +370,82 @@ class HTMLEditUtils final {
|
||||||
aFoundLinkElement);
|
aFoundLinkElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get adjacent content node of aNode if there is (even if one is in different
|
||||||
|
* parent element).
|
||||||
|
*
|
||||||
|
* @param aNode The node from which we start to walk the DOM
|
||||||
|
* tree.
|
||||||
|
* @param aOptions See WalkTreeOption for the detail.
|
||||||
|
* @param aAncestorLimiter Ancestor limiter element which these methods
|
||||||
|
* never cross its boundary. This is typically
|
||||||
|
* the editing host.
|
||||||
|
*/
|
||||||
|
enum class WalkTreeOption {
|
||||||
|
IgnoreNonEditableNode, // Ignore non-editable nodes and their children.
|
||||||
|
IgnoreDataNodeExceptText, // Ignore data nodes which are not text node.
|
||||||
|
StopAtBlockBoundary, // Stop waking the tree at a block boundary.
|
||||||
|
};
|
||||||
|
using WalkTreeOptions = EnumSet<WalkTreeOption>;
|
||||||
|
static nsIContent* GetPreviousContent(
|
||||||
|
const nsINode& aNode, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter = nullptr) {
|
||||||
|
if (&aNode == aAncestorLimiter ||
|
||||||
|
(aAncestorLimiter &&
|
||||||
|
!aNode.IsInclusiveDescendantOf(aAncestorLimiter))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return HTMLEditUtils::GetAdjacentContent(aNode, WalkTreeDirection::Backward,
|
||||||
|
aOptions, aAncestorLimiter);
|
||||||
|
}
|
||||||
|
static nsIContent* GetNextContent(const nsINode& aNode,
|
||||||
|
const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter = nullptr) {
|
||||||
|
if (&aNode == aAncestorLimiter ||
|
||||||
|
(aAncestorLimiter &&
|
||||||
|
!aNode.IsInclusiveDescendantOf(aAncestorLimiter))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return HTMLEditUtils::GetAdjacentContent(aNode, WalkTreeDirection::Forward,
|
||||||
|
aOptions, aAncestorLimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* And another version that takes a point in DOM tree rather than a node.
|
||||||
|
*/
|
||||||
|
template <typename PT, typename CT>
|
||||||
|
static nsIContent* GetPreviousContent(
|
||||||
|
const EditorDOMPointBase<PT, CT>& aPoint, const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* And another version that takes a point in DOM tree rather than a node.
|
||||||
|
*
|
||||||
|
* Note that this may return the child at the offset. E.g., following code
|
||||||
|
* causes infinite loop.
|
||||||
|
*
|
||||||
|
* EditorRawDOMPoint point(aEditableNode);
|
||||||
|
* while (nsIContent* content =
|
||||||
|
* GetNextContent(point, {WalkTreeOption::IgnoreNonEditableNode})) {
|
||||||
|
* // Do something...
|
||||||
|
* point.Set(content);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Following code must be you expected:
|
||||||
|
*
|
||||||
|
* while (nsIContent* content =
|
||||||
|
* GetNextContent(point, {WalkTreeOption::IgnoreNonEditableNode}) {
|
||||||
|
* // Do something...
|
||||||
|
* DebugOnly<bool> advanced = point.Advanced();
|
||||||
|
* MOZ_ASSERT(advanced);
|
||||||
|
* point.Set(point.GetChild());
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
template <typename PT, typename CT>
|
||||||
|
static nsIContent* GetNextContent(const EditorDOMPointBase<PT, CT>& aPoint,
|
||||||
|
const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetLastLeafChild() returns rightmost leaf content in aNode. It depends on
|
* GetLastLeafChild() returns rightmost leaf content in aNode. It depends on
|
||||||
* aLeafNodeTypes whether this which types of nodes are treated as leaf nodes.
|
* aLeafNodeTypes whether this which types of nodes are treated as leaf nodes.
|
||||||
|
@ -696,9 +772,8 @@ class HTMLEditUtils final {
|
||||||
aRootElement, {LeafNodeType::OnlyLeafNode});
|
aRootElement, {LeafNodeType::OnlyLeafNode});
|
||||||
if (leafContent && !EditorUtils::IsEditableContent(
|
if (leafContent && !EditorUtils::IsEditableContent(
|
||||||
*leafContent, EditorBase::EditorType::HTML)) {
|
*leafContent, EditorBase::EditorType::HTML)) {
|
||||||
leafContent = EditorBase::GetNextContent(
|
leafContent = HTMLEditUtils::GetNextContent(
|
||||||
*leafContent, {EditorBase::WalkTreeOption::IgnoreNonEditableNode},
|
*leafContent, {WalkTreeOption::IgnoreNonEditableNode}, &aRootElement);
|
||||||
&aRootElement);
|
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(leafContent != &aRootElement);
|
MOZ_ASSERT(leafContent != &aRootElement);
|
||||||
return leafContent;
|
return leafContent;
|
||||||
|
@ -1068,6 +1143,19 @@ class HTMLEditUtils final {
|
||||||
aContent.IsHTMLElement(nsGkAtoms::table));
|
aContent.IsHTMLElement(nsGkAtoms::table));
|
||||||
return !cannotCrossBoundary;
|
return !cannotCrossBoundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for GetPreviousContent() and GetNextContent().
|
||||||
|
*/
|
||||||
|
enum class WalkTreeDirection { Forward, Backward };
|
||||||
|
static nsIContent* GetAdjacentLeafContent(
|
||||||
|
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
||||||
|
const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter = nullptr);
|
||||||
|
static nsIContent* GetAdjacentContent(
|
||||||
|
const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
|
||||||
|
const WalkTreeOptions& aOptions,
|
||||||
|
const Element* aAncestorLimiter = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -72,6 +72,7 @@ using namespace widget;
|
||||||
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
|
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
|
||||||
using LeafNodeType = HTMLEditUtils::LeafNodeType;
|
using LeafNodeType = HTMLEditUtils::LeafNodeType;
|
||||||
using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
|
using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
|
||||||
|
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
|
||||||
|
|
||||||
const char16_t kNBSP = 160;
|
const char16_t kNBSP = 160;
|
||||||
|
|
||||||
|
@ -5006,12 +5007,12 @@ nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing
|
return aNoBlockCrossing
|
||||||
? EditorBase::GetPreviousContent(
|
? HTMLEditUtils::GetPreviousContent(
|
||||||
aNode,
|
aNode,
|
||||||
{WalkTreeOption::IgnoreDataNodeExceptText,
|
{WalkTreeOption::IgnoreDataNodeExceptText,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetPreviousContent(
|
: HTMLEditUtils::GetPreviousContent(
|
||||||
aNode, {WalkTreeOption::IgnoreDataNodeExceptText},
|
aNode, {WalkTreeOption::IgnoreDataNodeExceptText},
|
||||||
editingHost);
|
editingHost);
|
||||||
}
|
}
|
||||||
|
@ -5024,12 +5025,12 @@ nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing
|
return aNoBlockCrossing
|
||||||
? EditorBase::GetPreviousContent(
|
? HTMLEditUtils::GetPreviousContent(
|
||||||
aPoint,
|
aPoint,
|
||||||
{WalkTreeOption::IgnoreDataNodeExceptText,
|
{WalkTreeOption::IgnoreDataNodeExceptText,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetPreviousContent(
|
: HTMLEditUtils::GetPreviousContent(
|
||||||
aPoint, {WalkTreeOption::IgnoreDataNodeExceptText},
|
aPoint, {WalkTreeOption::IgnoreDataNodeExceptText},
|
||||||
editingHost);
|
editingHost);
|
||||||
}
|
}
|
||||||
|
@ -5041,12 +5042,12 @@ nsIContent* HTMLEditor::GetPreviousEditableHTMLNodeInternal(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing
|
return aNoBlockCrossing
|
||||||
? EditorBase::GetPreviousContent(
|
? HTMLEditUtils::GetPreviousContent(
|
||||||
aNode,
|
aNode,
|
||||||
{WalkTreeOption::IgnoreNonEditableNode,
|
{WalkTreeOption::IgnoreNonEditableNode,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetPreviousContent(
|
: HTMLEditUtils::GetPreviousContent(
|
||||||
aNode, {WalkTreeOption::IgnoreNonEditableNode}, editingHost);
|
aNode, {WalkTreeOption::IgnoreNonEditableNode}, editingHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5057,12 +5058,12 @@ nsIContent* HTMLEditor::GetPreviousEditableHTMLNodeInternal(
|
||||||
if (NS_WARN_IF(!editingHost)) {
|
if (NS_WARN_IF(!editingHost)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing ? EditorBase::GetPreviousContent(
|
return aNoBlockCrossing ? HTMLEditUtils::GetPreviousContent(
|
||||||
aPoint,
|
aPoint,
|
||||||
{WalkTreeOption::IgnoreNonEditableNode,
|
{WalkTreeOption::IgnoreNonEditableNode,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetPreviousContent(
|
: HTMLEditUtils::GetPreviousContent(
|
||||||
aPoint, {WalkTreeOption::IgnoreNonEditableNode},
|
aPoint, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
editingHost);
|
editingHost);
|
||||||
}
|
}
|
||||||
|
@ -5074,12 +5075,12 @@ nsIContent* HTMLEditor::GetNextHTMLElementOrTextInternal(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing
|
return aNoBlockCrossing
|
||||||
? EditorBase::GetNextContent(
|
? HTMLEditUtils::GetNextContent(
|
||||||
aNode,
|
aNode,
|
||||||
{WalkTreeOption::IgnoreDataNodeExceptText,
|
{WalkTreeOption::IgnoreDataNodeExceptText,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
aNode, {WalkTreeOption::IgnoreDataNodeExceptText},
|
aNode, {WalkTreeOption::IgnoreDataNodeExceptText},
|
||||||
editingHost);
|
editingHost);
|
||||||
}
|
}
|
||||||
|
@ -5092,12 +5093,12 @@ nsIContent* HTMLEditor::GetNextHTMLElementOrTextInternal(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing
|
return aNoBlockCrossing
|
||||||
? EditorBase::GetNextContent(
|
? HTMLEditUtils::GetNextContent(
|
||||||
aPoint,
|
aPoint,
|
||||||
{WalkTreeOption::IgnoreDataNodeExceptText,
|
{WalkTreeOption::IgnoreDataNodeExceptText,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
aPoint, {WalkTreeOption::IgnoreDataNodeExceptText},
|
aPoint, {WalkTreeOption::IgnoreDataNodeExceptText},
|
||||||
editingHost);
|
editingHost);
|
||||||
}
|
}
|
||||||
|
@ -5109,12 +5110,12 @@ nsIContent* HTMLEditor::GetNextEditableHTMLNodeInternal(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing
|
return aNoBlockCrossing
|
||||||
? EditorBase::GetNextContent(
|
? HTMLEditUtils::GetNextContent(
|
||||||
aNode,
|
aNode,
|
||||||
{WalkTreeOption::IgnoreNonEditableNode,
|
{WalkTreeOption::IgnoreNonEditableNode,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
aNode, {WalkTreeOption::IgnoreNonEditableNode}, editingHost);
|
aNode, {WalkTreeOption::IgnoreNonEditableNode}, editingHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5125,12 +5126,12 @@ nsIContent* HTMLEditor::GetNextEditableHTMLNodeInternal(
|
||||||
if (NS_WARN_IF(!editingHost)) {
|
if (NS_WARN_IF(!editingHost)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return aNoBlockCrossing ? EditorBase::GetNextContent(
|
return aNoBlockCrossing ? HTMLEditUtils::GetNextContent(
|
||||||
aPoint,
|
aPoint,
|
||||||
{WalkTreeOption::IgnoreNonEditableNode,
|
{WalkTreeOption::IgnoreNonEditableNode,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
aPoint, {WalkTreeOption::IgnoreNonEditableNode},
|
aPoint, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
editingHost);
|
editingHost);
|
||||||
}
|
}
|
||||||
|
|
|
@ -978,8 +978,8 @@ class HTMLEditor final : public TextEditor,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetPreviousEditableHTMLNode*() methods are similar to
|
* GetPreviousEditableHTMLNode*() methods are similar to
|
||||||
* EditorBase::GetPreviousContent({WalkTreeOption::IgnoreNonEditableNode}) but
|
* HTMLEditUtils::GetPreviousContent({WalkTreeOption::IgnoreNonEditableNode})
|
||||||
* this won't return nodes outside active editing host.
|
* but this won't return nodes outside active editing host.
|
||||||
*/
|
*/
|
||||||
nsIContent* GetPreviousEditableHTMLNode(nsINode& aNode) const {
|
nsIContent* GetPreviousEditableHTMLNode(nsINode& aNode) const {
|
||||||
return GetPreviousEditableHTMLNodeInternal(aNode, false);
|
return GetPreviousEditableHTMLNodeInternal(aNode, false);
|
||||||
|
@ -1047,7 +1047,7 @@ class HTMLEditor final : public TextEditor,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetNextEditableHTMLNode*() methods are similar to
|
* GetNextEditableHTMLNode*() methods are similar to
|
||||||
* EditorBase::GetNextContent({WalkTreeOption::IgnoreNonEditableNode}) but
|
* HTMLEditUtils::GetNextContent({WalkTreeOption::IgnoreNonEditableNode}) but
|
||||||
* this won't return nodes outside active editing host.
|
* this won't return nodes outside active editing host.
|
||||||
*
|
*
|
||||||
* Note that same as EditorBase::GetTextEditableNode(), methods which take
|
* Note that same as EditorBase::GetTextEditableNode(), methods which take
|
||||||
|
|
|
@ -50,6 +50,7 @@ using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
|
||||||
using InvisibleWhiteSpaces = HTMLEditUtils::InvisibleWhiteSpaces;
|
using InvisibleWhiteSpaces = HTMLEditUtils::InvisibleWhiteSpaces;
|
||||||
using StyleDifference = HTMLEditUtils::StyleDifference;
|
using StyleDifference = HTMLEditUtils::StyleDifference;
|
||||||
using TableBoundary = HTMLEditUtils::TableBoundary;
|
using TableBoundary = HTMLEditUtils::TableBoundary;
|
||||||
|
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
|
||||||
|
|
||||||
template nsresult HTMLEditor::DeleteTextAndTextNodesWithTransaction(
|
template nsresult HTMLEditor::DeleteTextAndTextNodesWithTransaction(
|
||||||
const EditorDOMPoint& aStartPoint, const EditorDOMPoint& aEndPoint,
|
const EditorDOMPoint& aStartPoint, const EditorDOMPoint& aEndPoint,
|
||||||
|
@ -3897,9 +3898,9 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteRangesWithTransaction(
|
||||||
if (howToHandleCollapsedRange ==
|
if (howToHandleCollapsedRange ==
|
||||||
EditorBase::HowToHandleCollapsedRange::ExtendBackward &&
|
EditorBase::HowToHandleCollapsedRange::ExtendBackward &&
|
||||||
caretPoint.IsStartOfContainer()) {
|
caretPoint.IsStartOfContainer()) {
|
||||||
nsIContent* previousEditableContent = EditorBase::GetPreviousContent(
|
nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousContent(
|
||||||
*caretPoint.GetContainer(),
|
*caretPoint.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
{EditorBase::WalkTreeOption::IgnoreNonEditableNode}, editingHost);
|
editingHost);
|
||||||
if (!previousEditableContent) {
|
if (!previousEditableContent) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -3920,9 +3921,9 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteRangesWithTransaction(
|
||||||
if (howToHandleCollapsedRange ==
|
if (howToHandleCollapsedRange ==
|
||||||
EditorBase::HowToHandleCollapsedRange::ExtendForward &&
|
EditorBase::HowToHandleCollapsedRange::ExtendForward &&
|
||||||
caretPoint.IsEndOfContainer()) {
|
caretPoint.IsEndOfContainer()) {
|
||||||
nsIContent* nextEditableContent = EditorBase::GetNextContent(
|
nsIContent* nextEditableContent = HTMLEditUtils::GetNextContent(
|
||||||
*caretPoint.GetContainer(),
|
*caretPoint.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
{EditorBase::WalkTreeOption::IgnoreNonEditableNode}, editingHost);
|
editingHost);
|
||||||
if (!nextEditableContent) {
|
if (!nextEditableContent) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -3957,13 +3958,11 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteRangesWithTransaction(
|
||||||
nsIContent* editableContent =
|
nsIContent* editableContent =
|
||||||
howToHandleCollapsedRange ==
|
howToHandleCollapsedRange ==
|
||||||
EditorBase::HowToHandleCollapsedRange::ExtendBackward
|
EditorBase::HowToHandleCollapsedRange::ExtendBackward
|
||||||
? EditorBase::GetPreviousContent(
|
? HTMLEditUtils::GetPreviousContent(
|
||||||
caretPoint,
|
caretPoint, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
{EditorBase::WalkTreeOption::IgnoreNonEditableNode},
|
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
caretPoint,
|
caretPoint, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
{EditorBase::WalkTreeOption::IgnoreNonEditableNode},
|
|
||||||
editingHost);
|
editingHost);
|
||||||
if (!editableContent) {
|
if (!editableContent) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -3973,10 +3972,10 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteRangesWithTransaction(
|
||||||
editableContent =
|
editableContent =
|
||||||
howToHandleCollapsedRange ==
|
howToHandleCollapsedRange ==
|
||||||
EditorBase::HowToHandleCollapsedRange::ExtendBackward
|
EditorBase::HowToHandleCollapsedRange::ExtendBackward
|
||||||
? EditorBase::GetPreviousContent(
|
? HTMLEditUtils::GetPreviousContent(
|
||||||
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
editingHost)
|
editingHost)
|
||||||
: EditorBase::GetNextContent(
|
: HTMLEditUtils::GetNextContent(
|
||||||
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
*editableContent, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
editingHost);
|
editingHost);
|
||||||
}
|
}
|
||||||
|
@ -4428,7 +4427,7 @@ nsresult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
|
||||||
atStart.IsEndOfContainer() && range.StartRef().GetChild() &&
|
atStart.IsEndOfContainer() && range.StartRef().GetChild() &&
|
||||||
range.StartRef().GetChild()->IsHTMLElement(nsGkAtoms::br) &&
|
range.StartRef().GetChild()->IsHTMLElement(nsGkAtoms::br) &&
|
||||||
!aHTMLEditor.IsVisibleBRElement(range.StartRef().GetChild())
|
!aHTMLEditor.IsVisibleBRElement(range.StartRef().GetChild())
|
||||||
? EditorBase::GetNextContent(
|
? HTMLEditUtils::GetNextContent(
|
||||||
*atStart.ContainerAsContent(),
|
*atStart.ContainerAsContent(),
|
||||||
{WalkTreeOption::IgnoreDataNodeExceptText,
|
{WalkTreeOption::IgnoreDataNodeExceptText,
|
||||||
WalkTreeOption::StopAtBlockBoundary},
|
WalkTreeOption::StopAtBlockBoundary},
|
||||||
|
@ -5177,7 +5176,7 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler::
|
||||||
EditorDOMPoint afterEmptyBlock(
|
EditorDOMPoint afterEmptyBlock(
|
||||||
EditorRawDOMPoint::After(mEmptyInclusiveAncestorBlockElement));
|
EditorRawDOMPoint::After(mEmptyInclusiveAncestorBlockElement));
|
||||||
MOZ_ASSERT(afterEmptyBlock.IsSet());
|
MOZ_ASSERT(afterEmptyBlock.IsSet());
|
||||||
if (nsIContent* nextContentOfEmptyBlock = EditorBase::GetNextContent(
|
if (nsIContent* nextContentOfEmptyBlock = HTMLEditUtils::GetNextContent(
|
||||||
afterEmptyBlock, {}, aHTMLEditor.GetActiveEditingHost())) {
|
afterEmptyBlock, {}, aHTMLEditor.GetActiveEditingHost())) {
|
||||||
EditorDOMPoint pt = aHTMLEditor.GetGoodCaretPointFor(
|
EditorDOMPoint pt = aHTMLEditor.GetGoodCaretPointFor(
|
||||||
*nextContentOfEmptyBlock, aDirectionAndAmount);
|
*nextContentOfEmptyBlock, aDirectionAndAmount);
|
||||||
|
@ -5199,7 +5198,7 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler::
|
||||||
// if there is. Otherwise, to after the empty block.
|
// if there is. Otherwise, to after the empty block.
|
||||||
EditorRawDOMPoint atEmptyBlock(mEmptyInclusiveAncestorBlockElement);
|
EditorRawDOMPoint atEmptyBlock(mEmptyInclusiveAncestorBlockElement);
|
||||||
if (nsIContent* previousContentOfEmptyBlock =
|
if (nsIContent* previousContentOfEmptyBlock =
|
||||||
EditorBase::GetPreviousContent(
|
HTMLEditUtils::GetPreviousContent(
|
||||||
atEmptyBlock, {WalkTreeOption::IgnoreNonEditableNode},
|
atEmptyBlock, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
aHTMLEditor.GetActiveEditingHost())) {
|
aHTMLEditor.GetActiveEditingHost())) {
|
||||||
EditorDOMPoint pt = aHTMLEditor.GetGoodCaretPointFor(
|
EditorDOMPoint pt = aHTMLEditor.GetGoodCaretPointFor(
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
using EditorType = EditorUtils::EditorType;
|
using EditorType = EditorUtils::EditorType;
|
||||||
|
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* ListElementSelectionState
|
* ListElementSelectionState
|
||||||
|
@ -247,8 +248,8 @@ AlignStateAtSelection::AlignStateAtSelection(HTMLEditor& aHTMLEditor,
|
||||||
else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
|
else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
|
||||||
atBodyOrDocumentElement.IsSet() &&
|
atBodyOrDocumentElement.IsSet() &&
|
||||||
atStartOfSelection.Offset() == atBodyOrDocumentElement.Offset()) {
|
atStartOfSelection.Offset() == atBodyOrDocumentElement.Offset()) {
|
||||||
editTargetContent = EditorBase::GetNextContent(
|
editTargetContent = HTMLEditUtils::GetNextContent(
|
||||||
atStartOfSelection, {EditorBase::WalkTreeOption::IgnoreNonEditableNode},
|
atStartOfSelection, {WalkTreeOption::IgnoreNonEditableNode},
|
||||||
aHTMLEditor.GetActiveEditingHost());
|
aHTMLEditor.GetActiveEditingHost());
|
||||||
if (NS_WARN_IF(!editTargetContent)) {
|
if (NS_WARN_IF(!editTargetContent)) {
|
||||||
aRv.Throw(NS_ERROR_FAILURE);
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче