--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -46,448 +46,16 @@ static NS_DEFINE_CID(kCPreContentIterato
#define CH_RIGHT_DOUBLE_QUOTE ((char16_t)0x201D)
#define CH_SHY ((char16_t)0xAD)
// nsFind::Find casts CH_SHY to char before calling StripChars
// This works correctly if and only if CH_SHY <= 255
static_assert(CH_SHY <= 255, "CH_SHY is not an ascii character");
-// nsFindContentIterator is a special iterator that also goes through any
-// existing <textarea>'s or text <input>'s editor to lookup the anonymous DOM
-// content there.
-//
-// Details:
-// 1) We use two iterators: The "outer-iterator" goes through the normal DOM.
-// The "inner-iterator" goes through the anonymous DOM inside the editor.
-//
-// 2) [MaybeSetupInnerIterator] As soon as the outer-iterator's current node is
-// changed, a check is made to see if the node is a <textarea> or a text <input>
-// node. If so, an inner-iterator is created to lookup the anynomous contents of
-// the editor underneath the text control.
-//
-// 3) When the inner-iterator is created, we position the outer-iterator 'after'
-// (or 'before' in backward search) the text control to avoid revisiting that
-// control.
-//
-// 4) As a consequence of searching through text controls, we can be called via
-// FindNext with the current selection inside a <textarea> or a text <input>.
-// This means that we can be given an initial search range that stretches across
-// the anonymous DOM and the normal DOM. To cater for this situation, we split
-// the anonymous part into the inner-iterator and then reposition the outer-
-// iterator outside.
-//
-// 5) The implementation assumes that First() and Next() are only called in
-// find-forward mode, while Last() and Prev() are used in find-backward.
-
-class nsFindContentIterator final : public nsIContentIterator
-{
-public:
- explicit nsFindContentIterator(bool aFindBackward)
- : mStartOffset(0)
- , mEndOffset(0)
- , mFindBackward(aFindBackward)
- {
- }
-
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_CLASS(nsFindContentIterator)
-
- // nsIContentIterator
- virtual nsresult Init(nsINode* aRoot) override
- {
- MOZ_ASSERT_UNREACHABLE("internal error");
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- virtual nsresult Init(nsRange* aRange) override
- {
- MOZ_ASSERT_UNREACHABLE("internal error");
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
- nsINode* aEndContainer, uint32_t aEndOffset) override
- {
- MOZ_ASSERT_UNREACHABLE("internal error");
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- virtual nsresult Init(const RawRangeBoundary& aStart,
- const RawRangeBoundary& aEnd) override
- {
- MOZ_ASSERT_UNREACHABLE("internal error");
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- // Not a range because one of the endpoints may be anonymous.
- nsresult Init(nsINode* aStartNode, int32_t aStartOffset,
- nsINode* aEndNode, int32_t aEndOffset);
- virtual void First() override;
- virtual void Last() override;
- virtual void Next() override;
- virtual void Prev() override;
- virtual nsINode* GetCurrentNode() override;
- virtual bool IsDone() override;
- virtual nsresult PositionAt(nsINode* aCurNode) override;
-
- void Reset();
-protected:
- virtual ~nsFindContentIterator() {}
-
-private:
- static already_AddRefed<nsRange> CreateRange(nsINode* aNode)
- {
- RefPtr<nsRange> range = new nsRange(aNode);
- range->SetMaySpanAnonymousSubtrees(true);
- return range.forget();
- }
-
- nsCOMPtr<nsIContentIterator> mOuterIterator;
- nsCOMPtr<nsIContentIterator> mInnerIterator;
- // Can't use a range here, since we want to represent part of the flattened
- // tree, including native anonymous content.
- nsCOMPtr<nsINode> mStartNode;
- int32_t mStartOffset;
- nsCOMPtr<nsINode> mEndNode;
- int32_t mEndOffset;
-
- nsCOMPtr<nsIContent> mStartOuterContent;
- nsCOMPtr<nsIContent> mEndOuterContent;
- bool mFindBackward;
-
- void MaybeSetupInnerIterator();
- void SetupInnerIterator(nsIContent* aContent);
-};
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFindContentIterator)
- NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFindContentIterator)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFindContentIterator)
-
-NS_IMPL_CYCLE_COLLECTION(nsFindContentIterator, mOuterIterator, mInnerIterator,
- mStartOuterContent, mEndOuterContent, mEndNode,
- mStartNode)
-
-nsresult
-nsFindContentIterator::Init(nsINode* aStartNode, int32_t aStartOffset,
- nsINode* aEndNode, int32_t aEndOffset)
-{
- NS_ENSURE_ARG_POINTER(aStartNode);
- NS_ENSURE_ARG_POINTER(aEndNode);
- if (!mOuterIterator) {
- if (mFindBackward) {
- // Use post-order in the reverse case, so we get parents before children
- // in case we want to prevent descending into a node.
- mOuterIterator = do_CreateInstance(kCContentIteratorCID);
- } else {
- // Use pre-order in the forward case, so we get parents before children in
- // case we want to prevent descending into a node.
- mOuterIterator = do_CreateInstance(kCPreContentIteratorCID);
- }
- NS_ENSURE_ARG_POINTER(mOuterIterator);
- }
-
- // Set up the search "range" that we will examine
- mStartNode = aStartNode;
- mStartOffset = aStartOffset;
- mEndNode = aEndNode;
- mEndOffset = aEndOffset;
-
- return NS_OK;
-}
-
-void
-nsFindContentIterator::First()
-{
- Reset();
-}
-
-void
-nsFindContentIterator::Last()
-{
- Reset();
-}
-
-void
-nsFindContentIterator::Next()
-{
- if (mInnerIterator) {
- mInnerIterator->Next();
- if (!mInnerIterator->IsDone()) {
- return;
- }
-
- // by construction, mOuterIterator is already on the next node
- } else {
- mOuterIterator->Next();
- }
- MaybeSetupInnerIterator();
-}
-
-void
-nsFindContentIterator::Prev()
-{
- if (mInnerIterator) {
- mInnerIterator->Prev();
- if (!mInnerIterator->IsDone()) {
- return;
- }
-
- // by construction, mOuterIterator is already on the previous node
- } else {
- mOuterIterator->Prev();
- }
- MaybeSetupInnerIterator();
-}
-
-nsINode*
-nsFindContentIterator::GetCurrentNode()
-{
- if (mInnerIterator && !mInnerIterator->IsDone()) {
- return mInnerIterator->GetCurrentNode();
- }
- return mOuterIterator->GetCurrentNode();
-}
-
-bool
-nsFindContentIterator::IsDone()
-{
- if (mInnerIterator && !mInnerIterator->IsDone()) {
- return false;
- }
- return mOuterIterator->IsDone();
-}
-
-static nsIContent&
-AnonymousSubtreeRootParent(nsINode& aNode)
-{
- MOZ_ASSERT(aNode.IsInNativeAnonymousSubtree());
-
- nsIContent* current = aNode.GetParent();
- while (current->IsInNativeAnonymousSubtree()) {
- current = current->GetParent();
- MOZ_ASSERT(current, "huh?");
- }
- return *current;
-}
-
-nsresult
-nsFindContentIterator::PositionAt(nsINode* aCurNode)
-{
- nsresult rv = mOuterIterator->PositionAt(aCurNode);
- if (NS_SUCCEEDED(rv)) {
- MaybeSetupInnerIterator();
- return rv;
- }
-
- // If this failed, it means that aCurNode is necessarily anonymous.
- nsIContent& nonAnonNode = AnonymousSubtreeRootParent(*aCurNode);
- SetupInnerIterator(&nonAnonNode);
- MOZ_ASSERT(mInnerIterator, "How did we have an anonymous node otherwise?");
- rv = mInnerIterator->PositionAt(aCurNode);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- if (!mOuterIterator->IsDone()) {
- if (mFindBackward) {
- mOuterIterator->Last();
- } else {
- mOuterIterator->First();
- }
- }
- return rv;
-}
-
-void
-nsFindContentIterator::Reset()
-{
- mInnerIterator = nullptr;
- mStartOuterContent = nullptr;
- mEndOuterContent = nullptr;
-
- // As a consequence of searching through text controls, we may have been
- // initialized with a selection inside a <textarea> or a text <input>.
-
- // see if the start node is an anonymous text node inside a text control
- nsCOMPtr<nsIContent> startContent(do_QueryInterface(mStartNode));
- if (startContent) {
- mStartOuterContent = startContent->FindFirstNonChromeOnlyAccessContent();
- }
-
- // see if the end node is an anonymous text node inside a text control
- nsCOMPtr<nsIContent> endContent(do_QueryInterface(mEndNode));
- if (endContent) {
- mEndOuterContent = endContent->FindFirstNonChromeOnlyAccessContent();
- }
-
- // Note: OK to just set up the outer iterator here; if our range has a native
- // anonymous endpoint we'll end up setting up an inner iterator, and reset the
- // outer one in the process.
- nsCOMPtr<nsINode> node = mStartNode;
- NS_ENSURE_TRUE_VOID(node);
-
- RefPtr<nsRange> range = CreateRange(node);
- range->SetStart(*mStartNode, mStartOffset, IgnoreErrors());
- range->SetEnd(*mEndNode, mEndOffset, IgnoreErrors());
- mOuterIterator->Init(range);
-
- if (!mFindBackward) {
- if (mStartOuterContent != startContent) {
- // the start node was an anonymous text node
- SetupInnerIterator(mStartOuterContent);
- if (mInnerIterator) {
- mInnerIterator->First();
- }
- }
- if (!mOuterIterator->IsDone()) {
- mOuterIterator->First();
- }
- } else {
- if (mEndOuterContent != endContent) {
- // the end node was an anonymous text node
- SetupInnerIterator(mEndOuterContent);
- if (mInnerIterator) {
- mInnerIterator->Last();
- }
- }
- if (!mOuterIterator->IsDone()) {
- mOuterIterator->Last();
- }
- }
-
- // if we didn't create an inner-iterator, the boundary node could still be
- // a text control, in which case we also need an inner-iterator straightaway
- if (!mInnerIterator) {
- MaybeSetupInnerIterator();
- }
-}
-
-void
-nsFindContentIterator::MaybeSetupInnerIterator()
-{
- mInnerIterator = nullptr;
-
- nsCOMPtr<nsIContent> content =
- do_QueryInterface(mOuterIterator->GetCurrentNode());
- if (!content || !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
- return;
- }
-
- nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
- if (!formControl->IsTextControl(true)) {
- return;
- }
-
- SetupInnerIterator(content);
- if (mInnerIterator) {
- if (!mFindBackward) {
- mInnerIterator->First();
- // finish setup: position mOuterIterator on the actual "next" node (this
- // completes its re-init, @see SetupInnerIterator)
- if (!mOuterIterator->IsDone()) {
- mOuterIterator->First();
- }
- } else {
- mInnerIterator->Last();
- // finish setup: position mOuterIterator on the actual "previous" node
- // (this completes its re-init, @see SetupInnerIterator)
- if (!mOuterIterator->IsDone()) {
- mOuterIterator->Last();
- }
- }
- }
-}
-
-void
-nsFindContentIterator::SetupInnerIterator(nsIContent* aContent)
-{
- if (!aContent) {
- return;
- }
- NS_ASSERTION(!aContent->IsRootOfNativeAnonymousSubtree(), "invalid call");
-
- nsITextControlFrame* tcFrame = do_QueryFrame(aContent->GetPrimaryFrame());
- if (!tcFrame) {
- return;
- }
-
- // don't mess with disabled input fields
- RefPtr<TextEditor> textEditor = tcFrame->GetTextEditor();
- if (!textEditor || textEditor->IsDisabled()) {
- return;
- }
-
- RefPtr<dom::Element> rootElement = textEditor->GetRoot();
-
- if (!rootElement) {
- return;
- }
-
- RefPtr<nsRange> innerRange = CreateRange(aContent);
- RefPtr<nsRange> outerRange = CreateRange(aContent);
- if (!innerRange || !outerRange) {
- return;
- }
-
- // now create the inner-iterator
- mInnerIterator = do_CreateInstance(kCPreContentIteratorCID);
-
- if (mInnerIterator) {
- innerRange->SelectNodeContents(*rootElement, IgnoreErrors());
-
- // fix up the inner bounds, we may have to only lookup a portion
- // of the text control if the current node is a boundary point
- if (aContent == mStartOuterContent) {
- innerRange->SetStart(*mStartNode, mStartOffset, IgnoreErrors());
- }
- if (aContent == mEndOuterContent) {
- innerRange->SetEnd(*mEndNode, mEndOffset, IgnoreErrors());
- }
- // Note: we just init here. We do First() or Last() later.
- mInnerIterator->Init(innerRange);
-
- // make sure to place the outer-iterator outside the text control so that we
- // don't go there again.
- IgnoredErrorResult res1, res2;
- if (!mFindBackward) { // find forward
- // cut the outer-iterator after the current node
- outerRange->SetEnd(*mEndNode, mEndOffset, res1);
- outerRange->SetStartAfter(*aContent, res2);
- } else { // find backward
- // cut the outer-iterator before the current node
- outerRange->SetStart(*mStartNode, mStartOffset, res1);
- outerRange->SetEndBefore(*aContent, res2);
- }
- if (res1.Failed() || res2.Failed()) {
- // we are done with the outer-iterator, the inner-iterator will traverse
- // what we want
- outerRange->Collapse(true);
- }
-
- // Note: we just re-init here, using the segment of our search range that
- // is yet to be visited. Thus when we later do mOuterIterator->First() [or
- // mOuterIterator->Last()], we will effectively be on the next node [or
- // the previous node] _with respect to_ the search range.
- mOuterIterator->Init(outerRange);
- }
-}
-
-nsresult
-NS_NewFindContentIterator(bool aFindBackward, nsIContentIterator** aResult)
-{
- NS_ENSURE_ARG_POINTER(aResult);
- if (!aResult) {
- return NS_ERROR_NULL_POINTER;
- }
-
- nsFindContentIterator* it = new nsFindContentIterator(aFindBackward);
- if (!it) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void**)aResult);
-}
-
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFind)
NS_INTERFACE_MAP_ENTRY(nsIFind)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFind)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFind)
@@ -503,16 +71,29 @@ nsFind::nsFind()
nsFind::~nsFind() = default;
#ifdef DEBUG_FIND
#define DEBUG_FIND_PRINTF(...) printf(__VA_ARGS__)
#else
#define DEBUG_FIND_PRINTF(...) /* nothing */
#endif
+static nsIContent&
+AnonymousSubtreeRootParent(const nsINode& aNode)
+{
+ MOZ_ASSERT(aNode.IsInNativeAnonymousSubtree());
+
+ nsIContent* current = aNode.GetParent();
+ while (current->IsInNativeAnonymousSubtree()) {
+ current = current->GetParent();
+ MOZ_ASSERT(current, "huh?");
+ }
+ return *current;
+}
+
static void
DumpNode(const nsINode* aNode)
{
#ifdef DEBUG_FIND
if (!aNode) {
printf(">>>> Node: NULL\n");
return;
}