Skip to content

Commit

Permalink
Bug 269442 - whole word matching support for nsFind. r=ehsan,dao. ui-…
Browse files Browse the repository at this point in the history
…r=shorlander

MozReview-Commit-ID: KIDWHyjOSYL
  • Loading branch information
mikedeboer committed Jun 28, 2016
1 parent d68e9d8 commit a951358
Show file tree
Hide file tree
Showing 16 changed files with 276 additions and 31 deletions.
144 changes: 133 additions & 11 deletions embedding/components/find/nsFind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,18 +557,21 @@ nsFind::SetCaseSensitive(bool aCaseSensitive)
return NS_OK;
}

/* attribute boolean entireWord; */
NS_IMETHODIMP
nsFind::GetWordBreaker(nsIWordBreaker** aWordBreaker)
nsFind::GetEntireWord(bool *aEntireWord)
{
*aWordBreaker = mWordBreaker;
NS_IF_ADDREF(*aWordBreaker);
if (!aEntireWord)
return NS_ERROR_NULL_POINTER;

*aEntireWord = !!mWordBreaker;
return NS_OK;
}

NS_IMETHODIMP
nsFind::SetWordBreaker(nsIWordBreaker* aWordBreaker)
nsFind::SetEntireWord(bool aEntireWord)
{
mWordBreaker = aWordBreaker;
mWordBreaker = aEntireWord ? nsContentUtils::WordBreaker() : nullptr;
return NS_OK;
}

Expand Down Expand Up @@ -730,6 +733,82 @@ nsFind::NextNode(nsIDOMRange* aSearchRange,
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<nsIDOMNode> mIterNode;
nsCOMPtr<nsINode> mCurrNode;
RefPtr<nsFind> mFind;
};

char16_t
nsFind::PeekNextChar(nsIDOMRange* aSearchRange,
nsIDOMRange* aStartPoint,
nsIDOMRange* aEndPoint)
{
// We need to restore the necessary member variables before this function
// returns.
PeekNextCharRestoreState restoreState(this);

nsCOMPtr<nsIContent> tc;
nsresult rv;
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);

// Get the text content:
tc = do_QueryInterface(mIterNode);

// Get the block parent.
nsCOMPtr<nsIDOMNode> blockParent;
rv = GetBlockParent(mIterNode, getter_AddRefs(blockParent));
if (NS_FAILED(rv))
return L'\0';

// If out of nodes or in new parent.
if (!mIterNode || !tc || (blockParent != mLastBlockParent))
return L'\0';

frag = tc->GetText();
fragLen = frag->GetLength();
} while (fragLen <= 0);

const char16_t *t2b = nullptr;
const char *t1b = nullptr;

if (frag->Is2b()) {
t2b = frag->Get2b();
} else {
t1b = frag->Get1b();
}

// Index of char to return.
int32_t index = mFindBackward ? fragLen - 1 : 0;

return t1b ? CHAR_TO_UNICHAR(t1b[index]) : t2b[index];
}

bool
nsFind::IsBlockNode(nsIContent* aContent)
{
Expand Down Expand Up @@ -901,6 +980,8 @@ nsFind::Find(const char16_t* aPatText, nsIDOMRange* aSearchRange,
// Keep track of when we're in whitespace:
// (only matters when we're matching)
bool inWhitespace = false;
// Keep track of whether the previous char was a word-breaking one.
bool wordBreakPrev = false;

// Place to save the range start point in case we find a match:
nsCOMPtr<nsIDOMNode> matchAnchorNode;
Expand All @@ -912,7 +993,10 @@ nsFind::Find(const char16_t* aPatText, nsIDOMRange* aSearchRange,
aEndPoint->GetEndContainer(getter_AddRefs(endNode));
aEndPoint->GetEndOffset(&endOffset);

char16_t c = 0;
char16_t patc = 0;
char16_t prevChar = 0;
char16_t prevCharInMatch = 0;
while (1) {
#ifdef DEBUG_FIND
printf("Loop ...\n");
Expand Down Expand Up @@ -1046,9 +1130,11 @@ nsFind::Find(const char16_t* aPatText, nsIDOMRange* aSearchRange,
return NS_OK;
}

// Save the previous character for word boundary detection
prevChar = c;
// The two characters we'll be comparing:
char16_t c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex]));
char16_t patc = patStr[pindex];
c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex]));
patc = patStr[pindex];

