From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- dom/events/ContentEventHandler.cpp | 3307 ++++++++++++++++++++++++++++++++++++ 1 file changed, 3307 insertions(+) create mode 100644 dom/events/ContentEventHandler.cpp (limited to 'dom/events/ContentEventHandler.cpp') diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp new file mode 100644 index 0000000000..e413e70897 --- /dev/null +++ b/dom/events/ContentEventHandler.cpp @@ -0,0 +1,3307 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ContentEventHandler.h" + +#include "mozilla/Assertions.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/ContentIterator.h" +#include "mozilla/IMEStateManager.h" +#include "mozilla/IntegerRange.h" +#include "mozilla/Maybe.h" +#include "mozilla/PresShell.h" +#include "mozilla/RangeBoundary.h" +#include "mozilla/RangeUtils.h" +#include "mozilla/TextComposition.h" +#include "mozilla/TextEditor.h" +#include "mozilla/TextEvents.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLBRElement.h" +#include "mozilla/dom/HTMLUnknownElement.h" +#include "mozilla/dom/Selection.h" +#include "mozilla/dom/Text.h" +#include "nsCaret.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsCopySupport.h" +#include "nsElementTable.h" +#include "nsFocusManager.h" +#include "nsFontMetrics.h" +#include "nsFrameSelection.h" +#include "nsIFrame.h" +#include "nsLayoutUtils.h" +#include "nsPresContext.h" +#include "nsQueryObject.h" +#include "nsRange.h" +#include "nsTextFragment.h" +#include "nsTextFrame.h" +#include "nsView.h" +#include "mozilla/ViewportUtils.h" + +#include + +// Work around conflicting define in rpcndr.h +#if defined(small) +# undef small +#endif // defined(small) + +namespace mozilla { + +using namespace dom; +using namespace widget; + +/******************************************************************/ +/* ContentEventHandler::RawRange */ +/******************************************************************/ + +void ContentEventHandler::RawRange::AssertStartIsBeforeOrEqualToEnd() { + MOZ_ASSERT( + *nsContentUtils::ComparePoints( + mStart.Container(), + *mStart.Offset(NodePosition::OffsetFilter::kValidOrInvalidOffsets), + mEnd.Container(), + *mEnd.Offset(NodePosition::OffsetFilter::kValidOrInvalidOffsets)) <= + 0); +} + +nsresult ContentEventHandler::RawRange::SetStart( + const RawRangeBoundary& aStart) { + nsINode* newRoot = RangeUtils::ComputeRootNode(aStart.Container()); + if (!newRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + + if (!aStart.IsSetAndValid()) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + // Collapse if not positioned yet, or if positioned in another document. + if (!IsPositioned() || newRoot != mRoot) { + mRoot = newRoot; + mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); + mEnd.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); + return NS_OK; + } + + mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); + AssertStartIsBeforeOrEqualToEnd(); + return NS_OK; +} + +nsresult ContentEventHandler::RawRange::SetEnd(const RawRangeBoundary& aEnd) { + nsINode* newRoot = RangeUtils::ComputeRootNode(aEnd.Container()); + if (!newRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + + if (!aEnd.IsSetAndValid()) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + // Collapse if not positioned yet, or if positioned in another document. + if (!IsPositioned() || newRoot != mRoot) { + mRoot = newRoot; + mStart.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); + mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); + return NS_OK; + } + + mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); + AssertStartIsBeforeOrEqualToEnd(); + return NS_OK; +} + +nsresult ContentEventHandler::RawRange::SetEndAfter(nsINode* aEndContainer) { + return SetEnd(RangeUtils::GetRawRangeBoundaryAfter(aEndContainer)); +} + +void ContentEventHandler::RawRange::SetStartAndEnd(const nsRange* aRange) { + DebugOnly rv = + SetStartAndEnd(aRange->StartRef().AsRaw(), aRange->EndRef().AsRaw()); + MOZ_ASSERT(!aRange->IsPositioned() || NS_SUCCEEDED(rv)); +} + +nsresult ContentEventHandler::RawRange::SetStartAndEnd( + const RawRangeBoundary& aStart, const RawRangeBoundary& aEnd) { + nsINode* newStartRoot = RangeUtils::ComputeRootNode(aStart.Container()); + if (!newStartRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + if (!aStart.IsSetAndValid()) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + if (aStart.Container() == aEnd.Container()) { + if (!aEnd.IsSetAndValid()) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + MOZ_ASSERT(*aStart.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets) <= + *aEnd.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets)); + mRoot = newStartRoot; + mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); + mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); + return NS_OK; + } + + nsINode* newEndRoot = RangeUtils::ComputeRootNode(aEnd.Container()); + if (!newEndRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + if (!aEnd.IsSetAndValid()) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + // If they have different root, this should be collapsed at the end point. + if (newStartRoot != newEndRoot) { + mRoot = newEndRoot; + mStart.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); + mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); + return NS_OK; + } + + // Otherwise, set the range as specified. + mRoot = newStartRoot; + mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); + mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); + AssertStartIsBeforeOrEqualToEnd(); + return NS_OK; +} + +nsresult ContentEventHandler::RawRange::SelectNodeContents( + const nsINode* aNodeToSelectContents) { + nsINode* const newRoot = + RangeUtils::ComputeRootNode(const_cast(aNodeToSelectContents)); + if (!newRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + mRoot = newRoot; + mStart = RangeBoundary(const_cast(aNodeToSelectContents), nullptr); + mEnd = RangeBoundary(const_cast(aNodeToSelectContents), + aNodeToSelectContents->GetLastChild()); + return NS_OK; +} + +/******************************************************************/ +/* ContentEventHandler */ +/******************************************************************/ + +// NOTE +// +// ContentEventHandler *creates* ranges as following rules: +// 1. Start of range: +// 1.1. Cases: [textNode or text[Node or textNode[ +// When text node is start of a range, start node is the text node and +// start offset is any number between 0 and the length of the text. +// 1.2. Case: [: +// When start of an element node is start of a range, start node is +// parent of the element and start offset is the element's index in the +// parent. +// 1.3. Case: [ +// When after an empty element node is start of a range, start node is +// parent of the element and start offset is the element's index in the +// parent + 1. +// 1.4. Case: [ +// When start of a non-empty element is start of a range, start node is +// the element and start offset is 0. +// 1.5. Case: [ +// When start of a range is 0 and there are no nodes causing text, +// start node is the root node and start offset is 0. +// 1.6. Case: [ +// When start of a range is out of bounds, start node is the root node +// and start offset is number of the children. +// 2. End of range: +// 2.1. Cases: ]textNode or text]Node or textNode] +// When a text node is end of a range, end node is the text node and +// end offset is any number between 0 and the length of the text. +// 2.2. Case: ] +// When before an element node (meaning before the open tag of the +// element) is end of a range, end node is previous node causing text. +// Note that this case shouldn't be handled directly. If rule 2.1 and +// 2.3 are handled correctly, the loop with ContentIterator shouldn't +// reach the element node since the loop should've finished already at +// handling the last node which caused some text. +// 2.3. Case: ] +// When a line break is caused before a non-empty element node and it's +// end of a range, end node is the element and end offset is 0. +// (i.e., including open tag of the element) +// 2.4. Cases: ] +// When after an empty element node is end of a range, end node is +// parent of the element node and end offset is the element's index in +// the parent + 1. (i.e., including close tag of the element or empty +// element) +// 2.5. Case: ] +// When end of a range is out of bounds, end node is the root node and +// end offset is number of the children. +// +// ContentEventHandler *treats* ranges as following additional rules: +// 1. When the start node is an element node which doesn't have children, +// it includes a line break caused before itself (i.e., includes its open +// tag). For example, if start position is {
, 0 }, the line break +// caused by
should be included into the flatten text. +// 2. When the end node is an element node which doesn't have children, +// it includes the end (i.e., includes its close tag except empty element). +// Although, currently, any close tags don't cause line break, this also +// includes its open tag. For example, if end position is {
, 0 }, the +// line break caused by the
should be included into the flatten text. + +ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext) + : mDocument(aPresContext->Document()) {} + +nsresult ContentEventHandler::InitBasic(bool aRequireFlush) { + NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE); + if (aRequireFlush) { + // If text frame which has overflowing selection underline is dirty, + // we need to flush the pending reflow here. + mDocument->FlushPendingNotifications(FlushType::Layout); + } + return NS_OK; +} + +nsresult ContentEventHandler::InitRootContent( + const Selection& aNormalSelection) { + // Root content should be computed with normal selection because normal + // selection is typically has at least one range but the other selections + // not so. If there is a range, computing its root is easy, but if + // there are no ranges, we need to use ancestor limit instead. + MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal); + + if (!aNormalSelection.RangeCount()) { + // If there is no selection range, we should compute the selection root + // from ancestor limiter or root content of the document. + mRootElement = + Element::FromNodeOrNull(aNormalSelection.GetAncestorLimiter()); + if (!mRootElement) { + mRootElement = mDocument->GetRootElement(); + if (NS_WARN_IF(!mRootElement)) { + return NS_ERROR_NOT_AVAILABLE; + } + } + return NS_OK; + } + + RefPtr range(aNormalSelection.GetRangeAt(0)); + if (NS_WARN_IF(!range)) { + return NS_ERROR_UNEXPECTED; + } + + // If there is a selection, we should retrieve the selection root from + // the range since when the window is inactivated, the ancestor limiter + // of selection was cleared by blur event handler of EditorBase but the + // selection range still keeps storing the nodes. If the active element of + // the deactive window is or