Bug 202251 - Add an option to ignore diacritics when searching. r=fluent-reviewers,mikedeboer,jfkthame,flod
Differential Revision: https://phabricator.services.mozilla.com/D51841
This commit is contained in:
@@ -162,6 +162,32 @@ void ToFoldedCase(const char16_t* aIn, char16_t* aOut, uint32_t aLen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t ToNaked(uint32_t aChar) {
|
||||||
|
if (IS_ASCII(aChar)) {
|
||||||
|
return aChar;
|
||||||
|
}
|
||||||
|
return mozilla::unicode::GetNaked(aChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToNaked(nsAString& aString) {
|
||||||
|
char16_t* buf = aString.BeginWriting();
|
||||||
|
ToNaked(buf, buf, aString.Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToNaked(const char16_t* aIn, char16_t* aOut, uint32_t aLen) {
|
||||||
|
for (uint32_t i = 0; i < aLen; i++) {
|
||||||
|
uint32_t ch = aIn[i];
|
||||||
|
if (i < aLen - 1 && NS_IS_SURROGATE_PAIR(ch, aIn[i + 1])) {
|
||||||
|
ch = mozilla::unicode::GetNaked(SURROGATE_TO_UCS4(ch, aIn[i + 1]));
|
||||||
|
NS_ASSERTION(!IS_IN_BMP(ch), "stripping crossed BMP/SMP boundary!");
|
||||||
|
aOut[i++] = H_SURROGATE(ch);
|
||||||
|
aOut[i] = L_SURROGATE(ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
aOut[i] = ToNaked(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int32_t nsCaseInsensitiveStringComparator::operator()(const char16_t* lhs,
|
int32_t nsCaseInsensitiveStringComparator::operator()(const char16_t* lhs,
|
||||||
const char16_t* rhs,
|
const char16_t* rhs,
|
||||||
uint32_t lLength,
|
uint32_t lLength,
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ uint32_t ToFoldedCase(uint32_t aChar);
|
|||||||
void ToFoldedCase(nsAString& aString);
|
void ToFoldedCase(nsAString& aString);
|
||||||
void ToFoldedCase(const char16_t* aIn, char16_t* aOut, uint32_t aLen);
|
void ToFoldedCase(const char16_t* aIn, char16_t* aOut, uint32_t aLen);
|
||||||
|
|
||||||
|
uint32_t ToNaked(uint32_t aChar);
|
||||||
|
void ToNaked(nsAString& aString);
|
||||||
|
void ToNaked(const char16_t* aIn, char16_t* aOut, uint32_t aLen);
|
||||||
|
|
||||||
class nsCaseInsensitiveStringComparator : public nsStringComparator {
|
class nsCaseInsensitiveStringComparator : public nsStringComparator {
|
||||||
public:
|
public:
|
||||||
nsCaseInsensitiveStringComparator() = default;
|
nsCaseInsensitiveStringComparator() = default;
|
||||||
|
|||||||
@@ -8,8 +8,12 @@
|
|||||||
#include "nsUnicodePropertyData.cpp"
|
#include "nsUnicodePropertyData.cpp"
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
|
#include "mozilla/HashTable.h"
|
||||||
#include "nsCharTraits.h"
|
#include "nsCharTraits.h"
|
||||||
|
|
||||||
|
#include "unicode/uchar.h"
|
||||||
|
#include "unicode/unorm2.h"
|
||||||
|
|
||||||
#define UNICODE_BMP_LIMIT 0x10000
|
#define UNICODE_BMP_LIMIT 0x10000
|
||||||
#define UNICODE_LIMIT 0x110000
|
#define UNICODE_LIMIT 0x110000
|
||||||
|
|
||||||
@@ -305,6 +309,80 @@ uint32_t CountGraphemeClusters(const char16_t* aText, uint32_t aLength) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t GetNaked(uint32_t aCh) {
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
|
static const UNormalizer2* normalizer;
|
||||||
|
static HashMap<uint32_t, uint32_t> nakedCharCache;
|
||||||
|
|
||||||
|
HashMap<uint32_t, uint32_t>::Ptr entry = nakedCharCache.lookup(aCh);
|
||||||
|
if (entry.found()) {
|
||||||
|
return entry->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
UErrorCode error = U_ZERO_ERROR;
|
||||||
|
if (!normalizer) {
|
||||||
|
normalizer = unorm2_getNFDInstance(&error);
|
||||||
|
if (U_FAILURE(error)) {
|
||||||
|
return aCh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const size_t MAX_DECOMPOSITION_SIZE = 16;
|
||||||
|
UChar decomposition[MAX_DECOMPOSITION_SIZE];
|
||||||
|
UChar* combiners;
|
||||||
|
int32_t decompositionLen;
|
||||||
|
uint32_t baseChar, nextChar;
|
||||||
|
decompositionLen = unorm2_getDecomposition(normalizer, aCh, decomposition,
|
||||||
|
MAX_DECOMPOSITION_SIZE, &error);
|
||||||
|
if (decompositionLen < 1) {
|
||||||
|
// The character does not decompose.
|
||||||
|
return aCh;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u_getIntPropertyValue(aCh, UCHAR_GENERAL_CATEGORY) & U_GC_M_MASK) {
|
||||||
|
// The character is itself a combining character, and we don't want to use
|
||||||
|
// its decomposition into multiple combining characters.
|
||||||
|
baseChar = aCh;
|
||||||
|
goto cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_IS_HIGH_SURROGATE(decomposition[0])) {
|
||||||
|
baseChar = SURROGATE_TO_UCS4(decomposition[0], decomposition[1]);
|
||||||
|
combiners = decomposition + 2;
|
||||||
|
} else {
|
||||||
|
baseChar = decomposition[0];
|
||||||
|
combiners = decomposition + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_IN_BMP(baseChar) != IS_IN_BMP(aCh)) {
|
||||||
|
// Mappings that would change the length of a UTF-16 string are not
|
||||||
|
// currently supported.
|
||||||
|
baseChar = aCh;
|
||||||
|
goto cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decompositionLen > 1) {
|
||||||
|
if (NS_IS_HIGH_SURROGATE(combiners[0])) {
|
||||||
|
nextChar = SURROGATE_TO_UCS4(combiners[0], combiners[1]);
|
||||||
|
} else {
|
||||||
|
nextChar = combiners[0];
|
||||||
|
}
|
||||||
|
if (u_getCombiningClass(nextChar) == 0) {
|
||||||
|
// Hangul syllables decompose but do not actually have diacritics.
|
||||||
|
baseChar = aCh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache:
|
||||||
|
if (!nakedCharCache.putNew(aCh, baseChar)) {
|
||||||
|
// We're out of memory, so delete the cache to free some up.
|
||||||
|
nakedCharCache.clearAndCompact();
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseChar;
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace unicode
|
} // end namespace unicode
|
||||||
|
|
||||||
} // end namespace mozilla
|
} // end namespace mozilla
|
||||||
|
|||||||
@@ -229,6 +229,9 @@ class ClusterIterator {
|
|||||||
// Count the number of grapheme clusters in the given string
|
// Count the number of grapheme clusters in the given string
|
||||||
uint32_t CountGraphemeClusters(const char16_t* aText, uint32_t aLength);
|
uint32_t CountGraphemeClusters(const char16_t* aText, uint32_t aLength);
|
||||||
|
|
||||||
|
// Remove diacritics from a character
|
||||||
|
uint32_t GetNaked(uint32_t aCh);
|
||||||
|
|
||||||
// A simple reverse iterator for a string of char16_t codepoints that
|
// A simple reverse iterator for a string of char16_t codepoints that
|
||||||
// advances by Unicode grapheme clusters
|
// advances by Unicode grapheme clusters
|
||||||
class ClusterReverseIterator {
|
class ClusterReverseIterator {
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ pref("accessibility.typeaheadfind.flashBar", 1);
|
|||||||
pref("accessibility.typeaheadfind.linksonly", false);
|
pref("accessibility.typeaheadfind.linksonly", false);
|
||||||
pref("accessibility.typeaheadfind.casesensitive", 0);
|
pref("accessibility.typeaheadfind.casesensitive", 0);
|
||||||
pref("accessibility.browsewithcaret_shortcut.enabled", false);
|
pref("accessibility.browsewithcaret_shortcut.enabled", false);
|
||||||
|
pref("findbar.matchdiacritics", 0);
|
||||||
|
|
||||||
// Whether the character encoding menu is under the main Firefox button. This
|
// Whether the character encoding menu is under the main Firefox button. This
|
||||||
// preference is a string so that localizers can alter it.
|
// preference is a string so that localizers can alter it.
|
||||||
|
|||||||
@@ -332,6 +332,7 @@ class GeckoViewContent extends GeckoViewModule {
|
|||||||
|
|
||||||
finder.caseSensitive = !!aData.matchCase;
|
finder.caseSensitive = !!aData.matchCase;
|
||||||
finder.entireWord = !!aData.wholeWord;
|
finder.entireWord = !!aData.wholeWord;
|
||||||
|
finder.matchDiacritics = !!aData.matchDiacritics;
|
||||||
finder.addResultListener(this._finderListener);
|
finder.addResultListener(this._finderListener);
|
||||||
|
|
||||||
const drawOutline =
|
const drawOutline =
|
||||||
|
|||||||
@@ -773,6 +773,11 @@ pref("accessibility.typeaheadfind.matchesCountLimit", 1000);
|
|||||||
pref("findbar.highlightAll", false);
|
pref("findbar.highlightAll", false);
|
||||||
pref("findbar.entireword", false);
|
pref("findbar.entireword", false);
|
||||||
pref("findbar.iteratorTimeout", 100);
|
pref("findbar.iteratorTimeout", 100);
|
||||||
|
// matchdiacritics: controls the find bar's diacritic matching
|
||||||
|
// 0 - "never" (ignore diacritics)
|
||||||
|
// 1 - "always" (match diacritics)
|
||||||
|
// other - "auto" (match diacritics if input has diacritics, ignore otherwise)
|
||||||
|
pref("findbar.matchdiacritics", 0);
|
||||||
|
|
||||||
// use Mac OS X Appearance panel text smoothing setting when rendering text, disabled by default
|
// use Mac OS X Appearance panel text smoothing setting when rendering text, disabled by default
|
||||||
pref("gfx.use_text_smoothing_setting", false);
|
pref("gfx.use_text_smoothing_setting", false);
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ class FinderChild extends JSWindowActorChild {
|
|||||||
this.finder.caseSensitive = data.caseSensitive;
|
this.finder.caseSensitive = data.caseSensitive;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "Finder:MatchDiacritics":
|
||||||
|
this.finder.matchDiacritics = data.matchDiacritics;
|
||||||
|
break;
|
||||||
|
|
||||||
case "Finder:EntireWord":
|
case "Finder:EntireWord":
|
||||||
this.finder.entireWord = data.entireWord;
|
this.finder.entireWord = data.entireWord;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class FindContent {
|
|||||||
* @param {string} queryphrase - the text to search for.
|
* @param {string} queryphrase - the text to search for.
|
||||||
* @param {boolean} caseSensitive - whether to use case sensitive matches.
|
* @param {boolean} caseSensitive - whether to use case sensitive matches.
|
||||||
* @param {boolean} includeRangeData - whether to collect and return range data.
|
* @param {boolean} includeRangeData - whether to collect and return range data.
|
||||||
|
* @param {boolean} matchDiacritics - whether diacritics must match.
|
||||||
* @param {boolean} searchString - whether to collect and return rect data.
|
* @param {boolean} searchString - whether to collect and return rect data.
|
||||||
*
|
*
|
||||||
* @returns {object} that includes:
|
* @returns {object} that includes:
|
||||||
@@ -66,6 +67,7 @@ class FindContent {
|
|||||||
entireWord,
|
entireWord,
|
||||||
includeRangeData,
|
includeRangeData,
|
||||||
includeRectData,
|
includeRectData,
|
||||||
|
matchDiacritics,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
this.iterator.reset();
|
this.iterator.reset();
|
||||||
@@ -77,6 +79,7 @@ class FindContent {
|
|||||||
entireWord: !!entireWord,
|
entireWord: !!entireWord,
|
||||||
finder: this.finder,
|
finder: this.finder,
|
||||||
listener: this.finder,
|
listener: this.finder,
|
||||||
|
matchDiacritics: !!matchDiacritics,
|
||||||
useSubFrames: false,
|
useSubFrames: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFind)
|
|||||||
NS_IMPL_CYCLE_COLLECTION(nsFind)
|
NS_IMPL_CYCLE_COLLECTION(nsFind)
|
||||||
|
|
||||||
nsFind::nsFind()
|
nsFind::nsFind()
|
||||||
: mFindBackward(false), mCaseSensitive(false), mWordBreaker(nullptr) {}
|
: mFindBackward(false),
|
||||||
|
mCaseSensitive(false),
|
||||||
|
mMatchDiacritics(false),
|
||||||
|
mWordBreaker(nullptr) {}
|
||||||
|
|
||||||
nsFind::~nsFind() = default;
|
nsFind::~nsFind() = default;
|
||||||
|
|
||||||
@@ -396,6 +399,22 @@ nsFind::SetEntireWord(bool aEntireWord) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsFind::GetMatchDiacritics(bool* aMatchDiacritics) {
|
||||||
|
if (!aMatchDiacritics) {
|
||||||
|
return NS_ERROR_NULL_POINTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
*aMatchDiacritics = mMatchDiacritics;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsFind::SetMatchDiacritics(bool aMatchDiacritics) {
|
||||||
|
mMatchDiacritics = aMatchDiacritics;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// Here begins the find code. A ten-thousand-foot view of how it works: Find
|
// Here begins the find code. A ten-thousand-foot view of how it works: Find
|
||||||
// needs to be able to compare across inline (but not block) nodes, e.g. find
|
// needs to be able to compare across inline (but not block) nodes, e.g. find
|
||||||
// for "abc" should match a<b>b</b>c. So after we've searched a node, we're not
|
// for "abc" should match a<b>b</b>c. So after we've searched a node, we're not
|
||||||
@@ -506,6 +525,9 @@ nsFind::Find(const nsAString& aPatText, nsRange* aSearchRange,
|
|||||||
if (!mCaseSensitive) {
|
if (!mCaseSensitive) {
|
||||||
ToFoldedCase(patAutoStr);
|
ToFoldedCase(patAutoStr);
|
||||||
}
|
}
|
||||||
|
if (!mMatchDiacritics) {
|
||||||
|
ToNaked(patAutoStr);
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore soft hyphens in the pattern
|
// Ignore soft hyphens in the pattern
|
||||||
static const char kShy[] = {char(CH_SHY), 0};
|
static const char kShy[] = {char(CH_SHY), 0};
|
||||||
@@ -684,9 +706,14 @@ nsFind::Find(const nsAString& aPatText, nsRange* aSearchRange,
|
|||||||
}
|
}
|
||||||
if (!inWhitespace && IsSpace(patc)) {
|
if (!inWhitespace && IsSpace(patc)) {
|
||||||
inWhitespace = true;
|
inWhitespace = true;
|
||||||
} else if (!inWhitespace && !mCaseSensitive) {
|
} else if (!inWhitespace) {
|
||||||
|
if (!mCaseSensitive) {
|
||||||
c = ToFoldedCase(c);
|
c = ToFoldedCase(c);
|
||||||
}
|
}
|
||||||
|
if (!mMatchDiacritics) {
|
||||||
|
c = ToNaked(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (c == CH_SHY) {
|
if (c == CH_SHY) {
|
||||||
// ignore soft hyphens in the document
|
// ignore soft hyphens in the document
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class nsFind : public nsIFind {
|
|||||||
// Parameters set from the interface:
|
// Parameters set from the interface:
|
||||||
bool mFindBackward;
|
bool mFindBackward;
|
||||||
bool mCaseSensitive;
|
bool mCaseSensitive;
|
||||||
|
bool mMatchDiacritics;
|
||||||
|
|
||||||
// Use "find entire words" mode by setting to a word breaker or null, to
|
// Use "find entire words" mode by setting to a word breaker or null, to
|
||||||
// disable "entire words" mode.
|
// disable "entire words" mode.
|
||||||
|
|||||||
@@ -79,3 +79,13 @@ NS_IMETHODIMP nsFindService::SetMatchCase(bool aMatchCase) {
|
|||||||
mMatchCase = aMatchCase;
|
mMatchCase = aMatchCase;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP nsFindService::GetMatchDiacritics(bool* aMatchDiacritics) {
|
||||||
|
NS_ENSURE_ARG_POINTER(aMatchDiacritics);
|
||||||
|
*aMatchDiacritics = mMatchDiacritics;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
NS_IMETHODIMP nsFindService::SetMatchDiacritics(bool aMatchDiacritics) {
|
||||||
|
mMatchDiacritics = aMatchDiacritics;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,4 +40,5 @@ class nsFindService : public nsIFindService {
|
|||||||
bool mWrapFind;
|
bool mWrapFind;
|
||||||
bool mEntireWord;
|
bool mEntireWord;
|
||||||
bool mMatchCase;
|
bool mMatchCase;
|
||||||
|
bool mMatchDiacritics;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface nsIFind : nsISupports
|
|||||||
attribute boolean findBackwards;
|
attribute boolean findBackwards;
|
||||||
attribute boolean caseSensitive;
|
attribute boolean caseSensitive;
|
||||||
attribute boolean entireWord;
|
attribute boolean entireWord;
|
||||||
|
attribute boolean matchDiacritics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find some text in the current context. The implementation is
|
* Find some text in the current context. The implementation is
|
||||||
|
|||||||
@@ -22,5 +22,6 @@ interface nsIFindService : nsISupports
|
|||||||
attribute boolean wrapFind;
|
attribute boolean wrapFind;
|
||||||
attribute boolean entireWord;
|
attribute boolean entireWord;
|
||||||
attribute boolean matchCase;
|
attribute boolean matchCase;
|
||||||
|
attribute boolean matchDiacritics;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,6 +79,13 @@ interface nsIWebBrowserFind : nsISupports
|
|||||||
*/
|
*/
|
||||||
attribute boolean matchCase;
|
attribute boolean matchCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* matchDiacritics
|
||||||
|
*
|
||||||
|
* Whether to match diacritics when searching. Default is false.
|
||||||
|
*/
|
||||||
|
attribute boolean matchDiacritics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* searchFrames
|
* searchFrames
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ nsWebBrowserFind::nsWebBrowserFind()
|
|||||||
mWrapFind(false),
|
mWrapFind(false),
|
||||||
mEntireWord(false),
|
mEntireWord(false),
|
||||||
mMatchCase(false),
|
mMatchCase(false),
|
||||||
|
mMatchDiacritics(false),
|
||||||
mSearchSubFrames(true),
|
mSearchSubFrames(true),
|
||||||
mSearchParentFrames(true) {}
|
mSearchParentFrames(true) {}
|
||||||
|
|
||||||
@@ -286,6 +287,19 @@ nsWebBrowserFind::SetMatchCase(bool aMatchCase) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsWebBrowserFind::GetMatchDiacritics(bool* aMatchDiacritics) {
|
||||||
|
NS_ENSURE_ARG_POINTER(aMatchDiacritics);
|
||||||
|
*aMatchDiacritics = mMatchDiacritics;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsWebBrowserFind::SetMatchDiacritics(bool aMatchDiacritics) {
|
||||||
|
mMatchDiacritics = aMatchDiacritics;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void nsWebBrowserFind::SetSelectionAndScroll(nsPIDOMWindowOuter* aWindow,
|
void nsWebBrowserFind::SetSelectionAndScroll(nsPIDOMWindowOuter* aWindow,
|
||||||
nsRange* aRange) {
|
nsRange* aRange) {
|
||||||
RefPtr<Document> doc = aWindow->GetDoc();
|
RefPtr<Document> doc = aWindow->GetDoc();
|
||||||
@@ -622,6 +636,7 @@ nsresult nsWebBrowserFind::SearchInFrame(nsPIDOMWindowOuter* aWindow,
|
|||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
(void)find->SetCaseSensitive(mMatchCase);
|
(void)find->SetCaseSensitive(mMatchCase);
|
||||||
|
(void)find->SetMatchDiacritics(mMatchDiacritics);
|
||||||
(void)find->SetFindBackwards(mFindBackwards);
|
(void)find->SetFindBackwards(mFindBackwards);
|
||||||
|
|
||||||
(void)find->SetEntireWord(mEntireWord);
|
(void)find->SetEntireWord(mEntireWord);
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ class nsWebBrowserFind : public nsIWebBrowserFind,
|
|||||||
bool mWrapFind;
|
bool mWrapFind;
|
||||||
bool mEntireWord;
|
bool mEntireWord;
|
||||||
bool mMatchCase;
|
bool mMatchCase;
|
||||||
|
bool mMatchDiacritics;
|
||||||
|
|
||||||
bool mSearchSubFrames;
|
bool mSearchSubFrames;
|
||||||
bool mSearchParentFrames;
|
bool mSearchParentFrames;
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ interface nsITypeAheadFind : nsISupports
|
|||||||
readonly attribute AString searchString;
|
readonly attribute AString searchString;
|
||||||
// Most recent search string
|
// Most recent search string
|
||||||
attribute boolean caseSensitive; // Searches are case sensitive
|
attribute boolean caseSensitive; // Searches are case sensitive
|
||||||
|
attribute boolean matchDiacritics; // Searches preserve diacritics
|
||||||
attribute boolean entireWord; // Search for whole words only
|
attribute boolean entireWord; // Search for whole words only
|
||||||
readonly attribute Element foundLink;
|
readonly attribute Element foundLink;
|
||||||
// Most recent elem found, if a link
|
// Most recent elem found, if a link
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ nsTypeAheadFind::nsTypeAheadFind()
|
|||||||
mLastFindLength(0),
|
mLastFindLength(0),
|
||||||
mIsSoundInitialized(false),
|
mIsSoundInitialized(false),
|
||||||
mCaseSensitive(false),
|
mCaseSensitive(false),
|
||||||
mEntireWord(false) {}
|
mEntireWord(false),
|
||||||
|
mMatchDiacritics(false) {}
|
||||||
|
|
||||||
nsTypeAheadFind::~nsTypeAheadFind() {
|
nsTypeAheadFind::~nsTypeAheadFind() {
|
||||||
nsCOMPtr<nsIPrefBranch> prefInternal(
|
nsCOMPtr<nsIPrefBranch> prefInternal(
|
||||||
@@ -204,6 +205,24 @@ nsTypeAheadFind::GetEntireWord(bool* isEntireWord) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsTypeAheadFind::SetMatchDiacritics(bool matchDiacritics) {
|
||||||
|
mMatchDiacritics = matchDiacritics;
|
||||||
|
|
||||||
|
if (mFind) {
|
||||||
|
mFind->SetMatchDiacritics(mMatchDiacritics);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsTypeAheadFind::GetMatchDiacritics(bool* matchDiacritics) {
|
||||||
|
*matchDiacritics = mMatchDiacritics;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell) {
|
nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell) {
|
||||||
mDocShell = do_GetWeakReference(aDocShell);
|
mDocShell = do_GetWeakReference(aDocShell);
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ class nsTypeAheadFind : public nsITypeAheadFind,
|
|||||||
|
|
||||||
bool mCaseSensitive;
|
bool mCaseSensitive;
|
||||||
bool mEntireWord;
|
bool mEntireWord;
|
||||||
|
bool mMatchDiacritics;
|
||||||
|
|
||||||
bool EnsureFind() {
|
bool EnsureFind() {
|
||||||
if (mFind) {
|
if (mFind) {
|
||||||
@@ -131,6 +132,7 @@ class nsTypeAheadFind : public nsITypeAheadFind,
|
|||||||
|
|
||||||
mFind->SetCaseSensitive(mCaseSensitive);
|
mFind->SetCaseSensitive(mCaseSensitive);
|
||||||
mFind->SetEntireWord(mEntireWord);
|
mFind->SetEntireWord(mEntireWord);
|
||||||
|
mFind->SetMatchDiacritics(mMatchDiacritics);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html>
|
<html>
|
||||||
<!--
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=202251
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=450048
|
https://bugzilla.mozilla.org/show_bug.cgi?id=450048
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=969980
|
https://bugzilla.mozilla.org/show_bug.cgi?id=969980
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1589786
|
https://bugzilla.mozilla.org/show_bug.cgi?id=1589786
|
||||||
@@ -13,7 +14,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1589786
|
|||||||
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
|
|
||||||
/** Test for Bug 450048 **/
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
async function runTests() {
|
async function runTests() {
|
||||||
@@ -47,15 +47,20 @@ async function runTests() {
|
|||||||
rf.findBackwards = false;
|
rf.findBackwards = false;
|
||||||
|
|
||||||
rf.caseSensitive = false;
|
rf.caseSensitive = false;
|
||||||
|
rf.matchDiacritics = false;
|
||||||
|
|
||||||
searchValue = "TexT";
|
searchValue = "TexT";
|
||||||
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
||||||
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
||||||
|
|
||||||
searchValue = "λόγος";
|
searchValue = "λογος";
|
||||||
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
||||||
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
||||||
|
|
||||||
|
searchValue = "유";
|
||||||
|
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
||||||
|
ok(!retRange, "\"" + searchValue + "\" found (not caseSensitive)");
|
||||||
|
|
||||||
searchValue = "degrees k";
|
searchValue = "degrees k";
|
||||||
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
||||||
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
||||||
@@ -64,9 +69,15 @@ async function runTests() {
|
|||||||
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
||||||
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
ok(retRange, "\"" + searchValue + "\" not found (not caseSensitive)");
|
||||||
|
|
||||||
|
rf.matchDiacritics = true;
|
||||||
|
|
||||||
|
searchValue = "λογος";
|
||||||
|
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
||||||
|
ok(!retRange, "\"" + searchValue + "\" found (not caseSensitive)");
|
||||||
|
|
||||||
rf.caseSensitive = true;
|
rf.caseSensitive = true;
|
||||||
|
|
||||||
// searchValue = "TexT";
|
searchValue = "TexT";
|
||||||
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
retRange = rf.Find(searchValue, searchRange, startPt, endPt);
|
||||||
ok(!retRange, "\"" + searchValue + "\" found (caseSensitive)");
|
ok(!retRange, "\"" + searchValue + "\" found (caseSensitive)");
|
||||||
|
|
||||||
@@ -267,6 +278,7 @@ async function runTests() {
|
|||||||
<p id="nullcharsnative">native null�</p>
|
<p id="nullcharsnative">native null�</p>
|
||||||
<p id="nullcharsinjected"></p>
|
<p id="nullcharsinjected"></p>
|
||||||
<p id="greek">ΛΌΓΟΣ</p>
|
<p id="greek">ΛΌΓΟΣ</p>
|
||||||
|
<p id="korean">위</p>
|
||||||
<p id="kelvin">degrees K</p>
|
<p id="kelvin">degrees K</p>
|
||||||
<p id="deseret">𐐐𐐯𐑊𐐬 𐐶𐐯𐑉𐑊𐐼!</p>
|
<p id="deseret">𐐐𐐯𐑊𐐬 𐐶𐐯𐑉𐑊𐐼!</p>
|
||||||
<div id="content" style="display: none">
|
<div id="content" style="display: none">
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
await testFind();
|
await testFind();
|
||||||
await testFindAgain();
|
await testFindAgain();
|
||||||
await testCaseSensitivity();
|
await testCaseSensitivity();
|
||||||
|
await testDiacriticMatching();
|
||||||
await testHighlight();
|
await testHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +144,24 @@
|
|||||||
await ContentTask.spawn(gBrowser, null, () => content.getSelection().removeAllRanges());
|
await ContentTask.spawn(gBrowser, null, () => content.getSelection().removeAllRanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testDiacriticMatching() {
|
||||||
|
info("Testing normal diacritic matching.");
|
||||||
|
let promise = once(gFindBar, "finddiacriticmatchingchange", false);
|
||||||
|
|
||||||
|
let matchDiacriticsCheckbox = gFindBar.getElement("find-match-diacritics");
|
||||||
|
matchDiacriticsCheckbox.click();
|
||||||
|
|
||||||
|
let e = await promise;
|
||||||
|
ok(e.detail.matchDiacritics, "find should match diacritics");
|
||||||
|
|
||||||
|
// Toggle it back to the original setting.
|
||||||
|
matchDiacriticsCheckbox.click();
|
||||||
|
|
||||||
|
// Changing diacritic matching does the search so clear the selected text
|
||||||
|
// before the next test.
|
||||||
|
await ContentTask.spawn(gBrowser, null, () => content.getSelection().removeAllRanges());
|
||||||
|
}
|
||||||
|
|
||||||
async function testHighlight() {
|
async function testHighlight() {
|
||||||
info("Testing find with highlight all.");
|
info("Testing find with highlight all.");
|
||||||
// Update the find state so the highlight button is clickable.
|
// Update the find state so the highlight button is clickable.
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
const SAMPLE_URL = "http://www.mozilla.org/";
|
const SAMPLE_URL = "http://www.mozilla.org/";
|
||||||
const SAMPLE_TEXT = "Some text in a text field.";
|
const SAMPLE_TEXT = "Some text in a text field.";
|
||||||
const SEARCH_TEXT = "Text Test";
|
const SEARCH_TEXT = "Text Test (δοκιμή)";
|
||||||
const NOT_FOUND_TEXT = "This text is not on the page."
|
const NOT_FOUND_TEXT = "This text is not on the page."
|
||||||
const ITERATOR_TIMEOUT = gPrefsvc.getIntPref("findbar.iteratorTimeout");
|
const ITERATOR_TIMEOUT = gPrefsvc.getIntPref("findbar.iteratorTimeout");
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
let promise = BrowserTestUtils.browserLoaded(gBrowser);
|
let promise = BrowserTestUtils.browserLoaded(gBrowser);
|
||||||
BrowserTestUtils.loadURI(gBrowser, "data:text/html,<h2 id='h2'>" + SEARCH_TEXT +
|
BrowserTestUtils.loadURI(gBrowser, "data:text/html;charset=utf-8,<h2 id='h2'>" + SEARCH_TEXT +
|
||||||
"</h2><h2><a href='" + SAMPLE_URL + "'>Link Test</a></h2><input id='text' type='text' value='" +
|
"</h2><h2><a href='" + SAMPLE_URL + "'>Link Test</a></h2><input id='text' type='text' value='" +
|
||||||
SAMPLE_TEXT + "'></input><input id='button' type='button'></input><img id='img' width='50' height='50'/>",
|
SAMPLE_TEXT + "'></input><input id='button' type='button'></input><img id='img' width='50' height='50'/>",
|
||||||
{ triggeringPrincipal: window.document.nodePrincipal });
|
{ triggeringPrincipal: window.document.nodePrincipal });
|
||||||
@@ -735,6 +735,24 @@
|
|||||||
ok(gFindBar.hidden, "Successful Find Again leaves the find bar closed.");
|
ok(gFindBar.hidden, "Successful Find Again leaves the find bar closed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testToggleDiacriticMatching() {
|
||||||
|
await openFindbar();
|
||||||
|
let promise = promiseFindResult();
|
||||||
|
await enterStringIntoFindField("δοκιμη", false);
|
||||||
|
let result = await promise;
|
||||||
|
is(result.result, Ci.nsITypeAheadFind.FIND_FOUND, "Text should be found");
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, ITERATOR_TIMEOUT + 20));
|
||||||
|
promise = promiseFindResult();
|
||||||
|
let check = gFindBar.getElement("find-match-diacritics");
|
||||||
|
check.click();
|
||||||
|
result = await promise;
|
||||||
|
is(result.result, Ci.nsITypeAheadFind.FIND_NOTFOUND, "Text should NOT be found");
|
||||||
|
|
||||||
|
check.click();
|
||||||
|
gFindBar.close(true);
|
||||||
|
}
|
||||||
|
|
||||||
async function testToggleEntireWord() {
|
async function testToggleEntireWord() {
|
||||||
await openFindbar();
|
await openFindbar();
|
||||||
let promise = promiseFindResult();
|
let promise = promiseFindResult();
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
]);
|
]);
|
||||||
const PREFS_TO_OBSERVE_INT = new Map([
|
const PREFS_TO_OBSERVE_INT = new Map([
|
||||||
["typeAheadCaseSensitive", "accessibility.typeaheadfind.casesensitive"],
|
["typeAheadCaseSensitive", "accessibility.typeaheadfind.casesensitive"],
|
||||||
|
["matchDiacritics", "findbar.matchdiacritics"],
|
||||||
]);
|
]);
|
||||||
const PREFS_TO_OBSERVE_ALL = new Map([
|
const PREFS_TO_OBSERVE_ALL = new Map([
|
||||||
...PREFS_TO_OBSERVE_BOOL,
|
...PREFS_TO_OBSERVE_BOOL,
|
||||||
@@ -72,9 +73,12 @@
|
|||||||
data-l10n-id="findbar-highlight-all2" oncommand="toggleHighlight(this.checked);" type="checkbox" />
|
data-l10n-id="findbar-highlight-all2" oncommand="toggleHighlight(this.checked);" type="checkbox" />
|
||||||
<toolbarbutton anonid="find-case-sensitive" class="findbar-case-sensitive findbar-button tabbable"
|
<toolbarbutton anonid="find-case-sensitive" class="findbar-case-sensitive findbar-button tabbable"
|
||||||
data-l10n-id="findbar-case-sensitive" oncommand="_setCaseSensitivity(this.checked ? 1 : 0);" type="checkbox" />
|
data-l10n-id="findbar-case-sensitive" oncommand="_setCaseSensitivity(this.checked ? 1 : 0);" type="checkbox" />
|
||||||
|
<toolbarbutton anonid="find-match-diacritics" class="findbar-match-diacritics findbar-button tabbable"
|
||||||
|
data-l10n-id="findbar-match-diacritics" oncommand="_setDiacriticMatching(this.checked ? 1 : 0);" type="checkbox" />
|
||||||
<toolbarbutton anonid="find-entire-word" class="findbar-entire-word findbar-button tabbable"
|
<toolbarbutton anonid="find-entire-word" class="findbar-entire-word findbar-button tabbable"
|
||||||
data-l10n-id="findbar-entire-word" oncommand="toggleEntireWord(this.checked);" type="checkbox" />
|
data-l10n-id="findbar-entire-word" oncommand="toggleEntireWord(this.checked);" type="checkbox" />
|
||||||
<label anonid="match-case-status" class="findbar-find-fast" />
|
<label anonid="match-case-status" class="findbar-find-fast" />
|
||||||
|
<label anonid="match-diacritics-status" class="findbar-find-fast" />
|
||||||
<label anonid="entire-word-status" class="findbar-find-fast" />
|
<label anonid="entire-word-status" class="findbar-find-fast" />
|
||||||
<label anonid="found-matches" class="findbar-find-fast found-matches" hidden="true" />
|
<label anonid="found-matches" class="findbar-find-fast found-matches" hidden="true" />
|
||||||
<image anonid="find-status-icon" class="findbar-find-fast find-status-icon" />
|
<image anonid="find-status-icon" class="findbar-find-fast find-status-icon" />
|
||||||
@@ -390,6 +394,9 @@
|
|||||||
case "findbar.highlightAll":
|
case "findbar.highlightAll":
|
||||||
this.toggleHighlight(prefsvc.getBoolPref(prefName), true);
|
this.toggleHighlight(prefsvc.getBoolPref(prefName), true);
|
||||||
break;
|
break;
|
||||||
|
case "findbar.matchdiacritics":
|
||||||
|
this._setDiacriticMatching(prefsvc.getIntPref(prefName));
|
||||||
|
break;
|
||||||
case "findbar.modalHighlight":
|
case "findbar.modalHighlight":
|
||||||
this._useModalHighlight = prefsvc.getBoolPref(prefName);
|
this._useModalHighlight = prefsvc.getBoolPref(prefName);
|
||||||
if (this.browser.finder) {
|
if (this.browser.finder) {
|
||||||
@@ -598,6 +605,55 @@
|
|||||||
this._dispatchFindEvent("casesensitivitychange");
|
this._dispatchFindEvent("casesensitivitychange");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the diacritic-matching mode of the findbar and its UI.
|
||||||
|
*
|
||||||
|
* @param {String} [str] The string for which diacritic matching might be
|
||||||
|
* turned on. This is only used when diacritic
|
||||||
|
* matching is in auto mode, see
|
||||||
|
* `_shouldMatchDiacritics`. The default value for
|
||||||
|
* this parameter is the find-field value.
|
||||||
|
* @see _shouldMatchDiacritics.
|
||||||
|
*/
|
||||||
|
_updateDiacriticMatching(str) {
|
||||||
|
let val = str || this._findField.value;
|
||||||
|
|
||||||
|
let matchDiacritics = this._shouldMatchDiacritics(val);
|
||||||
|
let checkbox = this.getElement("find-match-diacritics");
|
||||||
|
let statusLabel = this.getElement("match-diacritics-status");
|
||||||
|
checkbox.checked = matchDiacritics;
|
||||||
|
|
||||||
|
statusLabel.value = matchDiacritics ? this._matchDiacriticsStr : "";
|
||||||
|
|
||||||
|
// Show the checkbox on the full Find bar in non-auto mode.
|
||||||
|
// Show the label in all other cases.
|
||||||
|
let hideCheckbox =
|
||||||
|
this.findMode != this.FIND_NORMAL ||
|
||||||
|
(this._matchDiacritics != 0 && this._matchDiacritics != 1);
|
||||||
|
checkbox.hidden = hideCheckbox;
|
||||||
|
statusLabel.hidden = !hideCheckbox;
|
||||||
|
|
||||||
|
this.browser.finder.matchDiacritics = matchDiacritics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the findbar diacritic-matching mode
|
||||||
|
* @param {Number} diacriticMatching 0 - ignore diacritics,
|
||||||
|
* 1 - match diacritics,
|
||||||
|
* 2 - auto = match diacritics if the
|
||||||
|
* matching string contains
|
||||||
|
* diacritics.
|
||||||
|
* @see _shouldMatchDiacritics
|
||||||
|
*/
|
||||||
|
_setDiacriticMatching(diacriticMatching) {
|
||||||
|
this._matchDiacritics = diacriticMatching;
|
||||||
|
this._updateDiacriticMatching();
|
||||||
|
this._findFailedString = null;
|
||||||
|
this._find();
|
||||||
|
|
||||||
|
this._dispatchFindEvent("diacriticmatchingchange");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the entire-word mode of the findbar and its UI.
|
* Updates the entire-word mode of the findbar and its UI.
|
||||||
*/
|
*/
|
||||||
@@ -659,6 +715,7 @@
|
|||||||
this._fastFindStr = bundle.GetStringFromName("FastFind");
|
this._fastFindStr = bundle.GetStringFromName("FastFind");
|
||||||
this._fastFindLinksStr = bundle.GetStringFromName("FastFindLinks");
|
this._fastFindLinksStr = bundle.GetStringFromName("FastFindLinks");
|
||||||
this._caseSensitiveStr = bundle.GetStringFromName("CaseSensitive");
|
this._caseSensitiveStr = bundle.GetStringFromName("CaseSensitive");
|
||||||
|
this._matchDiacriticsStr = bundle.GetStringFromName("MatchDiacritics");
|
||||||
this._entireWordStr = bundle.GetStringFromName("EntireWord");
|
this._entireWordStr = bundle.GetStringFromName("EntireWord");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,6 +840,17 @@
|
|||||||
return str != str.toLowerCase();
|
return str != str.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_shouldMatchDiacritics(str) {
|
||||||
|
if (this._matchDiacritics == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this._matchDiacritics == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str != str.normalize("NFD");
|
||||||
|
}
|
||||||
|
|
||||||
onMouseUp() {
|
onMouseUp() {
|
||||||
if (!this.hidden && this.findMode != this.FIND_NORMAL) {
|
if (!this.hidden && this.findMode != this.FIND_NORMAL) {
|
||||||
this.close();
|
this.close();
|
||||||
@@ -884,6 +952,7 @@
|
|||||||
).hidden = showMinimalUI;
|
).hidden = showMinimalUI;
|
||||||
foundMatches.hidden = showMinimalUI || !foundMatches.value;
|
foundMatches.hidden = showMinimalUI || !foundMatches.value;
|
||||||
this._updateCaseSensitivity();
|
this._updateCaseSensitivity();
|
||||||
|
this._updateDiacriticMatching();
|
||||||
this._setEntireWord();
|
this._setEntireWord();
|
||||||
this._setHighlightAll();
|
this._setHighlightAll();
|
||||||
|
|
||||||
@@ -933,6 +1002,7 @@
|
|||||||
|
|
||||||
this._enableFindButtons(val);
|
this._enableFindButtons(val);
|
||||||
this._updateCaseSensitivity(val);
|
this._updateCaseSensitivity(val);
|
||||||
|
this._updateDiacriticMatching(val);
|
||||||
this._setEntireWord();
|
this._setEntireWord();
|
||||||
|
|
||||||
this.browser.finder.fastFind(
|
this.browser.finder.fastFind(
|
||||||
@@ -1026,6 +1096,7 @@
|
|||||||
event.initCustomEvent("find" + type, true, true, {
|
event.initCustomEvent("find" + type, true, true, {
|
||||||
query: this._findField.value,
|
query: this._findField.value,
|
||||||
caseSensitive: !!this._typeAheadCaseSensitive,
|
caseSensitive: !!this._typeAheadCaseSensitive,
|
||||||
|
matchDiacritics: !!this._matchDiacritics,
|
||||||
entireWord: this._entireWord,
|
entireWord: this._entireWord,
|
||||||
highlightAll: this._highlightAll,
|
highlightAll: this._highlightAll,
|
||||||
findPrevious,
|
findPrevious,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ NormalFind=Find in page
|
|||||||
FastFind=Quick find
|
FastFind=Quick find
|
||||||
FastFindLinks=Quick find (links only)
|
FastFindLinks=Quick find (links only)
|
||||||
CaseSensitive=(Case sensitive)
|
CaseSensitive=(Case sensitive)
|
||||||
|
MatchDiacritics=(Matching diacritics)
|
||||||
EntireWord=(Whole words only)
|
EntireWord=(Whole words only)
|
||||||
# LOCALIZATION NOTE (FoundMatches): Semicolon-separated list of plural forms.
|
# LOCALIZATION NOTE (FoundMatches): Semicolon-separated list of plural forms.
|
||||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ findbar-case-sensitive =
|
|||||||
.accesskey = C
|
.accesskey = C
|
||||||
.tooltiptext = Search with case sensitivity
|
.tooltiptext = Search with case sensitivity
|
||||||
|
|
||||||
|
findbar-match-diacritics =
|
||||||
|
.label = Match Diacritics
|
||||||
|
.accesskey = D
|
||||||
|
.tooltiptext = Distinguish between accented letters and their base letters (for example, when searching for “resume”, “résumé” will not be matched)
|
||||||
|
|
||||||
findbar-entire-word =
|
findbar-entire-word =
|
||||||
.label = Whole Words
|
.label = Whole Words
|
||||||
.accesskey = W
|
.accesskey = W
|
||||||
|
|||||||
@@ -170,6 +170,14 @@ Finder.prototype = {
|
|||||||
this.iterator.reset();
|
this.iterator.reset();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set matchDiacritics(aMatchDiacritics) {
|
||||||
|
if (this._fastFind.matchDiacritics === aMatchDiacritics) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._fastFind.matchDiacritics = aMatchDiacritics;
|
||||||
|
this.iterator.reset();
|
||||||
|
},
|
||||||
|
|
||||||
set entireWord(aEntireWord) {
|
set entireWord(aEntireWord) {
|
||||||
if (this._fastFind.entireWord === aEntireWord) {
|
if (this._fastFind.entireWord === aEntireWord) {
|
||||||
return;
|
return;
|
||||||
@@ -341,6 +349,7 @@ Finder.prototype = {
|
|||||||
caseSensitive: this._fastFind.caseSensitive,
|
caseSensitive: this._fastFind.caseSensitive,
|
||||||
entireWord: this._fastFind.entireWord,
|
entireWord: this._fastFind.entireWord,
|
||||||
linksOnly: aArgs.linksOnly,
|
linksOnly: aArgs.linksOnly,
|
||||||
|
matchDiacritics: this._fastFind.matchDiacritics,
|
||||||
word: aArgs.searchString,
|
word: aArgs.searchString,
|
||||||
useSubFrames: aArgs.useSubFrames,
|
useSubFrames: aArgs.useSubFrames,
|
||||||
})
|
})
|
||||||
@@ -589,6 +598,7 @@ Finder.prototype = {
|
|||||||
caseSensitive: this._fastFind.caseSensitive,
|
caseSensitive: this._fastFind.caseSensitive,
|
||||||
entireWord: this._fastFind.entireWord,
|
entireWord: this._fastFind.entireWord,
|
||||||
linksOnly: aLinksOnly,
|
linksOnly: aLinksOnly,
|
||||||
|
matchDiacritics: this._fastFind.matchDiacritics,
|
||||||
word: aWord,
|
word: aWord,
|
||||||
useSubFrames: aUseSubFrames,
|
useSubFrames: aUseSubFrames,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ FinderHighlighter.prototype = {
|
|||||||
word,
|
word,
|
||||||
finder: this.finder,
|
finder: this.finder,
|
||||||
listener: this,
|
listener: this,
|
||||||
|
matchDiacritics: this.finder._fastFind.matchDiacritics,
|
||||||
useCache: true,
|
useCache: true,
|
||||||
useSubFrames,
|
useSubFrames,
|
||||||
window,
|
window,
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ FinderIterator.prototype = {
|
|||||||
* - onIteratorReset();
|
* - onIteratorReset();
|
||||||
* - onIteratorRestart({Object} iterParams);
|
* - onIteratorRestart({Object} iterParams);
|
||||||
* - onIteratorStart({Object} iterParams);
|
* - onIteratorStart({Object} iterParams);
|
||||||
|
* @param {Boolean} options.matchDiacritics Whether to search in
|
||||||
|
* diacritic-matching mode
|
||||||
* @param {Boolean} [options.useCache] Whether to allow results already
|
* @param {Boolean} [options.useCache] Whether to allow results already
|
||||||
* present in the cache or demand fresh.
|
* present in the cache or demand fresh.
|
||||||
* Optional, defaults to `false`.
|
* Optional, defaults to `false`.
|
||||||
@@ -104,6 +106,7 @@ FinderIterator.prototype = {
|
|||||||
limit,
|
limit,
|
||||||
linksOnly,
|
linksOnly,
|
||||||
listener,
|
listener,
|
||||||
|
matchDiacritics,
|
||||||
useCache,
|
useCache,
|
||||||
word,
|
word,
|
||||||
useSubFrames,
|
useSubFrames,
|
||||||
@@ -132,6 +135,9 @@ FinderIterator.prototype = {
|
|||||||
if (typeof entireWord != "boolean") {
|
if (typeof entireWord != "boolean") {
|
||||||
throw new Error("Missing required option 'entireWord'");
|
throw new Error("Missing required option 'entireWord'");
|
||||||
}
|
}
|
||||||
|
if (typeof matchDiacritics != "boolean") {
|
||||||
|
throw new Error("Missing required option 'matchDiacritics'");
|
||||||
|
}
|
||||||
if (!finder) {
|
if (!finder) {
|
||||||
throw new Error("Missing required option 'finder'");
|
throw new Error("Missing required option 'finder'");
|
||||||
}
|
}
|
||||||
@@ -158,6 +164,7 @@ FinderIterator.prototype = {
|
|||||||
caseSensitive,
|
caseSensitive,
|
||||||
entireWord,
|
entireWord,
|
||||||
linksOnly,
|
linksOnly,
|
||||||
|
matchDiacritics,
|
||||||
useCache,
|
useCache,
|
||||||
window,
|
window,
|
||||||
word,
|
word,
|
||||||
@@ -304,6 +311,8 @@ FinderIterator.prototype = {
|
|||||||
* @param {Boolean} options.entireWord Whether to search in entire-word mode
|
* @param {Boolean} options.entireWord Whether to search in entire-word mode
|
||||||
* @param {Boolean} options.linksOnly Whether to search for the word to be
|
* @param {Boolean} options.linksOnly Whether to search for the word to be
|
||||||
* present in links only
|
* present in links only
|
||||||
|
* @param {Boolean} options.matchDiacritics Whether to search in
|
||||||
|
* diacritic-matching mode
|
||||||
* @param {String} options.word The word being searched for
|
* @param {String} options.word The word being searched for
|
||||||
* @param (Boolean) options.useSubFrames Whether to search subframes
|
* @param (Boolean) options.useSubFrames Whether to search subframes
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
@@ -312,6 +321,7 @@ FinderIterator.prototype = {
|
|||||||
caseSensitive,
|
caseSensitive,
|
||||||
entireWord,
|
entireWord,
|
||||||
linksOnly,
|
linksOnly,
|
||||||
|
matchDiacritics,
|
||||||
word,
|
word,
|
||||||
useSubFrames,
|
useSubFrames,
|
||||||
}) {
|
}) {
|
||||||
@@ -320,6 +330,7 @@ FinderIterator.prototype = {
|
|||||||
this._currentParams.caseSensitive === caseSensitive &&
|
this._currentParams.caseSensitive === caseSensitive &&
|
||||||
this._currentParams.entireWord === entireWord &&
|
this._currentParams.entireWord === entireWord &&
|
||||||
this._currentParams.linksOnly === linksOnly &&
|
this._currentParams.linksOnly === linksOnly &&
|
||||||
|
this._currentParams.matchDiacritics === matchDiacritics &&
|
||||||
this._currentParams.word == word &&
|
this._currentParams.word == word &&
|
||||||
this._currentParams.useSubFrames == useSubFrames
|
this._currentParams.useSubFrames == useSubFrames
|
||||||
);
|
);
|
||||||
@@ -374,6 +385,8 @@ FinderIterator.prototype = {
|
|||||||
* @param {Boolean} options.entireWord Whether to search in entire-word mode
|
* @param {Boolean} options.entireWord Whether to search in entire-word mode
|
||||||
* @param {Boolean} options.linksOnly Whether to search for the word to be
|
* @param {Boolean} options.linksOnly Whether to search for the word to be
|
||||||
* present in links only
|
* present in links only
|
||||||
|
* @param {Boolean} options.matchDiacritics Whether to search in
|
||||||
|
* diacritic-matching mode
|
||||||
* @param {Boolean} options.useCache Whether the consumer wants to use the
|
* @param {Boolean} options.useCache Whether the consumer wants to use the
|
||||||
* cached previous result at all
|
* cached previous result at all
|
||||||
* @param {String} options.word The word being searched for
|
* @param {String} options.word The word being searched for
|
||||||
@@ -383,6 +396,7 @@ FinderIterator.prototype = {
|
|||||||
caseSensitive,
|
caseSensitive,
|
||||||
entireWord,
|
entireWord,
|
||||||
linksOnly,
|
linksOnly,
|
||||||
|
matchDiacritics,
|
||||||
useCache,
|
useCache,
|
||||||
word,
|
word,
|
||||||
}) {
|
}) {
|
||||||
@@ -392,6 +406,7 @@ FinderIterator.prototype = {
|
|||||||
caseSensitive,
|
caseSensitive,
|
||||||
entireWord,
|
entireWord,
|
||||||
linksOnly,
|
linksOnly,
|
||||||
|
matchDiacritics,
|
||||||
word,
|
word,
|
||||||
}) &&
|
}) &&
|
||||||
this._previousRanges.length
|
this._previousRanges.length
|
||||||
@@ -415,6 +430,7 @@ FinderIterator.prototype = {
|
|||||||
paramSet1.caseSensitive === paramSet2.caseSensitive &&
|
paramSet1.caseSensitive === paramSet2.caseSensitive &&
|
||||||
paramSet1.entireWord === paramSet2.entireWord &&
|
paramSet1.entireWord === paramSet2.entireWord &&
|
||||||
paramSet1.linksOnly === paramSet2.linksOnly &&
|
paramSet1.linksOnly === paramSet2.linksOnly &&
|
||||||
|
paramSet1.matchDiacritics === paramSet2.matchDiacritics &&
|
||||||
paramSet1.window === paramSet2.window &&
|
paramSet1.window === paramSet2.window &&
|
||||||
paramSet1.useSubFrames === paramSet2.useSubFrames &&
|
paramSet1.useSubFrames === paramSet2.useSubFrames &&
|
||||||
NLP.levenshtein(paramSet1.word, paramSet2.word) <= allowDistance
|
NLP.levenshtein(paramSet1.word, paramSet2.word) <= allowDistance
|
||||||
@@ -624,11 +640,16 @@ FinderIterator.prototype = {
|
|||||||
* sensitive mode
|
* sensitive mode
|
||||||
* @param {Boolean} options.entireWord Whether to search in entire-word
|
* @param {Boolean} options.entireWord Whether to search in entire-word
|
||||||
* mode
|
* mode
|
||||||
|
* @param {Boolean} options.matchDiacritics Whether to search in
|
||||||
|
* diacritic-matching mode
|
||||||
* @param {String} options.word The word to search for
|
* @param {String} options.word The word to search for
|
||||||
* @param {nsIDOMWindow} window The window to search in
|
* @param {nsIDOMWindow} window The window to search in
|
||||||
* @yield {Range}
|
* @yield {Range}
|
||||||
*/
|
*/
|
||||||
*_iterateDocument({ caseSensitive, entireWord, word }, window) {
|
*_iterateDocument(
|
||||||
|
{ caseSensitive, entireWord, matchDiacritics, word },
|
||||||
|
window
|
||||||
|
) {
|
||||||
let doc = window.document;
|
let doc = window.document;
|
||||||
let body = doc.body || doc.documentElement;
|
let body = doc.body || doc.documentElement;
|
||||||
|
|
||||||
@@ -652,6 +673,7 @@ FinderIterator.prototype = {
|
|||||||
.QueryInterface(Ci.nsIFind);
|
.QueryInterface(Ci.nsIFind);
|
||||||
nsIFind.caseSensitive = caseSensitive;
|
nsIFind.caseSensitive = caseSensitive;
|
||||||
nsIFind.entireWord = entireWord;
|
nsIFind.entireWord = entireWord;
|
||||||
|
nsIFind.matchDiacritics = matchDiacritics;
|
||||||
|
|
||||||
while ((retRange = nsIFind.Find(word, searchRange, startPt, endPt))) {
|
while ((retRange = nsIFind.Find(word, searchRange, startPt, endPt))) {
|
||||||
yield retRange;
|
yield retRange;
|
||||||
|
|||||||
@@ -217,6 +217,12 @@ FinderParent.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set matchDiacritics(aMatchDiacritics) {
|
||||||
|
this.sendMessageToAllContexts("Finder:MatchDiacritics", {
|
||||||
|
matchDiacritics: aMatchDiacritics,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async setSearchStringToSelection() {
|
async setSearchStringToSelection() {
|
||||||
return this.setToSelection("Finder:SetSearchStringToSelection", false);
|
return this.setToSelection("Finder:SetSearchStringToSelection", false);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ add_task(async function test_start() {
|
|||||||
Assert.equal(range.toString(), findText, "Text content should match");
|
Assert.equal(range.toString(), findText, "Text content should match");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,6 +89,7 @@ add_task(async function test_subframes() {
|
|||||||
Assert.equal(range.toString(), findText, "Text content should match");
|
Assert.equal(range.toString(), findText, "Text content should match");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
useSubFrames: true,
|
useSubFrames: true,
|
||||||
});
|
});
|
||||||
@@ -119,6 +121,7 @@ add_task(async function test_valid_arguments() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -137,6 +140,7 @@ add_task(async function test_valid_arguments() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
}),
|
}),
|
||||||
/Missing required option 'caseSensitive'/,
|
/Missing required option 'caseSensitive'/,
|
||||||
@@ -144,6 +148,23 @@ add_task(async function test_valid_arguments() {
|
|||||||
);
|
);
|
||||||
finderIterator.reset();
|
finderIterator.reset();
|
||||||
|
|
||||||
|
Assert.throws(
|
||||||
|
() =>
|
||||||
|
finderIterator.start({
|
||||||
|
caseSensitive: false,
|
||||||
|
entireWord: false,
|
||||||
|
listener: {
|
||||||
|
onIteratorRangeFound(range) {
|
||||||
|
++count;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
word: findText,
|
||||||
|
}),
|
||||||
|
/Missing required option 'matchDiacritics'/,
|
||||||
|
"Should throw when missing an argument"
|
||||||
|
);
|
||||||
|
finderIterator.reset();
|
||||||
|
|
||||||
Assert.throws(
|
Assert.throws(
|
||||||
() =>
|
() =>
|
||||||
finderIterator.start({
|
finderIterator.start({
|
||||||
@@ -153,6 +174,7 @@ add_task(async function test_valid_arguments() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
}),
|
}),
|
||||||
/Missing required option 'entireWord'/,
|
/Missing required option 'entireWord'/,
|
||||||
@@ -170,6 +192,7 @@ add_task(async function test_valid_arguments() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
}),
|
}),
|
||||||
/Missing required option 'finder'/,
|
/Missing required option 'finder'/,
|
||||||
@@ -183,6 +206,7 @@ add_task(async function test_valid_arguments() {
|
|||||||
caseSensitive: true,
|
caseSensitive: true,
|
||||||
entireWord: false,
|
entireWord: false,
|
||||||
finder: gMockFinder,
|
finder: gMockFinder,
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
}),
|
}),
|
||||||
/Missing valid, required option 'listener'/,
|
/Missing valid, required option 'listener'/,
|
||||||
@@ -201,6 +225,7 @@ add_task(async function test_valid_arguments() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
}),
|
}),
|
||||||
/Missing required option 'word'/,
|
/Missing required option 'word'/,
|
||||||
"Should throw when missing an argument"
|
"Should throw when missing an argument"
|
||||||
@@ -225,6 +250,7 @@ add_task(async function test_stop() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -252,6 +278,7 @@ add_task(async function test_reset() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -294,10 +321,11 @@ add_task(async function test_parallel_starts() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise(resolve => gMockWindow.setTimeout(resolve, 120));
|
await new Promise(resolve => gMockWindow.setTimeout(resolve, 100));
|
||||||
Assert.ok(finderIterator.running, "We ought to be running here");
|
Assert.ok(finderIterator.running, "We ought to be running here");
|
||||||
|
|
||||||
let count2 = 0;
|
let count2 = 0;
|
||||||
@@ -310,6 +338,7 @@ add_task(async function test_parallel_starts() {
|
|||||||
++count2;
|
++count2;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -360,6 +389,7 @@ add_task(async function test_allowDistance() {
|
|||||||
++count;
|
++count;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: findText,
|
word: findText,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -373,6 +403,7 @@ add_task(async function test_allowDistance() {
|
|||||||
++count2;
|
++count2;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: "gu",
|
word: "gu",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -387,6 +418,7 @@ add_task(async function test_allowDistance() {
|
|||||||
++count3;
|
++count3;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
matchDiacritics: false,
|
||||||
word: "gu",
|
word: "gu",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user