#ifdef DEBUG_FIND
printf("Comparing '%c'=%x to '%c' (%d of %d), findex=%d%s\n",
Expand Down Expand Up @@ -1111,7 +1197,7 @@ nsFind::Find(const char16_t* aPatText, nsIDOMRange* aSearchRange,

// a '\n' between CJ characters is ignored
if (pindex != (mFindBackward ? patLen : 0) && c != patc && !inWhitespace) {
if (c == '\n' && t2b && IS_CJ_CHAR(prevChar)) {
if (c == '\n' && t2b && IS_CJ_CHAR(prevCharInMatch)) {
int32_t nindex = findex + incr;
if (mFindBackward ? (nindex >= 0) : (nindex < fragLen)) {
if (IS_CJ_CHAR(t2b[nindex])) {
Expand All @@ -1121,9 +1207,22 @@ nsFind::Find(const char16_t* aPatText, nsIDOMRange* aSearchRange,
}
}

// Compare
if (c == patc || (inWhitespace && IsSpace(c))) {
prevChar = c;
wordBreakPrev = false;
if (mWordBreaker) {
if (prevChar == NBSP_CHARCODE)
prevChar = CHAR_TO_UNICHAR(' ');
wordBreakPrev = mWordBreaker->BreakInBetween(&prevChar, 1, &c, 1);
}

// Compare. Match if we're in whitespace and c is whitespace, or if the
// characters match and at least one of the following is true:
// a) we're not matching the entire word
// b) a match has already been stored
// c) the previous character is a different "class" than the current character.
if ((c == patc && (!mWordBreaker || matchAnchorNode || wordBreakPrev)) ||
(inWhitespace && IsSpace(c)))
{
prevCharInMatch = c;
#ifdef DEBUG_FIND
if (inWhitespace) {
printf("YES (whitespace)(%d of %d)\n", pindex, patLen);
Expand All @@ -1148,6 +1247,29 @@ nsFind::Find(const char16_t* aPatText, nsIDOMRange* aSearchRange,
// Make the range:
nsCOMPtr<nsIDOMNode> startParent;
nsCOMPtr<nsIDOMNode> endParent;

// Check for word break (if necessary)
if (mWordBreaker) {
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);

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;
}
}

nsCOMPtr<nsIDOMRange> range = CreateRange(tc);
if (range) {
int32_t matchStartOffset, matchEndOffset;
Expand Down
9 changes: 9 additions & 0 deletions embedding/components/find/nsFind.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class nsFind : public nsIFind
bool mFindBackward;
bool mCaseSensitive;

// Use "find entire words" mode by setting to a word breaker or null, to
// disable "entire words" mode.
nsCOMPtr<nsIWordBreaker> mWordBreaker;

int32_t mIterOffset;
Expand All @@ -64,13 +66,20 @@ class nsFind : public nsIFind
nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
bool aContinueOk);

// Get the first character from the next node (last if mFindBackward).
char16_t PeekNextChar(nsIDOMRange* aSearchRange,
nsIDOMRange* aStartPoint,
nsIDOMRange* aEndPoint);

// Reset variables before returning -- don't hold any references.
void ResetAll();

// The iterator we use to move through the document:
nsresult InitIterator(nsIDOMNode* aStartNode, int32_t aStartOffset,
nsIDOMNode* aEndNode, int32_t aEndOffset);
RefPtr<nsFindContentIterator> mIterator;

friend class PeekNextCharRestoreState;
};

#endif // nsFind_h__
9 changes: 2 additions & 7 deletions embedding/components/find/nsIFind.idl
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@
interface nsIDOMRange;
interface nsIWordBreaker;

[scriptable, uuid(75125d55-37ee-4575-b9b5-f33bfa68c2a1)]
[scriptable, uuid(40aba110-2a56-4678-be90-e2c17a9ae7d7)]
interface nsIFind : nsISupports
{
attribute boolean findBackwards;
attribute boolean caseSensitive;

/**
* Use "find entire words" mode by setting to a word breaker
* or null, to disable "entire words" mode.
*/
[noscript] attribute nsIWordBreaker wordBreaker;
attribute boolean entireWord;

/**
* Find some text in the current context. The implementation is
Expand Down
3 changes: 1 addition & 2 deletions embedding/components/find/nsWebBrowserFind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,8 +719,7 @@ nsWebBrowserFind::SearchInFrame(nsPIDOMWindowOuter* aWindow, bool aWrapping,
(void)find->SetCaseSensitive(mMatchCase);
(void)find->SetFindBackwards(mFindBackwards);

// XXX Make and set a line breaker here, once that's implemented.
(void)find->SetWordBreaker(nullptr);
(void)find->SetEntireWord(mEntireWord);

// Now make sure the content (for actual finding) and frame (for
// selection) models are up to date.
Expand Down
1 change: 1 addition & 0 deletions modules/libpref/init/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ pref("accessibility.typeaheadfind.matchesCountTimeout", 100);
pref("accessibility.typeaheadfind.matchesCountLimit", 1000);
pref("findbar.highlightAll", false);
pref("findbar.modalHighlight", false);
pref("findbar.entireword", false);

// use Mac OS X Appearance panel text smoothing setting when rendering text, disabled by default
pref("gfx.use_text_smoothing_setting", false);
Expand Down
3 changes: 2 additions & 1 deletion toolkit/components/typeaheadfind/nsITypeAheadFind.idl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface nsIDocShell;

/****************************** nsTypeAheadFind ******************************/

[scriptable, uuid(c8ca2c38-7030-4453-ae63-a16eeb10e096)]
[scriptable, uuid(ae501e28-c57f-4692-ac74-410e1bed98b7)]
interface nsITypeAheadFind : nsISupports
{
/****************************** Initializer ******************************/
Expand Down Expand Up @@ -65,6 +65,7 @@ interface nsITypeAheadFind : nsISupports
readonly attribute AString searchString;
// Most recent search string
attribute boolean caseSensitive; // Searches are case sensitive
attribute boolean entireWord; // Search for whole words only
readonly attribute nsIDOMElement foundLink;
// Most recent elem found, if a link
readonly attribute nsIDOMElement foundEditable;
Expand Down
23 changes: 22 additions & 1 deletion toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ nsTypeAheadFind::nsTypeAheadFind():
mDidAddObservers(false),
mLastFindLength(0),
mIsSoundInitialized(false),
mCaseSensitive(false)
mCaseSensitive(false),
mEntireWord(false)
{
}

Expand Down Expand Up @@ -167,6 +168,26 @@ nsTypeAheadFind::GetCaseSensitive(bool* isCaseSensitive)
return NS_OK;
}

NS_IMETHODIMP
nsTypeAheadFind::SetEntireWord(bool isEntireWord)
{
mEntireWord = isEntireWord;

if (mFind) {
mFind->SetEntireWord(mEntireWord);
}

return NS_OK;
}

NS_IMETHODIMP
nsTypeAheadFind::GetEntireWord(bool* isEntireWord)
{
*isEntireWord = mEntireWord;

return NS_OK;
}

NS_IMETHODIMP
nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell)
{
Expand Down
3 changes: 2 additions & 1 deletion toolkit/components/typeaheadfind/nsTypeAheadFind.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class nsTypeAheadFind : public nsITypeAheadFind,
nsCOMPtr<nsIFind> mFind;

bool mCaseSensitive;
bool mEntireWord;

bool EnsureFind() {
if (mFind) {
Expand All @@ -118,7 +119,7 @@ class nsTypeAheadFind : public nsITypeAheadFind,
}

mFind->SetCaseSensitive(mCaseSensitive);
mFind->SetWordBreaker(nullptr);
mFind->SetEntireWord(mEntireWord);

return true;
}
Expand Down
Loading

0 comments on commit a951358

Please sign in to comment.