mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1548389 - part 5: Make nsTextFrame
and related code treat masking password characters as text-transform r=jfkthame
Anonymous text node in password field has `NS_MAYBE_MASKED` flag and `TextEditor` managing the node has range of unmasking. This patch makes `BuildTextRunsScanner::BuildTextRunForFrames()` treat text frame in password field as `text-transform` applied. For recording the unmask range, this patch creates `nsTransformedCharStyle::mMaskPassword` and treats unmask range start/end edges as different style boundary. Therefore, `nsCaseTransformTextRunFactory::TransformString()` can replace each character with a password mask character with the information. Note that we need to kill bidi algorithm in password fields with forms.css because caret position will tell everybody whether the typing character is an RTL or an LTR character. Differential Revision: https://phabricator.services.mozilla.com/D38009 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
aa340cd1c7
commit
5febecd19b
6
editor/reftests/passwd-5-with-Preview.html
Normal file
6
editor/reftests/passwd-5-with-Preview.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="password" value=" "><!-- even only whitespace, text frame should be created -->
|
||||
</body>
|
||||
</html>
|
8
editor/reftests/passwd-5-with-TextEditor.html
Normal file
8
editor/reftests/passwd-5-with-TextEditor.html
Normal file
@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body onload="document.getElementsByTagName('input')[0].focus();
|
||||
document.getElementsByTagName('input')[0].blur();
|
||||
document.documentElement.removeAttribute('class');">
|
||||
<input type="password" value=" "><!-- even only whitespace, text frame should be created -->
|
||||
</body>
|
||||
</html>
|
6
editor/reftests/passwd-6-ref.html
Normal file
6
editor/reftests/passwd-6-ref.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="password" value="012345678901234">
|
||||
</body>
|
||||
</html>
|
10
editor/reftests/passwd-6-with-Preview.html
Normal file
10
editor/reftests/passwd-6-with-Preview.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="password" value="🤔🦸🏽‍♀️辺󠄁क्‍"
|
||||
><!-- Simple Emoji (a surrogate pair in UTF-16),
|
||||
Complicate Emoji (Woman Superhero: Medium Skin Tone),
|
||||
Kanji with IVS,
|
||||
2 devanāgarī characters followed by ZWJ -->
|
||||
</body>
|
||||
</html>
|
12
editor/reftests/passwd-6-with-TextEditor.html
Normal file
12
editor/reftests/passwd-6-with-TextEditor.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body onload="document.getElementsByTagName('input')[0].focus();
|
||||
document.getElementsByTagName('input')[0].blur();
|
||||
document.documentElement.removeAttribute('class');">
|
||||
<input type="password" value="🤔🦸🏽‍♀️辺󠄁क्‍"
|
||||
><!-- Simple Emoji (a surrogate pair in UTF-16),
|
||||
Complicate Emoji (Woman Superhero: Medium Skin Tone),
|
||||
Kanji with IVS,
|
||||
2 devanāgarī characters followed by ZWJ -->
|
||||
</body>
|
||||
</html>
|
6
editor/reftests/passwd-7-with-Preview.html
Normal file
6
editor/reftests/passwd-7-with-Preview.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<input type="password" value="あ漢А؀ก"><!-- original character shouldn't affect the mask's font -->
|
||||
</body>
|
||||
</html>
|
8
editor/reftests/passwd-7-with-TextEditor.html
Normal file
8
editor/reftests/passwd-7-with-TextEditor.html
Normal file
@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body onload="document.getElementsByTagName('input')[0].focus();
|
||||
document.getElementsByTagName('input')[0].blur();
|
||||
document.documentElement.removeAttribute('class');">
|
||||
<input type="password" value="あ漢А؀ก"><!-- original character shouldn't affect the mask's font -->
|
||||
</body>
|
||||
</html>
|
7
editor/reftests/passwd-8-with-Preview.html
Normal file
7
editor/reftests/passwd-8-with-Preview.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body onload="document.getElementsByTagName('input')[0].type = 'password';
|
||||
document.documentElement.removeAttribute('class');">
|
||||
<input type="text" value="abcdef">
|
||||
</body>
|
||||
</html>
|
9
editor/reftests/passwd-8-with-TextEditor.html
Normal file
9
editor/reftests/passwd-8-with-TextEditor.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body onload="document.getElementsByTagName('input')[0].focus();
|
||||
document.getElementsByTagName('input')[0].blur();
|
||||
document.getElementsByTagName('input')[0].type = 'password';
|
||||
document.documentElement.removeAttribute('class');">
|
||||
<input type="text" value="abcdef">
|
||||
</body>
|
||||
</html>
|
9
editor/reftests/passwd-9-with-Preview.html
Normal file
9
editor/reftests/passwd-9-with-Preview.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body onload="document.getElementsByTagName('input')[0].focus();
|
||||
document.getElementsByTagName('input')[0].blur();
|
||||
document.getElementsByTagName('input')[0].type = 'text';
|
||||
document.documentElement.removeAttribute('class');">
|
||||
<input type="password" value="abcdef">
|
||||
</body>
|
||||
</html>
|
7
editor/reftests/passwd-9-with-TextEditor.html
Normal file
7
editor/reftests/passwd-9-with-TextEditor.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<body onload="document.getElementsByTagName('input')[0].type = 'text';
|
||||
document.documentElement.removeAttribute('class');">
|
||||
<input type="password" value="abcdef">
|
||||
</body>
|
||||
</html>
|
@ -14,6 +14,16 @@ include xul/reftest.list
|
||||
!= passwd-2.html passwd-ref.html
|
||||
== passwd-3.html passwd-ref.html
|
||||
needs-focus == passwd-4.html passwd-ref.html
|
||||
== passwd-5-with-Preview.html passwd-ref.html
|
||||
needs-focus == passwd-5-with-TextEditor.html passwd-ref.html
|
||||
== passwd-6-with-Preview.html passwd-6-ref.html
|
||||
needs-focus == passwd-6-with-TextEditor.html passwd-6-ref.html
|
||||
== passwd-7-with-Preview.html passwd-ref.html
|
||||
needs-focus == passwd-7-with-TextEditor.html passwd-ref.html
|
||||
== passwd-8-with-Preview.html passwd-ref.html
|
||||
needs-focus == passwd-8-with-TextEditor.html passwd-ref.html
|
||||
== passwd-9-with-Preview.html passwd-2.html
|
||||
needs-focus == passwd-9-with-TextEditor.html passwd-2.html
|
||||
== emptypasswd-1.html emptypasswd-ref.html
|
||||
== emptypasswd-2.html emptypasswd-ref.html
|
||||
== caret_on_positioned.html caret_on_positioned-ref.html
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/TextEditor.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/BinarySearch.h"
|
||||
#include "mozilla/IntegerRange.h"
|
||||
@ -103,6 +104,12 @@ using namespace mozilla::gfx;
|
||||
|
||||
typedef mozilla::layout::TextDrawTarget TextDrawTarget;
|
||||
|
||||
static bool NeedsToMaskPassword(nsTextFrame* aFrame) {
|
||||
MOZ_ASSERT(aFrame);
|
||||
MOZ_ASSERT(aFrame->GetContent());
|
||||
return aFrame->GetContent()->HasFlag(NS_MAYBE_MASKED);
|
||||
}
|
||||
|
||||
struct TabWidth {
|
||||
TabWidth(uint32_t aOffset, uint32_t aWidth)
|
||||
: mOffset(aOffset), mWidth(float(aWidth)) {}
|
||||
@ -2368,8 +2375,9 @@ already_AddRefed<gfxTextRun> BuildTextRunsScanner::BuildTextRunForFrames(
|
||||
}
|
||||
|
||||
// Setup factory chain
|
||||
bool needsToMaskPassword = NeedsToMaskPassword(firstFrame);
|
||||
UniquePtr<nsTransformingTextRunFactory> transformingFactory;
|
||||
if (anyTextTransformStyle) {
|
||||
if (anyTextTransformStyle || needsToMaskPassword) {
|
||||
transformingFactory = MakeUnique<nsCaseTransformTextRunFactory>(
|
||||
std::move(transformingFactory));
|
||||
}
|
||||
@ -2380,29 +2388,67 @@ already_AddRefed<gfxTextRun> BuildTextRunsScanner::BuildTextRunForFrames(
|
||||
}
|
||||
nsTArray<RefPtr<nsTransformedCharStyle>> styles;
|
||||
if (transformingFactory) {
|
||||
uint32_t unmaskStart = 0, unmaskEnd = UINT32_MAX;
|
||||
if (needsToMaskPassword) {
|
||||
unmaskStart = unmaskEnd = UINT32_MAX;
|
||||
TextEditor* passwordEditor =
|
||||
nsContentUtils::GetTextEditorFromAnonymousNodeWithoutCreation(
|
||||
firstFrame->GetContent());
|
||||
if (passwordEditor && !passwordEditor->IsAllMasked()) {
|
||||
unmaskStart = passwordEditor->UnmaskedStart();
|
||||
unmaskEnd = passwordEditor->UnmaskedEnd();
|
||||
}
|
||||
}
|
||||
|
||||
iter.SetOriginalOffset(0);
|
||||
for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
|
||||
MappedFlow* mappedFlow = &mMappedFlows[i];
|
||||
nsTextFrame* f;
|
||||
ComputedStyle* sc = nullptr;
|
||||
RefPtr<nsTransformedCharStyle> charStyle;
|
||||
RefPtr<nsTransformedCharStyle> defaultStyle;
|
||||
RefPtr<nsTransformedCharStyle> unmaskStyle;
|
||||
for (f = mappedFlow->mStartFrame; f != mappedFlow->mEndFrame;
|
||||
f = f->GetNextContinuation()) {
|
||||
uint32_t offset = iter.GetSkippedOffset();
|
||||
iter.AdvanceOriginal(f->GetContentLength());
|
||||
uint32_t end = iter.GetSkippedOffset();
|
||||
uint32_t skippedOffset = iter.GetSkippedOffset();
|
||||
// Text-combined frames have content-dependent transform, so we
|
||||
// want to create new nsTransformedCharStyle for them anyway.
|
||||
if (sc != f->Style() || sc->IsTextCombined()) {
|
||||
sc = f->Style();
|
||||
charStyle = new nsTransformedCharStyle(sc, f->PresContext());
|
||||
defaultStyle = new nsTransformedCharStyle(sc, f->PresContext());
|
||||
if (sc->IsTextCombined() && f->CountGraphemeClusters() > 1) {
|
||||
charStyle->mForceNonFullWidth = true;
|
||||
defaultStyle->mForceNonFullWidth = true;
|
||||
}
|
||||
if (needsToMaskPassword) {
|
||||
defaultStyle->mMaskPassword = true;
|
||||
if (unmaskStart != unmaskEnd) {
|
||||
unmaskStyle = new nsTransformedCharStyle(sc, f->PresContext());
|
||||
unmaskStyle->mForceNonFullWidth =
|
||||
defaultStyle->mForceNonFullWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t j;
|
||||
for (j = offset; j < end; ++j) {
|
||||
styles.AppendElement(charStyle);
|
||||
iter.AdvanceOriginal(f->GetContentLength());
|
||||
uint32_t skippedEnd = iter.GetSkippedOffset();
|
||||
if (unmaskStyle) {
|
||||
uint32_t skippedUnmaskStart =
|
||||
iter.ConvertOriginalToSkipped(unmaskStart);
|
||||
uint32_t skippedUnmaskEnd = iter.ConvertOriginalToSkipped(unmaskEnd);
|
||||
iter.SetSkippedOffset(skippedEnd);
|
||||
for (; skippedOffset < std::min(skippedEnd, skippedUnmaskStart);
|
||||
++skippedOffset) {
|
||||
styles.AppendElement(defaultStyle);
|
||||
}
|
||||
for (; skippedOffset < std::min(skippedEnd, skippedUnmaskEnd);
|
||||
++skippedOffset) {
|
||||
styles.AppendElement(unmaskStyle);
|
||||
}
|
||||
for (; skippedOffset < skippedEnd; ++skippedOffset) {
|
||||
styles.AppendElement(defaultStyle);
|
||||
}
|
||||
} else {
|
||||
for (; skippedOffset < skippedEnd; ++skippedOffset) {
|
||||
styles.AppendElement(defaultStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9576,7 +9622,7 @@ static void TransformChars(nsTextFrame* aFrame, const nsStyleText* aStyle,
|
||||
int32_t aFragLen, nsAString& aOut) {
|
||||
nsAutoString fragString;
|
||||
char16_t* out;
|
||||
if (aStyle->mTextTransform.IsNone()) {
|
||||
if (aStyle->mTextTransform.IsNone() && !NeedsToMaskPassword(aFrame)) {
|
||||
// No text-transform, so we can copy directly to the output string.
|
||||
aOut.SetLength(aOut.Length() + aFragLen);
|
||||
out = aOut.EndWriting() - aFragLen;
|
||||
@ -9596,7 +9642,7 @@ static void TransformChars(nsTextFrame* aFrame, const nsStyleText* aStyle,
|
||||
out[i] = ch;
|
||||
}
|
||||
|
||||
if (!aStyle->mTextTransform.IsNone()) {
|
||||
if (!aStyle->mTextTransform.IsNone() || NeedsToMaskPassword(aFrame)) {
|
||||
MOZ_ASSERT(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsTransformed);
|
||||
if (aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsTransformed) {
|
||||
// Apply text-transform according to style in the transformed run.
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "nsTextRunTransformations.h"
|
||||
|
||||
#include "mozilla/ComputedStyleInlines.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
@ -278,6 +279,7 @@ bool nsCaseTransformTextRunFactory::TransformString(
|
||||
|
||||
uint32_t length = aString.Length();
|
||||
const char16_t* str = aString.BeginReading();
|
||||
const char16_t kPasswordMask = LookAndFeel::GetPasswordCharacter();
|
||||
|
||||
bool mergeNeeded = false;
|
||||
|
||||
@ -334,6 +336,7 @@ bool nsCaseTransformTextRunFactory::TransformString(
|
||||
}
|
||||
}
|
||||
|
||||
bool maskPassword = charStyle && charStyle->mMaskPassword;
|
||||
int extraChars = 0;
|
||||
const mozilla::unicode::MultiCharMapping* mcm;
|
||||
bool inhibitBreakBefore = false; // have we just deleted preceding hyphen?
|
||||
@ -341,417 +344,431 @@ bool nsCaseTransformTextRunFactory::TransformString(
|
||||
if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 &&
|
||||
NS_IS_LOW_SURROGATE(str[i + 1])) {
|
||||
ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
|
||||
// A non-BMP character is masked with 2 mask characters. Therefore,
|
||||
// one of them is unmasked, we should unmask the surrogate pair.
|
||||
if (maskPassword &&
|
||||
!aTextRun->mStyles[aOffsetInTextRun + 1]->mMaskPassword) {
|
||||
maskPassword = false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (style.case_) {
|
||||
case StyleTextTransformCase::None:
|
||||
break;
|
||||
// Skip case transform if we're masking current character.
|
||||
if (!maskPassword) {
|
||||
switch (style.case_) {
|
||||
case StyleTextTransformCase::None:
|
||||
break;
|
||||
|
||||
case StyleTextTransformCase::Lowercase:
|
||||
if (languageSpecificCasing == eLSCB_Turkish) {
|
||||
if (ch == 'I') {
|
||||
ch = LATIN_SMALL_LETTER_DOTLESS_I;
|
||||
prevIsLetter = true;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE) {
|
||||
ch = 'i';
|
||||
prevIsLetter = true;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Lithuanian) {
|
||||
// clang-format off
|
||||
/* From SpecialCasing.txt:
|
||||
* # Introduce an explicit dot above when lowercasing capital I's and J's
|
||||
* # whenever there are more accents above.
|
||||
* # (of the accents used in Lithuanian: grave, acute, tilde above, and ogonek)
|
||||
*
|
||||
* 0049; 0069 0307; 0049; 0049; lt More_Above; # LATIN CAPITAL LETTER I
|
||||
* 004A; 006A 0307; 004A; 004A; lt More_Above; # LATIN CAPITAL LETTER J
|
||||
* 012E; 012F 0307; 012E; 012E; lt More_Above; # LATIN CAPITAL LETTER I WITH OGONEK
|
||||
* 00CC; 0069 0307 0300; 00CC; 00CC; lt; # LATIN CAPITAL LETTER I WITH GRAVE
|
||||
* 00CD; 0069 0307 0301; 00CD; 00CD; lt; # LATIN CAPITAL LETTER I WITH ACUTE
|
||||
* 0128; 0069 0307 0303; 0128; 0128; lt; # LATIN CAPITAL LETTER I WITH TILDE
|
||||
*/
|
||||
// clang-format on
|
||||
if (ch == 'I' || ch == 'J' || ch == 0x012E) {
|
||||
ch = ToLowerCase(ch);
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = true;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == 0x00CC) {
|
||||
aConvertedString.Append('i');
|
||||
aConvertedString.Append(0x0307);
|
||||
extraChars += 2;
|
||||
ch = 0x0300;
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = false;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == 0x00CD) {
|
||||
aConvertedString.Append('i');
|
||||
aConvertedString.Append(0x0307);
|
||||
extraChars += 2;
|
||||
ch = 0x0301;
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = false;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == 0x0128) {
|
||||
aConvertedString.Append('i');
|
||||
aConvertedString.Append(0x0307);
|
||||
extraChars += 2;
|
||||
ch = 0x0303;
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = false;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cat = mozilla::unicode::GetGenCategory(ch);
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Irish &&
|
||||
cat == nsUGenCategory::kLetter) {
|
||||
// See bug 1018805 for Irish lowercasing requirements
|
||||
if (!prevIsLetter && (ch == 'n' || ch == 't')) {
|
||||
ntPrefix = true;
|
||||
} else {
|
||||
if (ntPrefix && mozilla::IrishCasing::IsUpperVowel(ch)) {
|
||||
aConvertedString.Append('-');
|
||||
++extraChars;
|
||||
case StyleTextTransformCase::Lowercase:
|
||||
if (languageSpecificCasing == eLSCB_Turkish) {
|
||||
if (ch == 'I') {
|
||||
ch = LATIN_SMALL_LETTER_DOTLESS_I;
|
||||
prevIsLetter = true;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE) {
|
||||
ch = 'i';
|
||||
prevIsLetter = true;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Lithuanian) {
|
||||
// clang-format off
|
||||
/* From SpecialCasing.txt:
|
||||
* # Introduce an explicit dot above when lowercasing capital I's and J's
|
||||
* # whenever there are more accents above.
|
||||
* # (of the accents used in Lithuanian: grave, acute, tilde above, and ogonek)
|
||||
*
|
||||
* 0049; 0069 0307; 0049; 0049; lt More_Above; # LATIN CAPITAL LETTER I
|
||||
* 004A; 006A 0307; 004A; 004A; lt More_Above; # LATIN CAPITAL LETTER J
|
||||
* 012E; 012F 0307; 012E; 012E; lt More_Above; # LATIN CAPITAL LETTER I WITH OGONEK
|
||||
* 00CC; 0069 0307 0300; 00CC; 00CC; lt; # LATIN CAPITAL LETTER I WITH GRAVE
|
||||
* 00CD; 0069 0307 0301; 00CD; 00CD; lt; # LATIN CAPITAL LETTER I WITH ACUTE
|
||||
* 0128; 0069 0307 0303; 0128; 0128; lt; # LATIN CAPITAL LETTER I WITH TILDE
|
||||
*/
|
||||
// clang-format on
|
||||
if (ch == 'I' || ch == 'J' || ch == 0x012E) {
|
||||
ch = ToLowerCase(ch);
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = true;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == 0x00CC) {
|
||||
aConvertedString.Append('i');
|
||||
aConvertedString.Append(0x0307);
|
||||
extraChars += 2;
|
||||
ch = 0x0300;
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = false;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == 0x00CD) {
|
||||
aConvertedString.Append('i');
|
||||
aConvertedString.Append(0x0307);
|
||||
extraChars += 2;
|
||||
ch = 0x0301;
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = false;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (ch == 0x0128) {
|
||||
aConvertedString.Append('i');
|
||||
aConvertedString.Append(0x0307);
|
||||
extraChars += 2;
|
||||
ch = 0x0303;
|
||||
prevIsLetter = true;
|
||||
seenSoftDotted = false;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cat = mozilla::unicode::GetGenCategory(ch);
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Irish &&
|
||||
cat == nsUGenCategory::kLetter) {
|
||||
// See bug 1018805 for Irish lowercasing requirements
|
||||
if (!prevIsLetter && (ch == 'n' || ch == 't')) {
|
||||
ntPrefix = true;
|
||||
} else {
|
||||
if (ntPrefix && mozilla::IrishCasing::IsUpperVowel(ch)) {
|
||||
aConvertedString.Append('-');
|
||||
++extraChars;
|
||||
}
|
||||
ntPrefix = false;
|
||||
}
|
||||
} else {
|
||||
ntPrefix = false;
|
||||
}
|
||||
} else {
|
||||
ntPrefix = false;
|
||||
}
|
||||
|
||||
if (seenSoftDotted && cat == nsUGenCategory::kMark) {
|
||||
// The seenSoftDotted flag will only be set in Lithuanian mode.
|
||||
if (ch == 0x0300 || ch == 0x0301 || ch == 0x0303) {
|
||||
aConvertedString.Append(0x0307);
|
||||
++extraChars;
|
||||
if (seenSoftDotted && cat == nsUGenCategory::kMark) {
|
||||
// The seenSoftDotted flag will only be set in Lithuanian mode.
|
||||
if (ch == 0x0300 || ch == 0x0301 || ch == 0x0303) {
|
||||
aConvertedString.Append(0x0307);
|
||||
++extraChars;
|
||||
}
|
||||
}
|
||||
}
|
||||
seenSoftDotted = false;
|
||||
seenSoftDotted = false;
|
||||
|
||||
// Special lowercasing behavior for Greek Sigma: note that this is
|
||||
// listed as context-sensitive in Unicode's SpecialCasing.txt, but is
|
||||
// *not* a language-specific mapping; it applies regardless of the
|
||||
// language of the element.
|
||||
//
|
||||
// The lowercase mapping for CAPITAL SIGMA should be to SMALL SIGMA
|
||||
// (i.e. the non-final form) whenever there is a following letter, or
|
||||
// when the CAPITAL SIGMA occurs in isolation (neither preceded nor
|
||||
// followed by a LETTER); and to FINAL SIGMA when it is preceded by
|
||||
// another letter but not followed by one.
|
||||
//
|
||||
// To implement the context-sensitive nature of this mapping, we keep
|
||||
// track of whether the previous character was a letter. If not, CAPITAL
|
||||
// SIGMA will map directly to SMALL SIGMA. If the previous character
|
||||
// was a letter, CAPITAL SIGMA maps to FINAL SIGMA and we record the
|
||||
// position in the converted string; if we then encounter another
|
||||
// letter, that FINAL SIGMA is replaced with a standard SMALL SIGMA.
|
||||
// Special lowercasing behavior for Greek Sigma: note that this is
|
||||
// listed as context-sensitive in Unicode's SpecialCasing.txt, but is
|
||||
// *not* a language-specific mapping; it applies regardless of the
|
||||
// language of the element.
|
||||
//
|
||||
// The lowercase mapping for CAPITAL SIGMA should be to SMALL SIGMA
|
||||
// (i.e. the non-final form) whenever there is a following letter, or
|
||||
// when the CAPITAL SIGMA occurs in isolation (neither preceded nor
|
||||
// followed by a LETTER); and to FINAL SIGMA when it is preceded by
|
||||
// another letter but not followed by one.
|
||||
//
|
||||
// To implement the context-sensitive nature of this mapping, we keep
|
||||
// track of whether the previous character was a letter. If not,
|
||||
// CAPITAL SIGMA will map directly to SMALL SIGMA. If the previous
|
||||
// character was a letter, CAPITAL SIGMA maps to FINAL SIGMA and we
|
||||
// record the position in the converted string; if we then encounter
|
||||
// another letter, that FINAL SIGMA is replaced with a standard
|
||||
// SMALL SIGMA.
|
||||
|
||||
// If sigmaIndex is not -1, it marks where we have provisionally mapped
|
||||
// a CAPITAL SIGMA to FINAL SIGMA; if we now find another letter, we
|
||||
// need to change it to SMALL SIGMA.
|
||||
if (sigmaIndex != uint32_t(-1)) {
|
||||
if (cat == nsUGenCategory::kLetter) {
|
||||
aConvertedString.SetCharAt(GREEK_SMALL_LETTER_SIGMA, sigmaIndex);
|
||||
// If sigmaIndex is not -1, it marks where we have provisionally
|
||||
// mapped a CAPITAL SIGMA to FINAL SIGMA; if we now find another
|
||||
// letter, we need to change it to SMALL SIGMA.
|
||||
if (sigmaIndex != uint32_t(-1)) {
|
||||
if (cat == nsUGenCategory::kLetter) {
|
||||
aConvertedString.SetCharAt(GREEK_SMALL_LETTER_SIGMA, sigmaIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == GREEK_CAPITAL_LETTER_SIGMA) {
|
||||
// If preceding char was a letter, map to FINAL instead of SMALL,
|
||||
// and note where it occurred by setting sigmaIndex; we'll change it
|
||||
// to standard SMALL SIGMA later if another letter follows
|
||||
if (prevIsLetter) {
|
||||
ch = GREEK_SMALL_LETTER_FINAL_SIGMA;
|
||||
sigmaIndex = aConvertedString.Length();
|
||||
} else {
|
||||
// CAPITAL SIGMA not preceded by a letter is unconditionally mapped
|
||||
// to SMALL SIGMA
|
||||
ch = GREEK_SMALL_LETTER_SIGMA;
|
||||
if (ch == GREEK_CAPITAL_LETTER_SIGMA) {
|
||||
// If preceding char was a letter, map to FINAL instead of SMALL,
|
||||
// and note where it occurred by setting sigmaIndex; we'll change
|
||||
// it to standard SMALL SIGMA later if another letter follows
|
||||
if (prevIsLetter) {
|
||||
ch = GREEK_SMALL_LETTER_FINAL_SIGMA;
|
||||
sigmaIndex = aConvertedString.Length();
|
||||
} else {
|
||||
// CAPITAL SIGMA not preceded by a letter is unconditionally
|
||||
// mapped to SMALL SIGMA
|
||||
ch = GREEK_SMALL_LETTER_SIGMA;
|
||||
sigmaIndex = uint32_t(-1);
|
||||
}
|
||||
prevIsLetter = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// ignore diacritics for the purpose of contextual sigma mapping;
|
||||
// otherwise, reset prevIsLetter appropriately and clear the
|
||||
// sigmaIndex marker
|
||||
if (cat != nsUGenCategory::kMark) {
|
||||
prevIsLetter = (cat == nsUGenCategory::kLetter);
|
||||
sigmaIndex = uint32_t(-1);
|
||||
}
|
||||
prevIsLetter = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// ignore diacritics for the purpose of contextual sigma mapping;
|
||||
// otherwise, reset prevIsLetter appropriately and clear the
|
||||
// sigmaIndex marker
|
||||
if (cat != nsUGenCategory::kMark) {
|
||||
prevIsLetter = (cat == nsUGenCategory::kLetter);
|
||||
sigmaIndex = uint32_t(-1);
|
||||
}
|
||||
|
||||
mcm = mozilla::unicode::SpecialLower(ch);
|
||||
if (mcm) {
|
||||
int j = 0;
|
||||
while (j < 2 && mcm->mMappedChars[j + 1]) {
|
||||
aConvertedString.Append(mcm->mMappedChars[j]);
|
||||
++extraChars;
|
||||
++j;
|
||||
}
|
||||
ch = mcm->mMappedChars[j];
|
||||
break;
|
||||
}
|
||||
|
||||
ch = ToLowerCase(ch);
|
||||
break;
|
||||
|
||||
case StyleTextTransformCase::Uppercase:
|
||||
if (languageSpecificCasing == eLSCB_Turkish && ch == 'i') {
|
||||
ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Greek) {
|
||||
bool markEta;
|
||||
bool updateEta;
|
||||
ch = mozilla::GreekCasing::UpperCase(ch, greekState, markEta,
|
||||
updateEta);
|
||||
if (markEta) {
|
||||
greekMark = aConvertedString.Length();
|
||||
} else if (updateEta) {
|
||||
// Remove the TONOS from an uppercase ETA-TONOS that turned out
|
||||
// not to be disjunctive-eta.
|
||||
MOZ_ASSERT(aConvertedString.Length() > 0 &&
|
||||
greekMark < aConvertedString.Length(),
|
||||
"bad greekMark!");
|
||||
aConvertedString.SetCharAt(kGreekUpperEta, greekMark);
|
||||
greekMark = uint32_t(-1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Lithuanian) {
|
||||
/*
|
||||
* # Remove DOT ABOVE after "i" with upper or titlecase
|
||||
*
|
||||
* 0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE
|
||||
*/
|
||||
if (ch == 'i' || ch == 'j' || ch == 0x012F) {
|
||||
seenSoftDotted = true;
|
||||
ch = ToTitleCase(ch);
|
||||
mcm = mozilla::unicode::SpecialLower(ch);
|
||||
if (mcm) {
|
||||
int j = 0;
|
||||
while (j < 2 && mcm->mMappedChars[j + 1]) {
|
||||
aConvertedString.Append(mcm->mMappedChars[j]);
|
||||
++extraChars;
|
||||
++j;
|
||||
}
|
||||
ch = mcm->mMappedChars[j];
|
||||
break;
|
||||
}
|
||||
if (seenSoftDotted) {
|
||||
seenSoftDotted = false;
|
||||
if (ch == 0x0307) {
|
||||
ch = uint32_t(-1);
|
||||
|
||||
ch = ToLowerCase(ch);
|
||||
break;
|
||||
|
||||
case StyleTextTransformCase::Uppercase:
|
||||
if (languageSpecificCasing == eLSCB_Turkish && ch == 'i') {
|
||||
ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Greek) {
|
||||
bool markEta;
|
||||
bool updateEta;
|
||||
ch = mozilla::GreekCasing::UpperCase(ch, greekState, markEta,
|
||||
updateEta);
|
||||
if (markEta) {
|
||||
greekMark = aConvertedString.Length();
|
||||
} else if (updateEta) {
|
||||
// Remove the TONOS from an uppercase ETA-TONOS that turned out
|
||||
// not to be disjunctive-eta.
|
||||
MOZ_ASSERT(aConvertedString.Length() > 0 &&
|
||||
greekMark < aConvertedString.Length(),
|
||||
"bad greekMark!");
|
||||
aConvertedString.SetCharAt(kGreekUpperEta, greekMark);
|
||||
greekMark = uint32_t(-1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Lithuanian) {
|
||||
/*
|
||||
* # Remove DOT ABOVE after "i" with upper or titlecase
|
||||
*
|
||||
* 0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE
|
||||
*/
|
||||
if (ch == 'i' || ch == 'j' || ch == 0x012F) {
|
||||
seenSoftDotted = true;
|
||||
ch = ToTitleCase(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Irish) {
|
||||
bool mark;
|
||||
uint8_t action;
|
||||
ch = mozilla::IrishCasing::UpperCase(ch, irishState, mark, action);
|
||||
if (mark) {
|
||||
irishMark = aConvertedString.Length();
|
||||
irishMarkSrc = i;
|
||||
break;
|
||||
} else if (action) {
|
||||
nsString& str = aConvertedString; // shorthand
|
||||
switch (action) {
|
||||
case 1:
|
||||
// lowercase a single prefix letter
|
||||
NS_ASSERTION(str.Length() > 0 && irishMark < str.Length(),
|
||||
"bad irishMark!");
|
||||
str.SetCharAt(ToLowerCase(str[irishMark]), irishMark);
|
||||
irishMark = uint32_t(-1);
|
||||
irishMarkSrc = uint32_t(-1);
|
||||
break;
|
||||
case 2:
|
||||
// lowercase two prefix letters (immediately before current pos)
|
||||
NS_ASSERTION(str.Length() >= 2 && irishMark == str.Length() - 2,
|
||||
"bad irishMark!");
|
||||
str.SetCharAt(ToLowerCase(str[irishMark]), irishMark);
|
||||
str.SetCharAt(ToLowerCase(str[irishMark + 1]), irishMark + 1);
|
||||
irishMark = uint32_t(-1);
|
||||
irishMarkSrc = uint32_t(-1);
|
||||
break;
|
||||
case 3:
|
||||
// lowercase one prefix letter, and delete following hyphen
|
||||
// (which must be the immediately-preceding char)
|
||||
NS_ASSERTION(str.Length() >= 2 && irishMark == str.Length() - 2,
|
||||
"bad irishMark!");
|
||||
MOZ_ASSERT(
|
||||
irishMark != uint32_t(-1) && irishMarkSrc != uint32_t(-1),
|
||||
"failed to set irishMarks");
|
||||
str.Replace(irishMark, 2, ToLowerCase(str[irishMark]));
|
||||
aDeletedCharsArray[irishMarkSrc + 1] = true;
|
||||
// Remove the trailing entries (corresponding to the deleted
|
||||
// hyphen) from the auxiliary arrays.
|
||||
aCharsToMergeArray.SetLength(aCharsToMergeArray.Length() - 1);
|
||||
if (auxiliaryOutputArrays) {
|
||||
aStyleArray->SetLength(aStyleArray->Length() - 1);
|
||||
aCanBreakBeforeArray->SetLength(
|
||||
aCanBreakBeforeArray->Length() - 1);
|
||||
inhibitBreakBefore = true;
|
||||
}
|
||||
mergeNeeded = true;
|
||||
irishMark = uint32_t(-1);
|
||||
irishMarkSrc = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
// ch has been set to the uppercase for current char;
|
||||
// No need to check for SpecialUpper here as none of the characters
|
||||
// that could trigger an Irish casing action have special mappings.
|
||||
break;
|
||||
}
|
||||
// If we didn't have any special action to perform, fall through
|
||||
// to check for special uppercase (ß)
|
||||
}
|
||||
|
||||
mcm = mozilla::unicode::SpecialUpper(ch);
|
||||
if (mcm) {
|
||||
int j = 0;
|
||||
while (j < 2 && mcm->mMappedChars[j + 1]) {
|
||||
aConvertedString.Append(mcm->mMappedChars[j]);
|
||||
++extraChars;
|
||||
++j;
|
||||
}
|
||||
ch = mcm->mMappedChars[j];
|
||||
break;
|
||||
}
|
||||
|
||||
// Bug 1476304: we exclude Georgian letters U+10D0..10FF because of lack
|
||||
// of widespread font support for the corresponding Mtavruli characters
|
||||
// at this time (July 2018).
|
||||
// This condition is to be removed once the major platforms ship with
|
||||
// fonts that support U+1C90..1CBF.
|
||||
if (ch < 0x10D0 || ch > 0x10FF) {
|
||||
ch = ToUpperCase(ch);
|
||||
}
|
||||
break;
|
||||
|
||||
case StyleTextTransformCase::Capitalize:
|
||||
if (aTextRun) {
|
||||
if (capitalizeDutchIJ && ch == 'j') {
|
||||
ch = 'J';
|
||||
capitalizeDutchIJ = false;
|
||||
break;
|
||||
}
|
||||
capitalizeDutchIJ = false;
|
||||
if (aOffsetInTextRun < aTextRun->mCapitalize.Length() &&
|
||||
aTextRun->mCapitalize[aOffsetInTextRun]) {
|
||||
if (languageSpecificCasing == eLSCB_Turkish && ch == 'i') {
|
||||
ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
|
||||
break;
|
||||
}
|
||||
if (languageSpecificCasing == eLSCB_Dutch && ch == 'i') {
|
||||
ch = 'I';
|
||||
capitalizeDutchIJ = true;
|
||||
break;
|
||||
}
|
||||
if (languageSpecificCasing == eLSCB_Lithuanian) {
|
||||
/*
|
||||
* # Remove DOT ABOVE after "i" with upper or titlecase
|
||||
*
|
||||
* 0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE
|
||||
*/
|
||||
if (ch == 'i' || ch == 'j' || ch == 0x012F) {
|
||||
seenSoftDotted = true;
|
||||
ch = ToTitleCase(ch);
|
||||
if (seenSoftDotted) {
|
||||
seenSoftDotted = false;
|
||||
if (ch == 0x0307) {
|
||||
ch = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
if (seenSoftDotted) {
|
||||
seenSoftDotted = false;
|
||||
if (ch == 0x0307) {
|
||||
ch = uint32_t(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (languageSpecificCasing == eLSCB_Irish) {
|
||||
bool mark;
|
||||
uint8_t action;
|
||||
ch = mozilla::IrishCasing::UpperCase(ch, irishState, mark, action);
|
||||
if (mark) {
|
||||
irishMark = aConvertedString.Length();
|
||||
irishMarkSrc = i;
|
||||
break;
|
||||
} else if (action) {
|
||||
nsString& str = aConvertedString; // shorthand
|
||||
switch (action) {
|
||||
case 1:
|
||||
// lowercase a single prefix letter
|
||||
NS_ASSERTION(str.Length() > 0 && irishMark < str.Length(),
|
||||
"bad irishMark!");
|
||||
str.SetCharAt(ToLowerCase(str[irishMark]), irishMark);
|
||||
irishMark = uint32_t(-1);
|
||||
irishMarkSrc = uint32_t(-1);
|
||||
break;
|
||||
case 2:
|
||||
// lowercase two prefix letters (immediately before current
|
||||
// pos)
|
||||
NS_ASSERTION(
|
||||
str.Length() >= 2 && irishMark == str.Length() - 2,
|
||||
"bad irishMark!");
|
||||
str.SetCharAt(ToLowerCase(str[irishMark]), irishMark);
|
||||
str.SetCharAt(ToLowerCase(str[irishMark + 1]), irishMark + 1);
|
||||
irishMark = uint32_t(-1);
|
||||
irishMarkSrc = uint32_t(-1);
|
||||
break;
|
||||
case 3:
|
||||
// lowercase one prefix letter, and delete following hyphen
|
||||
// (which must be the immediately-preceding char)
|
||||
NS_ASSERTION(
|
||||
str.Length() >= 2 && irishMark == str.Length() - 2,
|
||||
"bad irishMark!");
|
||||
MOZ_ASSERT(
|
||||
irishMark != uint32_t(-1) && irishMarkSrc != uint32_t(-1),
|
||||
"failed to set irishMarks");
|
||||
str.Replace(irishMark, 2, ToLowerCase(str[irishMark]));
|
||||
aDeletedCharsArray[irishMarkSrc + 1] = true;
|
||||
// Remove the trailing entries (corresponding to the deleted
|
||||
// hyphen) from the auxiliary arrays.
|
||||
aCharsToMergeArray.SetLength(aCharsToMergeArray.Length() - 1);
|
||||
if (auxiliaryOutputArrays) {
|
||||
aStyleArray->SetLength(aStyleArray->Length() - 1);
|
||||
aCanBreakBeforeArray->SetLength(
|
||||
aCanBreakBeforeArray->Length() - 1);
|
||||
inhibitBreakBefore = true;
|
||||
}
|
||||
mergeNeeded = true;
|
||||
irishMark = uint32_t(-1);
|
||||
irishMarkSrc = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
// ch has been set to the uppercase for current char;
|
||||
// No need to check for SpecialUpper here as none of the
|
||||
// characters that could trigger an Irish casing action have
|
||||
// special mappings.
|
||||
break;
|
||||
}
|
||||
// If we didn't have any special action to perform, fall through
|
||||
// to check for special uppercase (ß)
|
||||
}
|
||||
|
||||
mcm = mozilla::unicode::SpecialUpper(ch);
|
||||
if (mcm) {
|
||||
int j = 0;
|
||||
while (j < 2 && mcm->mMappedChars[j + 1]) {
|
||||
aConvertedString.Append(mcm->mMappedChars[j]);
|
||||
++extraChars;
|
||||
++j;
|
||||
}
|
||||
ch = mcm->mMappedChars[j];
|
||||
break;
|
||||
}
|
||||
|
||||
// Bug 1476304: we exclude Georgian letters U+10D0..10FF because of
|
||||
// lack of widespread font support for the corresponding Mtavruli
|
||||
// characters at this time (July 2018).
|
||||
// This condition is to be removed once the major platforms ship with
|
||||
// fonts that support U+1C90..1CBF.
|
||||
if (ch < 0x10D0 || ch > 0x10FF) {
|
||||
ch = ToUpperCase(ch);
|
||||
}
|
||||
break;
|
||||
|
||||
case StyleTextTransformCase::Capitalize:
|
||||
if (aTextRun) {
|
||||
if (capitalizeDutchIJ && ch == 'j') {
|
||||
ch = 'J';
|
||||
capitalizeDutchIJ = false;
|
||||
break;
|
||||
}
|
||||
capitalizeDutchIJ = false;
|
||||
if (aOffsetInTextRun < aTextRun->mCapitalize.Length() &&
|
||||
aTextRun->mCapitalize[aOffsetInTextRun]) {
|
||||
if (languageSpecificCasing == eLSCB_Turkish && ch == 'i') {
|
||||
ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
|
||||
break;
|
||||
}
|
||||
if (languageSpecificCasing == eLSCB_Dutch && ch == 'i') {
|
||||
ch = 'I';
|
||||
capitalizeDutchIJ = true;
|
||||
break;
|
||||
}
|
||||
if (languageSpecificCasing == eLSCB_Lithuanian) {
|
||||
/*
|
||||
* # Remove DOT ABOVE after "i" with upper or titlecase
|
||||
*
|
||||
* 0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE
|
||||
*/
|
||||
if (ch == 'i' || ch == 'j' || ch == 0x012F) {
|
||||
seenSoftDotted = true;
|
||||
ch = ToTitleCase(ch);
|
||||
break;
|
||||
}
|
||||
if (seenSoftDotted) {
|
||||
seenSoftDotted = false;
|
||||
if (ch == 0x0307) {
|
||||
ch = uint32_t(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mcm = mozilla::unicode::SpecialTitle(ch);
|
||||
if (mcm) {
|
||||
int j = 0;
|
||||
while (j < 2 && mcm->mMappedChars[j + 1]) {
|
||||
aConvertedString.Append(mcm->mMappedChars[j]);
|
||||
++extraChars;
|
||||
++j;
|
||||
mcm = mozilla::unicode::SpecialTitle(ch);
|
||||
if (mcm) {
|
||||
int j = 0;
|
||||
while (j < 2 && mcm->mMappedChars[j + 1]) {
|
||||
aConvertedString.Append(mcm->mMappedChars[j]);
|
||||
++extraChars;
|
||||
++j;
|
||||
}
|
||||
ch = mcm->mMappedChars[j];
|
||||
break;
|
||||
}
|
||||
ch = mcm->mMappedChars[j];
|
||||
break;
|
||||
}
|
||||
|
||||
ch = ToTitleCase(ch);
|
||||
ch = ToTitleCase(ch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("all cases should be handled");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!aCaseTransformsOnly) {
|
||||
if (!forceNonFullWidth &&
|
||||
(style.other_ & StyleTextTransformOther_FULL_WIDTH)) {
|
||||
ch = mozilla::unicode::GetFullWidth(ch);
|
||||
}
|
||||
|
||||
if (style.other_ & StyleTextTransformOther_FULL_SIZE_KANA) {
|
||||
// clang-format off
|
||||
static const uint16_t kSmallKanas[] = {
|
||||
// ぁ ぃ ぅ ぇ ぉ っ ゃ ゅ ょ
|
||||
0x3041, 0x3043, 0x3045, 0x3047, 0x3049, 0x3063, 0x3083, 0x3085, 0x3087,
|
||||
// ゎ ゕ ゖ
|
||||
0x308E, 0x3095, 0x3096,
|
||||
// ァ ィ ゥ ェ ォ ッ ャ ュ ョ
|
||||
0x30A1, 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7,
|
||||
// ヮ ヵ ヶ ㇰ ㇱ ㇲ ㇳ ㇴ ㇵ
|
||||
0x30EE, 0x30F5, 0x30F6, 0x31F0, 0x31F1, 0x31F2, 0x31F3, 0x31F4, 0x31F5,
|
||||
// ㇶ ㇷ ㇸ ㇹ ㇺ ㇻ ㇼ ㇽ ㇾ
|
||||
0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB, 0x31FC, 0x31FD, 0x31FE,
|
||||
// ㇿ
|
||||
0x31FF,
|
||||
// ァ ィ ゥ ェ ォ ャ ュ ョ ッ
|
||||
0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F};
|
||||
static const uint16_t kFullSizeKanas[] = {
|
||||
// あ い う え お つ や ゆ よ
|
||||
0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x3064, 0x3084, 0x3086, 0x3088,
|
||||
// わ か け
|
||||
0x308F, 0x304B, 0x3051,
|
||||
// ア イ ウ エ オ ツ ヤ ユ ヨ
|
||||
0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30C4, 0x30E4, 0x30E6, 0x30E8,
|
||||
// ワ カ ケ ク シ ス ト ヌ ハ
|
||||
0x30EF, 0x30AB, 0x30B1, 0x30AF, 0x30B7, 0x30B9, 0x30C8, 0x30CC, 0x30CF,
|
||||
// ヒ フ ヘ ホ ム ラ リ ル レ
|
||||
0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30E0, 0x30E9, 0x30EA, 0x30EB, 0x30EC,
|
||||
// ロ
|
||||
0x30ED,
|
||||
// ア イ ウ エ オ ヤ ユ ヨ ツ
|
||||
0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF94, 0xFF95, 0xFF96, 0xFF82};
|
||||
// clang-format on
|
||||
|
||||
size_t index;
|
||||
const uint16_t len = MOZ_ARRAY_LENGTH(kSmallKanas);
|
||||
if (mozilla::BinarySearch(kSmallKanas, 0, len, ch, &index)) {
|
||||
ch = kFullSizeKanas[index];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("all cases should be handled");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!aCaseTransformsOnly) {
|
||||
if (!forceNonFullWidth &&
|
||||
(style.other_ & StyleTextTransformOther_FULL_WIDTH)) {
|
||||
ch = mozilla::unicode::GetFullWidth(ch);
|
||||
}
|
||||
|
||||
if (style.other_ & StyleTextTransformOther_FULL_SIZE_KANA) {
|
||||
// clang-format off
|
||||
static const uint16_t kSmallKanas[] = {
|
||||
// ぁ ぃ ぅ ぇ ぉ っ ゃ ゅ ょ
|
||||
0x3041, 0x3043, 0x3045, 0x3047, 0x3049, 0x3063, 0x3083, 0x3085, 0x3087,
|
||||
// ゎ ゕ ゖ
|
||||
0x308E, 0x3095, 0x3096,
|
||||
// ァ ィ ゥ ェ ォ ッ ャ ュ ョ
|
||||
0x30A1, 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7,
|
||||
// ヮ ヵ ヶ ㇰ ㇱ ㇲ ㇳ ㇴ ㇵ
|
||||
0x30EE, 0x30F5, 0x30F6, 0x31F0, 0x31F1, 0x31F2, 0x31F3, 0x31F4, 0x31F5,
|
||||
// ㇶ ㇷ ㇸ ㇹ ㇺ ㇻ ㇼ ㇽ ㇾ
|
||||
0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB, 0x31FC, 0x31FD, 0x31FE,
|
||||
// ㇿ
|
||||
0x31FF,
|
||||
// ァ ィ ゥ ェ ォ ャ ュ ョ ッ
|
||||
0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F};
|
||||
static const uint16_t kFullSizeKanas[] = {
|
||||
// あ い う え お つ や ゆ よ
|
||||
0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x3064, 0x3084, 0x3086, 0x3088,
|
||||
// わ か け
|
||||
0x308F, 0x304B, 0x3051,
|
||||
// ア イ ウ エ オ ツ ヤ ユ ヨ
|
||||
0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30C4, 0x30E4, 0x30E6, 0x30E8,
|
||||
// ワ カ ケ ク シ ス ト ヌ ハ
|
||||
0x30EF, 0x30AB, 0x30B1, 0x30AF, 0x30B7, 0x30B9, 0x30C8, 0x30CC, 0x30CF,
|
||||
// ヒ フ ヘ ホ ム ラ リ ル レ
|
||||
0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30E0, 0x30E9, 0x30EA, 0x30EB, 0x30EC,
|
||||
// ロ
|
||||
0x30ED,
|
||||
// ア イ ウ エ オ ヤ ユ ヨ ツ
|
||||
0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF94, 0xFF95, 0xFF96, 0xFF82};
|
||||
// clang-format on
|
||||
|
||||
size_t index;
|
||||
const uint16_t len = MOZ_ARRAY_LENGTH(kSmallKanas);
|
||||
if (mozilla::BinarySearch(kSmallKanas, 0, len, ch, &index)) {
|
||||
ch = kFullSizeKanas[index];
|
||||
}
|
||||
if (forceNonFullWidth) {
|
||||
ch = mozilla::unicode::GetFullWidthInverse(ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (forceNonFullWidth) {
|
||||
ch = mozilla::unicode::GetFullWidthInverse(ch);
|
||||
}
|
||||
|
||||
if (ch == uint32_t(-1)) {
|
||||
aDeletedCharsArray.AppendElement(true);
|
||||
mergeNeeded = true;
|
||||
@ -767,16 +784,21 @@ bool nsCaseTransformTextRunFactory::TransformString(
|
||||
}
|
||||
|
||||
if (IS_IN_BMP(ch)) {
|
||||
aConvertedString.Append(ch);
|
||||
aConvertedString.Append(maskPassword ? kPasswordMask : ch);
|
||||
} else {
|
||||
aConvertedString.Append(H_SURROGATE(ch));
|
||||
aConvertedString.Append(L_SURROGATE(ch));
|
||||
i++;
|
||||
aOffsetInTextRun++;
|
||||
aDeletedCharsArray.AppendElement(
|
||||
true); // not exactly deleted, but the
|
||||
// trailing surrogate is skipped
|
||||
if (maskPassword) {
|
||||
aConvertedString.Append(kPasswordMask);
|
||||
// TODO: We should show a password mask for a surrogate pair later.
|
||||
aConvertedString.Append(kPasswordMask);
|
||||
} else {
|
||||
aConvertedString.Append(H_SURROGATE(ch));
|
||||
aConvertedString.Append(L_SURROGATE(ch));
|
||||
}
|
||||
++extraChars;
|
||||
++i;
|
||||
++aOffsetInTextRun;
|
||||
// Skip the trailing surrogate.
|
||||
aDeletedCharsArray.AppendElement(true);
|
||||
}
|
||||
|
||||
while (extraChars-- > 0) {
|
||||
|
@ -38,6 +38,7 @@ struct nsTransformedCharStyle final {
|
||||
uint8_t mMathVariant;
|
||||
bool mExplicitLanguage;
|
||||
bool mForceNonFullWidth = false;
|
||||
bool mMaskPassword = false;
|
||||
|
||||
private:
|
||||
~nsTransformedCharStyle() {}
|
||||
@ -105,6 +106,12 @@ class nsCaseTransformTextRunFactory : public nsTransformingTextRunFactory {
|
||||
// these are ignored.
|
||||
// If aCaseTransformsOnly is true, then only the upper/lower/capitalize
|
||||
// transformations are performed; full-width and full-size-kana are ignored.
|
||||
// If `aTextRun` is not nullptr and characters which are styled with setting
|
||||
// `nsTransformedCharStyle::mMaskPassword` to true, they are replaced with
|
||||
// password mask characters and are not transformed (i.e., won't be added
|
||||
// or merged for the specified transform). However, unmasked characters
|
||||
// whose `nsTransformedCharStyle::mMaskPassword` is set to false are
|
||||
// transformed normally.
|
||||
static bool TransformString(
|
||||
const nsAString& aString, nsString& aConvertedString, bool aAllUppercase,
|
||||
bool aCaseTransformsOnly, const nsAtom* aLanguage,
|
||||
|
@ -173,6 +173,17 @@ input > .preview-div {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
input[type=password] > .anonymous-div,
|
||||
input[type=password] > .preview-div {
|
||||
/*
|
||||
* In password fields, any character should be put same direction. Otherwise,
|
||||
* caret position at typing tells everybody whether the character is an RTL
|
||||
* or an LTR character. Unfortunately, this makes odd rendering when bidi
|
||||
* text is unmasked.
|
||||
*/
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
textarea > .anonymous-div {
|
||||
scroll-behavior: inherit;
|
||||
overscroll-behavior: inherit;
|
||||
|
Loading…
Reference in New Issue
Block a user