mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
8a0a002cf2
- Bug 1148708: Add missing 'override' annotations in DocAccessibleChild.h. rs=ehsan (d606358545)
- Bug 1210408 - make nsMaiInterfaceAction work with proxies, r=tbsaunde (f7c819c6ae)
- Bug 1210407 - teach nsMaiInterfaceTable to use proxies, r=tbsaunde (4ca4f10b5f)
- bug 1185157 make sure we don't send an event to a destroyed ipc document r=billm (23acf53f75)
- bug 1214864 - make SetCarretOffset() async r=davidb (e3079e9b2d)
- missing of Bug 1139972 - IPC Proxy for charAt, r=tbsaunde (e9593ed752)
- bug 1191598 - Pass MOZ_CURRENT_PROJECT in environment when running post-build automation steps for universal mac builds. r=gps (fc342c6ced)
- Bug 1164596 - Add mach android-emulator command; r=ahal (afeb9b27d1)
- Bug 1223149 - Add basic usage documentation for mach build; r=glandium (bfb802d175)
- Bug 1182301 - Improve 'mach build' notifications. r=gps (2c65a122d1)
- Bug 1184696 - Add clobber targets to |mach clobber|; Ability to clobber compiled python files, r=gps (35d8be292e)
- Bug 1117958 - Allow any debugging options to the run or gtest mach subcommands to automatically enable debugging. r=gps (32f986af4b)
- Bug 1180081 - Properly rebuild gtest/libxul before running gtests. r=gps (80db9a3d49)
- Bug 1171647, part 1 - Define a new function to convert the mode to a string. r=njn (61ad16f5ba)
- Bug 1171647, part 2 - Remove redundant assertion for dark matter mode. r=njn (b5ac9519f3)
- Bug 1058178, part 1 - Implement DMD heap scanning mode. r=njn (60e1079536)
- Bug 1058178, part 2 - Implement address clamping analysis for DMD scan logs. r=njn (45c0326b93)
- Bug 1102388 - Fix DMD static constructor ordering dependency. r=mccr8 (59b87897a1)
- Bug 1128705 - Don't redefine PAGE_SIZE in DMD if it's already defined. r=erahm (49216348ee)
- Bug 1179042 - Add a script for analyzing memory blocks using a heap scan DMD log. r=njn DONTBUILD (1c08d2d66e)
- Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat (1c999d139b)
- Bug 1158772 - fix non-idiomatic memset call in nsDeque.cpp; r=erahm (de6b555245)
- Bug 1199400 - Part 1: Use CheckedInt when growing nsDeque capacity. r=froydnj (dfdf6814a3)
- Bug 1199400 - Part 0: Remove unused nsDequeIterator. r=froydnj (38d69d7f47)
- Bug 1199400 - Part 2: Add tests for possible nsDeque corner cases. r=froydnj (931baff195)
- Bug 1201997 - Part 1 - Converted compiled test to gtest for nsDeque class. Added tests to test untested methods. r=froydn (e893916651)
- Bug 1201997 - Part 2 - Removing unused methods from the nsDeque class. r=froydn (41595a90ac)
- Bug 1201997 - Part 3 - Make internally used methods private. r=froydn (8cd3afd96f)
- Bug 1201997 - Part 4 - Change size and offset variables to size_t.r=froydn (73eabc8d60)
- Bug 1215140 P1 Add an nsIConsoleReportCollector interface to support navigation channel logging. r=bz (8a41535e2b)
- Bug 1215140 P2 Make HttpBaseChannel implement nsIConsoleReportCollector. r=bz (75fca301f2)
- Bug 1197679 - If nsUnknownDecoder is involved in e10s DivertToParent can break. r=jduell (5d94a12504)
- Bug 1178991 - smartptr for http converter r=hurley (8e7fbc8443)
- bug 366559 - patch 2, fix nsHTTPCompressConv indentation r=bagder (ba762da587)
- bug 366559 - patch 3, fix nsHTTPCompressConv bracing style r=bagder (54195ab451)
- bits of bug 366559 - patch 7, content-encoding brotli for http (f0b4051022)
- Bug 1205112 - Make PushEvent.data nullable. r=mt,smaug (775db32856)
- Bug 1193414 - SharedWorkers thread should be kept alive also when the SharedWorker object is CCed, r=khuey (b77ea8125c)
- Bug 1206520: Add about:config prefs to enable throwing on asm.js validation failures; r=bz (c42126665d)
- Bug 1193414 - Telemetry for SharedWorker spawning. r=bkelly (77984b7bcc)
- Bug 1205676 - Enable WPT service-worker/unregister-then-register-new-script.https.html in e10s, r=nsm (ec24939cf6)
- Bug 1193133 - Throw when calling postMessage from a Service Worker dom object with no global. r=bkelly (526dcacfab)
- Bug 1181871 P1 Only enforce Cache Context shared data destruction on target thread after init. r=ehsan (cdbf3ed3a8)
- Bug 1181871 P2 Fix ServiceWorkerManager usage of stack-based ErrorResult. r=ehsan (c449195d90)
- minor cleanup and missing bit of 1198230 (02f459db05)
- Bug 1143717 - Implement the ServiceWorkerMessageEvent interface. r=baku (027b3465f2)
- fix misspatch (708eee4e84)
- Bug 1188545 - Disentangle service workers from shared workers and refactor event dispatching code into a separate class. r=nsm,mrbkap (fb5b5341c9)
- Bug 1205228 - Change PackagedAppVerifier to notify the verification result asynchronously. r=valentin. (9edda0fa00)
- Bug 1178518 - Packaged App Utils. r=valentin (f60f3b7a93)
- Bug 1213150 - Part 1: Add a nsContentUtils::IsNonSubresourceRequest helper; r=jdm (b509cc3cc9)
- Bug 1213150 - Part 2: Rework ShouldPrepareForIntercept() in terms of subresource requests; r=jdm (2e92fe8780)
- Bug 1213150 - Part 3: Remove nsIInterceptedChannel.isNavigation; r=jdm (becf1cc12f)
- Bug 1213150 follow-up: fix build bustage (8d73d6ca73)
- Bug 1198394 - Part 1: Allow interception of HSTS upgraded connections in non-e10s mode; r=mcmanus (f504c5be08)
- Bug 1198394 - Part 2: Add a test for interception of HSTS upgraded connections; r=jdm (054e984eef)
- Bug 1187011 - Don't allow response body with null body status. r=bkelly (b1860741d1)
- missing bit of 1140788 (29d319712e)
- Bug 1213436 - Reject core dumps with node IDs that don't fit in an IEEE 754 double; r=sfink (3c1f6fdda0)
- Bug 1211006 - Add Debugger.Source.prototype.canonicalId; r=ejpbruel (eef7b79fce)
- Bug 1199218 - Implement JS::ubi::Node::size for js::LazyScript referents; r=sfink (098a48d240)
- Bug 1220031 - Add JS::ubi::Node::scriptFilename; r=sfink (6b824ae680)
- Bug 1143575. Remove unused MediaQueue::Empty. r=cpearce (de737f3433)
- Bug 1209933 - Make sure all parent runtime pointers are the topmost parent, r=billm. (fe824d967d)
- Bug 1197012 - Fix ThrowTypeError in Notification. r=mccr8 (0b1a097526)
- Bug 1197893 - Check the number of arguments for ThrowTypeError() and ThrowRangeError() at compile time. r=peterv (d98c7d78a0)
- Bug 1142083 - Add test for IDN Unicode domain redirect. r=mcmanus (0c8961fe17)
- Bug 1187159 - Add mochitest for loading packaged apps (iframe+fetch+mozapp) r=jduell (ce90ea561b)
- Bug 1186290 - Notify TabParent to switch process when loading a signed package. r=honzab, r=kanru. (c58a14554a)
- fix (15e2df75eb)
- Bug 1206124 P1 Fix "same-origin" CORS credentials in FetchDriver. r=ehsan (fae1bb6ab3)
- Bug 1206124 P2 Test fetch() with credentials and redirects. r=ehsan (ffc6254112)
- Bug 1211751: Remove nsIChannelEventSink-forwarding from EventSource and FetchDriver. It's never needed. r=smaug (adafe5737a)
- Bug 1212433 Fail fetch() calls that require preflight and also redirect. r=sicking a=abillings (c0d6742b9e)
- Bug 1193128 - Fix base64 decoding when fetching data URIs. r=baku (80bafa291a)
- Bug 1195167 part 1: Let necko handle all protocols. r=bkelly (bb932b0ada)
- Bug 1195167 part 2: Remove redundant aCORSFlag argument and instead use mCORSFlagEverSet. r=bkelly (beadafcad0)
- Bug 1195167 part 3: Remove more scheme-specific handling from FetchDriver. r=bkelly (d00b38db9e)
- Bug 1195167 part 4: Remove FetchDriver::BasicFetch since it is empty. r=bkelly (c5ed097267)
- Bug 1210413 P2 Test CORS credentials on cross-origin redirects. r=sicking a=dveditz (b4eeb8aac0)
- Bug 1210413 P1 Propagate new channel load flags from child to parent on redirect. r=jduell a=dveditz (8b329af4fa)
- Bug 1195167 part 5: Make FetchDriver use AsyncOpen2. r=bkelly (cc217c4cc1)
- Bug 1195167 part 6: Some code simplification since necko handles fetch recursion. r=bkelly (f3b6da2262)
- Bug 1195167: Followup to fix test which I forgot to change (81e7439a2e)
- Bug 1215746: Remove RequestMode::Cors_with_forced_preflight. r=bkelly (0336e812b6)
- Bug 1211000: Move CORS preflight logic from nsCORSListenerProxy to nsCORSPreflightListener. r=ehsan (bf2f71cf22)
- missing bit of Bug 1211443 - Drop scheduled update if decoder initialization isn't done yet. r=jya (f6bc074e33)
- Bug 1182571: Fix nsILoadInfo->GetContentPolicyType API to be less ambigious. Audit and fix all users of it. r=ckerschb (5af6fa7442)
- fix (e40c8e7625)
- Bug 1173811 - Part 1: Propagate the response URL to intercepted channels when necessary (non-e10s). r=mayhemer,bkelly (26f4f13c28)
- Bug 1173811 - Part 2: Propagate the response URL to intercepted channels when necessary (e10s). r=mayhemer,bkelly (a603fe1df2)
- Bug 1154309 - Add New Resource Timing Fields r=bz,hurley (1d14eb6bef)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (a5d18bb637)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (fb07d2c8aa)
- Bug 1212904 P1 Add a LoadTainting enumeration. r=jduell (a1db8a3e99)
- Bug 1212904 P2 Add LoadTainting information to nsILoadInfo. r=jduell (2482e5e334)
- Bug 1221151 - use [infallible] in nsILoadInfo.idl instead of manual %{C++ blocks; r=jduell (aae73129b6)
- Bug 1045891 - CSP 2 child-src implementation r=ckerschb (792920aeb9)
- Bug 1219931 - CSP: Don't allow removing a policy (r=sicking) (9daaab4186)
- Bug 1208661 - Dump client-side layer textures. r=BenWa (1f2d17d515)
685 lines
25 KiB
C++
685 lines
25 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsTextRunTransformations.h"
|
|
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "mozilla/Move.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsUnicodeProperties.h"
|
|
#include "nsSpecialCasingData.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "nsTextFrameUtils.h"
|
|
#include "nsIPersistentProperties2.h"
|
|
#include "GreekCasing.h"
|
|
#include "IrishCasing.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
// Unicode characters needing special casing treatment in tr/az languages
|
|
#define LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE 0x0130
|
|
#define LATIN_SMALL_LETTER_DOTLESS_I 0x0131
|
|
|
|
// Greek sigma needs custom handling for the lowercase transform; for details
|
|
// see comments under "case NS_STYLE_TEXT_TRANSFORM_LOWERCASE" within
|
|
// nsCaseTransformTextRunFactory::RebuildTextRun(), and bug 740120.
|
|
#define GREEK_CAPITAL_LETTER_SIGMA 0x03A3
|
|
#define GREEK_SMALL_LETTER_FINAL_SIGMA 0x03C2
|
|
#define GREEK_SMALL_LETTER_SIGMA 0x03C3
|
|
|
|
nsTransformedTextRun *
|
|
nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams,
|
|
nsTransformingTextRunFactory* aFactory,
|
|
gfxFontGroup* aFontGroup,
|
|
const char16_t* aString, uint32_t aLength,
|
|
const uint32_t aFlags,
|
|
nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
|
|
bool aOwnsFactory)
|
|
{
|
|
NS_ASSERTION(!(aFlags & gfxTextRunFactory::TEXT_IS_8BIT),
|
|
"didn't expect text to be marked as 8-bit here");
|
|
|
|
void *storage = AllocateStorageForTextRun(sizeof(nsTransformedTextRun), aLength);
|
|
if (!storage) {
|
|
return nullptr;
|
|
}
|
|
|
|
return new (storage) nsTransformedTextRun(aParams, aFactory, aFontGroup,
|
|
aString, aLength,
|
|
aFlags, Move(aStyles),
|
|
aOwnsFactory);
|
|
}
|
|
|
|
void
|
|
nsTransformedTextRun::SetCapitalization(uint32_t aStart, uint32_t aLength,
|
|
bool* aCapitalization,
|
|
gfxContext* aRefContext)
|
|
{
|
|
if (mCapitalize.IsEmpty()) {
|
|
if (!mCapitalize.AppendElements(GetLength()))
|
|
return;
|
|
memset(mCapitalize.Elements(), 0, GetLength()*sizeof(bool));
|
|
}
|
|
memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(bool));
|
|
mNeedsRebuild = true;
|
|
}
|
|
|
|
bool
|
|
nsTransformedTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
|
|
uint8_t* aBreakBefore,
|
|
gfxContext* aRefContext)
|
|
{
|
|
bool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength,
|
|
aBreakBefore, aRefContext);
|
|
if (changed) {
|
|
mNeedsRebuild = true;
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
size_t
|
|
nsTransformedTextRun::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
|
{
|
|
size_t total = gfxTextRun::SizeOfExcludingThis(aMallocSizeOf);
|
|
total += mStyles.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
|
total += mCapitalize.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
|
if (mOwnsFactory) {
|
|
total += aMallocSizeOf(mFactory);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
size_t
|
|
nsTransformedTextRun::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
|
{
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
}
|
|
|
|
nsTransformedTextRun*
|
|
nsTransformingTextRunFactory::MakeTextRun(const char16_t* aString, uint32_t aLength,
|
|
const gfxTextRunFactory::Parameters* aParams,
|
|
gfxFontGroup* aFontGroup, uint32_t aFlags,
|
|
nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
|
|
bool aOwnsFactory)
|
|
{
|
|
return nsTransformedTextRun::Create(aParams, this, aFontGroup,
|
|
aString, aLength, aFlags, Move(aStyles),
|
|
aOwnsFactory);
|
|
}
|
|
|
|
nsTransformedTextRun*
|
|
nsTransformingTextRunFactory::MakeTextRun(const uint8_t* aString, uint32_t aLength,
|
|
const gfxTextRunFactory::Parameters* aParams,
|
|
gfxFontGroup* aFontGroup, uint32_t aFlags,
|
|
nsTArray<RefPtr<nsTransformedCharStyle>>&& aStyles,
|
|
bool aOwnsFactory)
|
|
{
|
|
// We'll only have a Unicode code path to minimize the amount of code needed
|
|
// for these rarely used features
|
|
NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString), aLength);
|
|
return MakeTextRun(unicodeString.get(), aLength, aParams, aFontGroup,
|
|
aFlags & ~(gfxFontGroup::TEXT_IS_PERSISTENT | gfxFontGroup::TEXT_IS_8BIT),
|
|
Move(aStyles), aOwnsFactory);
|
|
}
|
|
|
|
void
|
|
MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc,
|
|
const bool* aCharsToMerge, const bool* aDeletedChars)
|
|
{
|
|
aDest->ResetGlyphRuns();
|
|
|
|
gfxTextRun::GlyphRunIterator iter(aSrc, 0, aSrc->GetLength());
|
|
uint32_t offset = 0;
|
|
nsAutoTArray<gfxTextRun::DetailedGlyph,2> glyphs;
|
|
while (iter.NextRun()) {
|
|
gfxTextRun::GlyphRun* run = iter.GetGlyphRun();
|
|
nsresult rv = aDest->AddGlyphRun(run->mFont, run->mMatchType,
|
|
offset, false, run->mOrientation);
|
|
if (NS_FAILED(rv))
|
|
return;
|
|
|
|
bool anyMissing = false;
|
|
uint32_t mergeRunStart = iter.GetStringStart();
|
|
const gfxTextRun::CompressedGlyph *srcGlyphs = aSrc->GetCharacterGlyphs();
|
|
gfxTextRun::CompressedGlyph mergedGlyph = srcGlyphs[mergeRunStart];
|
|
uint32_t stringEnd = iter.GetStringEnd();
|
|
for (uint32_t k = iter.GetStringStart(); k < stringEnd; ++k) {
|
|
const gfxTextRun::CompressedGlyph g = srcGlyphs[k];
|
|
if (g.IsSimpleGlyph()) {
|
|
if (!anyMissing) {
|
|
gfxTextRun::DetailedGlyph details;
|
|
details.mGlyphID = g.GetSimpleGlyph();
|
|
details.mAdvance = g.GetSimpleAdvance();
|
|
details.mXOffset = 0;
|
|
details.mYOffset = 0;
|
|
glyphs.AppendElement(details);
|
|
}
|
|
} else {
|
|
if (g.IsMissing()) {
|
|
anyMissing = true;
|
|
glyphs.Clear();
|
|
}
|
|
if (g.GetGlyphCount() > 0) {
|
|
glyphs.AppendElements(aSrc->GetDetailedGlyphs(k), g.GetGlyphCount());
|
|
}
|
|
}
|
|
|
|
if (k + 1 < iter.GetStringEnd() && aCharsToMerge[k + 1]) {
|
|
// next char is supposed to merge with current, so loop without
|
|
// writing current merged glyph to the destination
|
|
continue;
|
|
}
|
|
|
|
// If the start of the merge run is actually a character that should
|
|
// have been merged with the previous character (this can happen
|
|
// if there's a font change in the middle of a case-mapped character,
|
|
// that decomposed into a sequence of base+diacritics, for example),
|
|
// just discard the entire merge run. See comment at start of this
|
|
// function.
|
|
NS_WARN_IF_FALSE(!aCharsToMerge[mergeRunStart],
|
|
"unable to merge across a glyph run boundary, "
|
|
"glyph(s) discarded");
|
|
if (!aCharsToMerge[mergeRunStart]) {
|
|
if (anyMissing) {
|
|
mergedGlyph.SetMissing(glyphs.Length());
|
|
} else {
|
|
mergedGlyph.SetComplex(mergedGlyph.IsClusterStart(),
|
|
mergedGlyph.IsLigatureGroupStart(),
|
|
glyphs.Length());
|
|
}
|
|
aDest->SetGlyphs(offset, mergedGlyph, glyphs.Elements());
|
|
++offset;
|
|
|
|
while (offset < aDest->GetLength() && aDeletedChars[offset]) {
|
|
aDest->SetGlyphs(offset++, gfxTextRun::CompressedGlyph(), nullptr);
|
|
}
|
|
}
|
|
|
|
glyphs.Clear();
|
|
anyMissing = false;
|
|
mergeRunStart = k + 1;
|
|
if (mergeRunStart < stringEnd) {
|
|
mergedGlyph = srcGlyphs[mergeRunStart];
|
|
}
|
|
}
|
|
NS_ASSERTION(glyphs.Length() == 0,
|
|
"Leftover glyphs, don't request merging of the last character with its next!");
|
|
}
|
|
NS_ASSERTION(offset == aDest->GetLength(), "Bad offset calculations");
|
|
}
|
|
|
|
gfxTextRunFactory::Parameters
|
|
GetParametersForInner(nsTransformedTextRun* aTextRun, uint32_t* aFlags,
|
|
gfxContext* aRefContext)
|
|
{
|
|
gfxTextRunFactory::Parameters params =
|
|
{ aRefContext, nullptr, nullptr,
|
|
nullptr, 0, aTextRun->GetAppUnitsPerDevUnit()
|
|
};
|
|
*aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT;
|
|
return params;
|
|
}
|
|
|
|
// Some languages have special casing conventions that differ from the
|
|
// default Unicode mappings.
|
|
// The enum values here are named for well-known exemplar languages that
|
|
// exhibit the behavior in question; multiple lang tags may map to the
|
|
// same setting here, if the behavior is shared by other languages.
|
|
enum LanguageSpecificCasingBehavior {
|
|
eLSCB_None, // default non-lang-specific behavior
|
|
eLSCB_Dutch, // treat "ij" digraph as a unit for capitalization
|
|
eLSCB_Greek, // strip accent when uppercasing Greek vowels
|
|
eLSCB_Irish, // keep prefix letters as lowercase when uppercasing Irish
|
|
eLSCB_Turkish // preserve dotted/dotless-i distinction in uppercase
|
|
};
|
|
|
|
static LanguageSpecificCasingBehavior
|
|
GetCasingFor(const nsIAtom* aLang)
|
|
{
|
|
if (!aLang) {
|
|
return eLSCB_None;
|
|
}
|
|
if (aLang == nsGkAtoms::tr ||
|
|
aLang == nsGkAtoms::az ||
|
|
aLang == nsGkAtoms::ba ||
|
|
aLang == nsGkAtoms::crh ||
|
|
aLang == nsGkAtoms::tt) {
|
|
return eLSCB_Turkish;
|
|
}
|
|
if (aLang == nsGkAtoms::nl) {
|
|
return eLSCB_Dutch;
|
|
}
|
|
if (aLang == nsGkAtoms::el) {
|
|
return eLSCB_Greek;
|
|
}
|
|
if (aLang == nsGkAtoms::ga) {
|
|
return eLSCB_Irish;
|
|
}
|
|
|
|
// Is there a region subtag we should ignore?
|
|
nsAtomString langStr(const_cast<nsIAtom*>(aLang));
|
|
int index = langStr.FindChar('-');
|
|
if (index > 0) {
|
|
langStr.Truncate(index);
|
|
nsCOMPtr<nsIAtom> truncatedLang = do_GetAtom(langStr);
|
|
return GetCasingFor(truncatedLang);
|
|
}
|
|
|
|
return eLSCB_None;
|
|
}
|
|
|
|
bool
|
|
nsCaseTransformTextRunFactory::TransformString(
|
|
const nsAString& aString,
|
|
nsString& aConvertedString,
|
|
bool aAllUppercase,
|
|
const nsIAtom* aLanguage,
|
|
nsTArray<bool>& aCharsToMergeArray,
|
|
nsTArray<bool>& aDeletedCharsArray,
|
|
nsTransformedTextRun* aTextRun,
|
|
nsTArray<uint8_t>* aCanBreakBeforeArray,
|
|
nsTArray<RefPtr<nsTransformedCharStyle>>* aStyleArray)
|
|
{
|
|
NS_PRECONDITION(!aTextRun || (aCanBreakBeforeArray && aStyleArray),
|
|
"either none or all three optional parameters required");
|
|
|
|
uint32_t length = aString.Length();
|
|
const char16_t* str = aString.BeginReading();
|
|
|
|
bool mergeNeeded = false;
|
|
|
|
bool capitalizeDutchIJ = false;
|
|
bool prevIsLetter = false;
|
|
bool ntPrefix = false; // true immediately after a word-initial 'n' or 't'
|
|
// when doing Irish lowercasing
|
|
uint32_t sigmaIndex = uint32_t(-1);
|
|
nsIUGenCategory::nsUGenCategory cat;
|
|
|
|
uint8_t style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE : 0;
|
|
const nsIAtom* lang = aLanguage;
|
|
|
|
LanguageSpecificCasingBehavior languageSpecificCasing = GetCasingFor(lang);
|
|
mozilla::GreekCasing::State greekState;
|
|
mozilla::IrishCasing::State irishState;
|
|
uint32_t irishMark = uint32_t(-1); // location of possible prefix letter(s)
|
|
// in the output string
|
|
uint32_t irishMarkSrc = uint32_t(-1); // corresponding location in source
|
|
// string (may differ from output due to
|
|
// expansions like eszet -> 'SS')
|
|
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
uint32_t ch = str[i];
|
|
|
|
RefPtr<nsTransformedCharStyle> charStyle;
|
|
if (aTextRun) {
|
|
charStyle = aTextRun->mStyles[i];
|
|
style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE :
|
|
charStyle->mTextTransform;
|
|
|
|
nsIAtom* newLang = charStyle->mExplicitLanguage
|
|
? charStyle->mLanguage : nullptr;
|
|
if (lang != newLang) {
|
|
lang = newLang;
|
|
languageSpecificCasing = GetCasingFor(lang);
|
|
greekState.Reset();
|
|
irishState.Reset();
|
|
irishMark = uint32_t(-1);
|
|
irishMarkSrc = uint32_t(-1);
|
|
}
|
|
}
|
|
|
|
int extraChars = 0;
|
|
const mozilla::unicode::MultiCharMapping *mcm;
|
|
bool inhibitBreakBefore = false; // have we just deleted preceding hyphen?
|
|
|
|
if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 &&
|
|
NS_IS_LOW_SURROGATE(str[i + 1])) {
|
|
ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
|
|
}
|
|
|
|
switch (style) {
|
|
case NS_STYLE_TEXT_TRANSFORM_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;
|
|
}
|
|
}
|
|
|
|
cat = mozilla::unicode::GetGenCategory(ch);
|
|
|
|
if (languageSpecificCasing == eLSCB_Irish &&
|
|
cat == nsIUGenCategory::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;
|
|
}
|
|
|
|
// 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 == nsIUGenCategory::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;
|
|
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 != nsIUGenCategory::kMark) {
|
|
prevIsLetter = (cat == nsIUGenCategory::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 NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
|
|
if (languageSpecificCasing == eLSCB_Turkish && ch == 'i') {
|
|
ch = LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE;
|
|
break;
|
|
}
|
|
|
|
if (languageSpecificCasing == eLSCB_Greek) {
|
|
ch = mozilla::GreekCasing::UpperCase(ch, greekState);
|
|
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)),
|
|
"irishMark and irishMarkSrc initialization out of sync");
|
|
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 (aTextRun) {
|
|
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;
|
|
}
|
|
|
|
ch = ToUpperCase(ch);
|
|
break;
|
|
|
|
case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
|
|
if (aTextRun) {
|
|
if (capitalizeDutchIJ && ch == 'j') {
|
|
ch = 'J';
|
|
capitalizeDutchIJ = false;
|
|
break;
|
|
}
|
|
capitalizeDutchIJ = false;
|
|
if (i < aTextRun->mCapitalize.Length() && aTextRun->mCapitalize[i]) {
|
|
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;
|
|
}
|
|
|
|
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 = ToTitleCase(ch);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NS_STYLE_TEXT_TRANSFORM_FULLWIDTH:
|
|
ch = mozilla::unicode::GetFullWidth(ch);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ch == uint32_t(-1)) {
|
|
aDeletedCharsArray.AppendElement(true);
|
|
mergeNeeded = true;
|
|
} else {
|
|
aDeletedCharsArray.AppendElement(false);
|
|
aCharsToMergeArray.AppendElement(false);
|
|
if (aTextRun) {
|
|
aStyleArray->AppendElement(charStyle);
|
|
aCanBreakBeforeArray->AppendElement(
|
|
inhibitBreakBefore ? gfxShapedText::CompressedGlyph::FLAG_BREAK_TYPE_NONE
|
|
: aTextRun->CanBreakBefore(i));
|
|
}
|
|
|
|
if (IS_IN_BMP(ch)) {
|
|
aConvertedString.Append(ch);
|
|
} else {
|
|
aConvertedString.Append(H_SURROGATE(ch));
|
|
aConvertedString.Append(L_SURROGATE(ch));
|
|
++i;
|
|
aDeletedCharsArray.AppendElement(true); // not exactly deleted, but the
|
|
// trailing surrogate is skipped
|
|
++extraChars;
|
|
}
|
|
|
|
while (extraChars-- > 0) {
|
|
mergeNeeded = true;
|
|
aCharsToMergeArray.AppendElement(true);
|
|
if (aTextRun) {
|
|
aStyleArray->AppendElement(charStyle);
|
|
aCanBreakBeforeArray->AppendElement(
|
|
gfxShapedText::CompressedGlyph::FLAG_BREAK_TYPE_NONE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return mergeNeeded;
|
|
}
|
|
|
|
void
|
|
nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
|
|
gfxContext* aRefContext, gfxMissingFontRecorder *aMFR)
|
|
{
|
|
nsAutoString convertedString;
|
|
nsAutoTArray<bool,50> charsToMergeArray;
|
|
nsAutoTArray<bool,50> deletedCharsArray;
|
|
nsAutoTArray<uint8_t,50> canBreakBeforeArray;
|
|
nsAutoTArray<RefPtr<nsTransformedCharStyle>,50> styleArray;
|
|
|
|
bool mergeNeeded = TransformString(aTextRun->mString,
|
|
convertedString,
|
|
mAllUppercase,
|
|
nullptr,
|
|
charsToMergeArray,
|
|
deletedCharsArray,
|
|
aTextRun,
|
|
&canBreakBeforeArray,
|
|
&styleArray);
|
|
|
|
uint32_t flags;
|
|
gfxTextRunFactory::Parameters innerParams =
|
|
GetParametersForInner(aTextRun, &flags, aRefContext);
|
|
gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
|
|
|
|
nsAutoPtr<nsTransformedTextRun> transformedChild;
|
|
nsAutoPtr<gfxTextRun> cachedChild;
|
|
gfxTextRun* child;
|
|
|
|
if (mInnerTransformingTextRunFactory) {
|
|
transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
|
|
convertedString.BeginReading(), convertedString.Length(),
|
|
&innerParams, fontGroup, flags, Move(styleArray), false);
|
|
child = transformedChild.get();
|
|
} else {
|
|
cachedChild = fontGroup->MakeTextRun(
|
|
convertedString.BeginReading(), convertedString.Length(),
|
|
&innerParams, flags, aMFR);
|
|
child = cachedChild.get();
|
|
}
|
|
if (!child)
|
|
return;
|
|
// Copy potential linebreaks into child so they're preserved
|
|
// (and also child will be shaped appropriately)
|
|
NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
|
|
"Dropped characters or break-before values somewhere!");
|
|
child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
|
|
canBreakBeforeArray.Elements(), aRefContext);
|
|
if (transformedChild) {
|
|
transformedChild->FinishSettingProperties(aRefContext, aMFR);
|
|
}
|
|
|
|
if (mergeNeeded) {
|
|
// Now merge multiple characters into one multi-glyph character as required
|
|
// and deal with skipping deleted accent chars
|
|
NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(),
|
|
"source length mismatch");
|
|
NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(),
|
|
"destination length mismatch");
|
|
MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(),
|
|
deletedCharsArray.Elements());
|
|
} else {
|
|
// No merging to do, so just copy; this produces a more optimized textrun.
|
|
// We can't steal the data because the child may be cached and stealing
|
|
// the data would break the cache.
|
|
aTextRun->ResetGlyphRuns();
|
|
aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0);
|
|
}
|
|
}
|