--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -452,31 +452,75 @@ NS_NewFindContentIterator(bool aFindBack
nsFindContentIterator* it = new nsFindContentIterator(aFindBackward);
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void**)aResult);
}
+struct nsFind::State final
+{
+ // Disallow copying because copying the iterator would be a lie.
+ State(const State&) = delete;
+ State() = default;
+
+ int32_t mIterOffset = 0;
+
+ // TODO(emilio): I'm reasonably sure these can be weak pointer, since we don't
+ // mutate the DOM.
+ nsCOMPtr<nsINode> mIterNode;
+ nsCOMPtr<nsIContent> mLastBlockParent;
+
+ RefPtr<nsFindContentIterator> mIterator;
+};
+
+class MOZ_STACK_CLASS nsFind::StateRestorer final
+{
+public:
+ explicit StateRestorer(State& aState)
+ : mState(aState)
+ , mIterOffset(aState.mIterOffset)
+ , mIterNode(aState.mIterNode)
+ , mCurrNode(aState.mIterator->GetCurrentNode())
+ , mLastBlockParent(aState.mLastBlockParent)
+ {
+ }
+
+ ~StateRestorer()
+ {
+ mState.mIterOffset = mIterOffset;
+ mState.mIterNode = mIterNode;
+ mState.mLastBlockParent = mLastBlockParent;
+ mState.mIterator->PositionAt(mCurrNode);
+ }
+
+private:
+ State& mState;
+
+ int32_t mIterOffset;
+ nsCOMPtr<nsINode> mIterNode;
+ nsCOMPtr<nsINode> mCurrNode;
+ nsCOMPtr<nsIContent> mLastBlockParent;
+};
+
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)
-NS_IMPL_CYCLE_COLLECTION(nsFind, mLastBlockParent, mIterNode, mIterator)
+NS_IMPL_CYCLE_COLLECTION(nsFind)
nsFind::nsFind()
: mFindBackward(false)
, mCaseSensitive(false)
, mWordBreaker(nullptr)
- , mIterOffset(0)
{
}
nsFind::~nsFind() = default;
#ifdef DEBUG_FIND
#define DEBUG_FIND_PRINTF(...) printf(__VA_ARGS__)
#else
@@ -577,43 +621,46 @@ SkipNode(nsIContent* aContent)
content = content->GetParent();
}
return false;
}
nsresult
-nsFind::InitIterator(nsINode* aStartNode, int32_t aStartOffset,
- nsINode* aEndNode, int32_t aEndOffset)
+nsFind::InitIterator(State& aState,
+ nsINode* aStartNode,
+ int32_t aStartOffset,
+ nsINode* aEndNode,
+ int32_t aEndOffset)
{
- if (!mIterator) {
- mIterator = new nsFindContentIterator(mFindBackward);
- NS_ENSURE_TRUE(mIterator, NS_ERROR_OUT_OF_MEMORY);
+ if (!aState.mIterator) {
+ aState.mIterator = new nsFindContentIterator(mFindBackward);
}
NS_ENSURE_ARG_POINTER(aStartNode);
NS_ENSURE_ARG_POINTER(aEndNode);
#ifdef DEBUG_FIND
DEBUG_FIND_PRINTF("InitIterator search range:\n");
DEBUG_FIND_PRINTF(" -- start %d, ", aStartOffset);
nsCOMPtr<nsINode> start = do_QueryInterface(aStartNode);
DumpNode(start);
DEBUG_FIND_PRINTF(" -- end %d, ", aEndOffset);
nsCOMPtr<nsINode> end = do_QueryInterface(aEndNode);
DumpNode(end);
#endif
- nsresult rv = mIterator->Init(aStartNode, aStartOffset, aEndNode, aEndOffset);
+ nsresult rv =
+ aState.mIterator->Init(aStartNode, aStartOffset, aEndNode, aEndOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (mFindBackward) {
- mIterator->Last();
+ aState.mIterator->Last();
} else {
- mIterator->First();
+ aState.mIterator->First();
}
return NS_OK;
}
NS_IMETHODIMP
nsFind::GetFindBackwards(bool* aFindBackward)
{
if (!aFindBackward) {
@@ -675,25 +722,27 @@ nsFind::SetEntireWord(bool aEntireWord)
// "match anchor" node and offset.
//
// Text nodes store their text in an nsTextFragment, which is effectively a
// union of a one-byte string or a two-byte string. Single and double strings
// are intermixed in the dom. We don't have string classes which can deal with
// intermixed strings, so all the handling is done explicitly here.
nsresult
-nsFind::NextNode(nsRange* aSearchRange,
- nsRange* aStartPoint, nsRange* aEndPoint,
+nsFind::NextNode(State& aState,
+ nsRange* aSearchRange,
+ nsRange* aStartPoint,
+ nsRange* aEndPoint,
bool aContinueOk)
{
nsresult rv;
nsCOMPtr<nsIContent> content;
- if (!mIterator || aContinueOk) {
+ if (!aState.mIterator || aContinueOk) {
// If we are continuing, that means we have a match in progress. In that
// case, we want to continue from the end point (where we are now) to the
// beginning/end of the search range.
nsCOMPtr<nsINode> startNode;
nsCOMPtr<nsINode> endNode;
uint32_t startOffset, endOffset;
if (aContinueOk) {
DEBUG_FIND_PRINTF("Match in progress: continuing past endpoint\n");
@@ -723,61 +772,64 @@ nsFind::NextNode(nsRange* aSearchRange,
} else { // forward
startNode = aStartPoint->GetStartContainer();
startOffset = aStartPoint->StartOffset();
endNode = aEndPoint->GetEndContainer();
endOffset = aEndPoint->EndOffset();
}
}
- rv = InitIterator(startNode, static_cast<int32_t>(startOffset),
- endNode, static_cast<int32_t>(endOffset));
+ rv = InitIterator(aState,
+ startNode,
+ static_cast<int32_t>(startOffset),
+ endNode,
+ static_cast<int32_t>(endOffset));
NS_ENSURE_SUCCESS(rv, rv);
if (!aStartPoint) {
aStartPoint = aSearchRange;
}
- content = do_QueryInterface(mIterator->GetCurrentNode());
+ content = do_QueryInterface(aState.mIterator->GetCurrentNode());
DEBUG_FIND_PRINTF(":::::: Got the first node ");
DumpNode(content);
if (content && content->IsText() && !SkipNode(content)) {
- mIterNode = content;
+ aState.mIterNode = content;
// Also set mIterOffset if appropriate:
nsCOMPtr<nsINode> node;
if (mFindBackward) {
node = aStartPoint->GetEndContainer();
- if (mIterNode == node) {
+ if (aState.mIterNode == node) {
uint32_t endOffset = aStartPoint->EndOffset();
- mIterOffset = static_cast<int32_t>(endOffset);
+ aState.mIterOffset = static_cast<int32_t>(endOffset);
} else {
- mIterOffset = -1; // sign to start from end
+ aState.mIterOffset = -1; // sign to start from end
}
} else {
node = aStartPoint->GetStartContainer();
- if (mIterNode == node) {
+ if (aState.mIterNode == node) {
uint32_t startOffset = aStartPoint->StartOffset();
- mIterOffset = static_cast<int32_t>(startOffset);
+ aState.mIterOffset = static_cast<int32_t>(startOffset);
} else {
- mIterOffset = 0;
+ aState.mIterOffset = 0;
}
}
- DEBUG_FIND_PRINTF("Setting initial offset to %d\n", mIterOffset);
+ DEBUG_FIND_PRINTF("Setting initial offset to %d\n", aState.mIterOffset);
return NS_OK;
}
}
while (true) {
if (mFindBackward) {
- mIterator->Prev();
+ aState.mIterator->Prev();
} else {
- mIterator->Next();
+ aState.mIterator->Next();
}
- content = do_QueryInterface(mIterator->GetCurrentNode());
+ content = do_QueryInterface(aState.mIterator->GetCurrentNode());
if (!content) {
break;
}
DEBUG_FIND_PRINTF(":::::: Got another node ");
DumpNode(content);
// If we ever cross a block node, we might want to reset the match anchor:
@@ -796,77 +848,52 @@ nsFind::NextNode(nsRange* aSearchRange,
if (content->IsText()) {
break;
}
DEBUG_FIND_PRINTF("Not a text node: ");
DumpNode(content);
}
- mIterNode = content;
- mIterOffset = -1;
+ // FIXME(emilio): Is there a case for mIterNode !=
+ // mIterator->GetCurrentNode()? If not, why does it exist?
+ aState.mIterNode = content;
+ aState.mIterOffset = -1;
DEBUG_FIND_PRINTF("Iterator gave: ");
- DumpNode(mIterNode);
+ DumpNode(aState.mIterNode);
return NS_OK;
}
-class MOZ_STACK_CLASS PeekNextCharRestoreState final
-{
-public:
- explicit PeekNextCharRestoreState(nsFind* aFind)
- : mIterOffset(aFind->mIterOffset),
- mIterNode(aFind->mIterNode),
- mCurrNode(aFind->mIterator->GetCurrentNode()),
- mFind(aFind)
- {
- }
-
- ~PeekNextCharRestoreState()
- {
- mFind->mIterOffset = mIterOffset;
- mFind->mIterNode = mIterNode;
- mFind->mIterator->PositionAt(mCurrNode);
- }
-
-private:
- int32_t mIterOffset;
- nsCOMPtr<nsINode> mIterNode;
- nsCOMPtr<nsINode> mCurrNode;
- RefPtr<nsFind> mFind;
-};
-
char16_t
-nsFind::PeekNextChar(nsRange* aSearchRange,
+nsFind::PeekNextChar(State& aState,
+ nsRange* aSearchRange,
nsRange* aStartPoint,
nsRange* aEndPoint)
{
- // We need to restore the necessary member variables before this function
- // returns.
- PeekNextCharRestoreState restoreState(this);
+ // We need to restore the necessary state before this function returns.
+ StateRestorer restorer(aState);
- nsCOMPtr<nsIContent> tc;
const nsTextFragment *frag;
int32_t fragLen;
// Loop through non-block nodes until we find one that's not empty.
do {
- tc = nullptr;
- NextNode(aSearchRange, aStartPoint, aEndPoint, false);
+ NextNode(aState, aSearchRange, aStartPoint, aEndPoint, false);
// Get the text content:
- tc = do_QueryInterface(mIterNode);
+ nsCOMPtr<nsIContent> tc = do_QueryInterface(aState.mIterNode);
// Get the block parent.
- nsIContent* blockParent = GetBlockParent(mIterNode);
+ nsIContent* blockParent = GetBlockParent(aState.mIterNode);
if (!blockParent)
return L'\0';
// If out of nodes or in new parent.
- if (!mIterNode || !tc || (blockParent != mLastBlockParent))
+ if (!aState.mIterNode || !tc || blockParent != aState.mLastBlockParent)
return L'\0';
frag = tc->GetText();
fragLen = frag->GetLength();
} while (fragLen <= 0);
const char16_t *t2b = nullptr;
const char *t1b = nullptr;
@@ -895,26 +922,16 @@ nsFind::GetBlockParent(nsINode* aNode)
current = current->GetParent()) {
if (IsBlockNode(current)) {
return current;
}
}
return nullptr;
}
-// Call ResetAll before returning, to remove all references to external objects.
-void
-nsFind::ResetAll()
-{
- mIterator = nullptr;
- mLastBlockParent = nullptr;
- mIterNode = nullptr;
- mIterOffset = -1;
-}
-
#define NBSP_CHARCODE (CHAR_TO_UNICHAR(160))
#define IsSpace(c) (nsCRT::IsAsciiSpace(c) || (c) == NBSP_CHARCODE)
#define OVERFLOW_PINDEX (mFindBackward ? pindex < 0 : pindex > patLen)
#define DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen)
#define ALMOST_DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen - 1)
// Take nodes out of the tree with NextNode, until null (NextNode will return 0
// at the end of our range).
@@ -933,18 +950,16 @@ nsFind::Find(const char16_t* aPatText, n
NS_ENSURE_ARG(aEndPoint);
NS_ENSURE_ARG_POINTER(aRangeRet);
*aRangeRet = 0;
if (!aPatText) {
return NS_ERROR_NULL_POINTER;
}
- ResetAll();
-
nsAutoString patAutoStr(aPatText);
if (!mCaseSensitive) {
ToLowerCase(patAutoStr);
}
// Ignore soft hyphens in the pattern
static const char kShy[] = { char(CH_SHY), 0 };
patAutoStr.StripChars(kShy);
@@ -982,90 +997,89 @@ nsFind::Find(const char16_t* aPatText, n
// Get the end point, so we know when to end searches:
nsCOMPtr<nsINode> endNode = aEndPoint->GetEndContainer();;
uint32_t endOffset = aEndPoint->EndOffset();
char16_t c = 0;
char16_t patc = 0;
char16_t prevChar = 0;
char16_t prevCharInMatch = 0;
+
+ State state;
+
while (1) {
DEBUG_FIND_PRINTF("Loop ...\n");
// If this is our first time on a new node, reset the pointers:
if (!frag) {
tc = nullptr;
- NextNode(aSearchRange, aStartPoint, aEndPoint, false);
- if (!mIterNode) { // Out of nodes
+ NextNode(state, aSearchRange, aStartPoint, aEndPoint, false);
+ if (!state.mIterNode) { // Out of nodes
// Are we in the middle of a match? If so, try again with continuation.
+ //
+ // FIXME(emilio): If we return here unconditionally, why is this useful?
+ // Shouldn't we check `state.mIterNode` again?
if (matchAnchorNode) {
- NextNode(aSearchRange, aStartPoint, aEndPoint, true);
+ NextNode(state, aSearchRange, aStartPoint, aEndPoint, true);
}
-
- // Reset the iterator, so this nsFind will be usable if the user wants
- // to search again (from beginning/end).
- ResetAll();
return NS_OK;
}
// We have a new text content. If its block parent is different from the
// block parent of the last text content, then we need to clear the match
// since we don't want to find across block boundaries.
- nsIContent* blockParent = GetBlockParent(mIterNode);
+ nsIContent* blockParent = GetBlockParent(state.mIterNode);
DEBUG_FIND_PRINTF("New node: old blockparent = %p, new = %p\n",
- (void*)mLastBlockParent.get(), (void*)blockParent);
- if (blockParent != mLastBlockParent) {
+ (void*)state.mLastBlockParent.get(), (void*)blockParent);
+ if (blockParent != state.mLastBlockParent) {
DEBUG_FIND_PRINTF("Different block parent!\n");
- mLastBlockParent = blockParent;
+ state.mLastBlockParent = blockParent;
// End any pending match:
matchAnchorNode = nullptr;
matchAnchorOffset = 0;
c = 0;
prevChar = 0;
prevCharInMatch = 0;
pindex = (mFindBackward ? patLen : 0);
inWhitespace = false;
}
// Get the text content:
- tc = do_QueryInterface(mIterNode);
+ tc = do_QueryInterface(state.mIterNode);
if (!tc || !(frag = tc->GetText())) { // Out of nodes
- mIterator = nullptr;
- mLastBlockParent = nullptr;
- ResetAll();
return NS_OK;
}
fragLen = frag->GetLength();
// Set our starting point in this node. If we're going back to the anchor
// node, which means that we just ended a partial match, use the saved
// offset:
- if (mIterNode == matchAnchorNode) {
+ if (state.mIterNode == matchAnchorNode) {
findex = matchAnchorOffset + (mFindBackward ? 1 : 0);
}
- // mIterOffset, if set, is the range's idea of an offset, and points
+ // state.mIterOffset, if set, is the range's idea of an offset, and points
// between characters. But when translated to a string index, it points to
// a character. If we're going backward, this is one character too late
// and we'll match part of our previous pattern.
- else if (mIterOffset >= 0) {
- findex = mIterOffset - (mFindBackward ? 1 : 0);
+ else if (state.mIterOffset >= 0) {
+ findex = state.mIterOffset - (mFindBackward ? 1 : 0);
}
// Otherwise, just start at the appropriate end of the fragment:
else if (mFindBackward) {
findex = fragLen - 1;
} else {
findex = 0;
}
// Offset can only apply to the first node:
- mIterOffset = -1;
+ state.mIterOffset = -1;
// If this is outside the bounds of the string, then skip this node:
if (findex < 0 || findex > fragLen - 1) {
DEBUG_FIND_PRINTF("At the end of a text node -- skipping to the next\n");
frag = 0;
continue;
}
@@ -1096,20 +1110,19 @@ nsFind::Find(const char16_t* aPatText, n
// Done with this node. Pull a new one.
frag = nullptr;
continue;
}
}
// Have we gone past the endpoint yet? If we have, and we're not in the
// middle of a match, return.
- if (mIterNode == endNode &&
+ if (state.mIterNode == endNode &&
((mFindBackward && findex < static_cast<int32_t>(endOffset)) ||
(!mFindBackward && findex > static_cast<int32_t>(endOffset)))) {
- ResetAll();
return NS_OK;
}
// Save the previous character for word boundary detection
prevChar = c;
// The two characters we'll be comparing:
c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex]));
patc = patStr[pindex];
@@ -1202,17 +1215,17 @@ nsFind::Find(const char16_t* aPatText, n
if (inWhitespace) {
DEBUG_FIND_PRINTF("YES (whitespace)(%d of %d)\n", pindex, patLen);
} else {
DEBUG_FIND_PRINTF("YES! '%c' == '%c' (%d of %d)\n", c, patc, pindex, patLen);
}
// Save the range anchors if we haven't already:
if (!matchAnchorNode) {
- matchAnchorNode = mIterNode;
+ matchAnchorNode = state.mIterNode;
matchAnchorOffset = findex;
}
// Are we done?
if (DONE_WITH_PINDEX) {
// Matched the whole string!
DEBUG_FIND_PRINTF("Found a match!\n");
@@ -1225,17 +1238,17 @@ nsFind::Find(const char16_t* aPatText, n
int32_t nextfindex = findex + incr;
char16_t nextChar;
// If still in array boundaries, get nextChar.
if (mFindBackward ? (nextfindex >= 0) : (nextfindex < fragLen))
nextChar = (t2b ? t2b[nextfindex] : CHAR_TO_UNICHAR(t1b[nextfindex]));
// Get next character from the next node.
else
- nextChar = PeekNextChar(aSearchRange, aStartPoint, aEndPoint);
+ nextChar = PeekNextChar(state, aSearchRange, aStartPoint, aEndPoint);
if (nextChar == NBSP_CHARCODE)
nextChar = CHAR_TO_UNICHAR(' ');
// If a word break isn't there when it needs to be, reset search.
if (!mWordBreaker->BreakInBetween(&c, 1, &nextChar, 1)) {
matchAnchorNode = nullptr;
continue;
@@ -1269,21 +1282,19 @@ nsFind::Find(const char16_t* aPatText, n
startParent = nullptr;
}
}
if (startParent) {
// If startParent == nullptr, we didn't successfully make range
// or, we didn't make a range because the start or end node were
// invisible. Reset the offset to the other end of the found string:
- mIterOffset = findex + (mFindBackward ? 1 : 0);
- DEBUG_FIND_PRINTF("mIterOffset = %d, mIterNode = ", mIterOffset);
- DumpNode(mIterNode);
-
- ResetAll();
+ state.mIterOffset = findex + (mFindBackward ? 1 : 0);
+ DEBUG_FIND_PRINTF("mIterOffset = %d, mIterNode = ", state.mIterOffset);
+ DumpNode(state.mIterNode);
return NS_OK;
}
// This match is no good, continue on in document
matchAnchorNode = nullptr;
}
if (matchAnchorNode) {
// Not done, but still matching. Advance and loop around for the next
@@ -1300,36 +1311,34 @@ nsFind::Find(const char16_t* aPatText, n
}
DEBUG_FIND_PRINTF("NOT: %c == %c\n", c, patc);
// If we didn't match, go back to the beginning of patStr, and set findex
// back to the next char after we started the current match.
if (matchAnchorNode) { // we're ending a partial match
findex = matchAnchorOffset;
- mIterOffset = matchAnchorOffset;
+ state.mIterOffset = matchAnchorOffset;
// +incr will be added to findex when we continue
// Are we going back to a previous node?
- if (matchAnchorNode != mIterNode) {
+ if (matchAnchorNode != state.mIterNode) {
nsCOMPtr<nsIContent> content(do_QueryInterface(matchAnchorNode));
DebugOnly<nsresult> rv = NS_ERROR_UNEXPECTED;
if (content) {
- rv = mIterator->PositionAt(content);
+ rv = state.mIterator->PositionAt(content);
}
frag = 0;
NS_ASSERTION(NS_SUCCEEDED(rv), "Text content wasn't nsIContent!");
DEBUG_FIND_PRINTF("Repositioned anchor node\n");
}
DEBUG_FIND_PRINTF("Ending a partial match; findex -> %d, mIterOffset -> %d\n",
- findex, mIterOffset);
+ findex, state.mIterOffset);
}
matchAnchorNode = nullptr;
matchAnchorOffset = 0;
inWhitespace = false;
pindex = (mFindBackward ? patLen : 0);
DEBUG_FIND_PRINTF("Setting findex back to %d, pindex to %d\n", findex, pindex);
}
- // Out of nodes, and didn't match.
- ResetAll();
return NS_OK;
}