/* 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/. */
// Sanity-check that the distances are within the expected range // (update if implementation of the distance functions is changed).
MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0);
MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
// weight/style/stretch priority: stretch >> style >> weight // so we multiply the stretch and style values to make them dominate // the result return stretchDist * kStretchFactor + styleDist * kStyleFactor +
weightDist * kWeightFactor;
}
void* Pointer::ToPtr(FontList* aFontList,
size_t aSize) const MOZ_NO_THREAD_SAFETY_ANALYSIS { // On failure, we'll return null; callers need to handle this appropriately // (e.g. via fallback). void* result = nullptr;
if (IsNull()) { return result;
}
// Ensure the list doesn't get replaced out from under us. Font-list rebuild // happens on the main thread, so only non-main-thread callers need to lock // it here. bool isMainThread = NS_IsMainThread(); if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Lock();
}
uint32_t blockIndex = Block();
// If the Pointer refers to a block we have not yet mapped in this process, // we first need to retrieve new block handle(s) from the parent and update // our mBlocks list. auto& blocks = aFontList->mBlocks; if (blockIndex >= blocks.Length()) { if (MOZ_UNLIKELY(XRE_IsParentProcess())) { // Shouldn't happen! A content process tried to pass a bad Pointer? goto cleanup;
} // If we're not on the main thread, we can't do the IPC involved in // UpdateShmBlocks; just let the lookup fail for now. if (!isMainThread) { goto cleanup;
} // UpdateShmBlocks can fail, if the parent has replaced the font list with // a new generation. In that case we just return null, and whatever font // the content process was trying to use will appear unusable for now. It's // about to receive a notification of the new font list anyhow, at which // point it will flush its caches and reflow everything, so the temporary // failure of this font will be forgotten. // UpdateShmBlocks will take the platform font-list lock during the update. if (MOZ_UNLIKELY(!aFontList->UpdateShmBlocks(true))) { goto cleanup;
}
MOZ_ASSERT(blockIndex < blocks.Length(), "failure in UpdateShmBlocks?"); // This is wallpapering bug 1667977; it's unclear if we will always survive // this, as the content process may be unable to shape/render text if all // font lookups are failing. // In at least some cases, however, this can occur transiently while the // font list is being rebuilt by the parent; content will then be notified // that the list has changed, and should refresh everything successfully. if (MOZ_UNLIKELY(blockIndex >= blocks.Length())) { goto cleanup;
}
}
{ // Don't create a pointer that's outside what the block has allocated! constauto& block = blocks[blockIndex]; if (MOZ_LIKELY(Offset() + aSize <= block->Allocated())) {
result = static_cast<char*>(block->Memory()) + Offset();
}
}
cleanup: if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Unlock();
}
return result;
}
void String::Assign(const nsACString& aString, FontList* aList) { // We only assign to previously-empty strings; they are never modified // after initial assignment.
MOZ_ASSERT(mPointer.IsNull());
mLength = aString.Length();
mPointer = aList->Alloc(mLength + 1); auto* p = mPointer.ToArray<char>(aList, mLength);
std::memcpy(p, aString.BeginReading(), mLength);
p[mLength] = '\0';
}
uint32_t count = aFaces.Length(); bool isSimple = false; // A family is "simple" (i.e. simplified style selection may be used instead // of the full CSS font-matching algorithm) if there is at maximum one normal, // bold, italic, and bold-italic face; in this case, they are stored at known // positions in the mFaces array. const Face::InitData* slots[4] = {nullptr, nullptr, nullptr, nullptr}; if (count >= 2 && count <= 4) { // Check if this can be treated as a "simple" family
isSimple = true; for (constauto& f : aFaces) { if (!f.mWeight.IsSingle() || !f.mStretch.IsSingle() ||
!f.mStyle.IsSingle()) {
isSimple = false; break;
} if (!f.mStretch.Min().IsNormal()) {
isSimple = false; break;
} // Figure out which slot (0-3) this face belongs in
size_t slot = 0;
static_assert((kBoldMask | kItalicMask) == 0b11, "bad bold/italic bits"); if (f.mWeight.Min().IsBold()) {
slot |= kBoldMask;
} if (f.mStyle.Min().IsItalic() || f.mStyle.Min().IsOblique()) {
slot |= kItalicMask;
} if (slots[slot]) { // More than one face mapped to the same slot - not a simple family!
isSimple = false; break;
}
slots[slot] = &f;
} if (isSimple) { // Ensure all 4 slots will exist, even if some are empty.
count = 4;
}
}
// Allocate space for the face records, and initialize them. // coverity[suspicious_sizeof]
Pointer p = aList->Alloc(count * sizeof(Pointer)); auto* facePtrs = p.ToArray<Pointer>(aList, count); for (size_t i = 0; i < count; i++) { if (isSimple && !slots[i]) {
facePtrs[i] = Pointer::Null();
} else { constauto* initData = isSimple ? slots[i] : &aFaces[i];
Pointer fp = aList->Alloc(sizeof(Face)); auto* face = fp.ToPtr<Face>(aList);
(void)new (face) Face(aList, *initData);
facePtrs[i] = fp; if (initData->mCharMap) {
face->SetCharacterMap(aList, initData->mCharMap, this);
}
}
}
Pointer* facePtrs = Faces(aList); if (!facePtrs) { returnfalse;
}
// Depending on the kind of family, we have to do varying amounts of work // to figure out what face(s) to use for the requested style properties.
// If the family has only one face, we simply use it; no further style // checking needed. (However, for bitmap fonts we may still need to check // whether the size is acceptable.) if (NumFaces() == 1) {
MOZ_ASSERT(!facePtrs[0].IsNull()); auto* face = facePtrs[0].ToPtr<Face>(aList); if (face && face->HasValidDescriptor()) {
aFaceList.AppendElement(face); #ifdef MOZ_WIDGET_GTK if (face->mSize) { returntrue;
} #endif
} returnfalse;
}
// Most families are "simple", having just Regular/Bold/Italic/BoldItalic, // or some subset of these. In this case, we have exactly 4 entries in // mAvailableFonts, stored in the above order; note that some of the entries // may be nullptr. We can then pick the required entry based on whether the // request is for bold or non-bold, italic or non-italic, without running // the more complex matching algorithm used for larger families with many // weights and/or widths.
if (mIsSimple) { // Family has no more than the "standard" 4 faces, at fixed indexes; // calculate which one we want. // Note that we cannot simply return it as not all 4 faces are necessarily // present. bool wantBold = aStyle.weight.IsBold(); bool wantItalic = !aStyle.style.IsNormal();
uint8_t faceIndex =
(wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
// If the desired style is available, use it directly. auto* face = facePtrs[faceIndex].ToPtr<Face>(aList); if (face && face->HasValidDescriptor()) {
aFaceList.AppendElement(face); #ifdef MOZ_WIDGET_GTK if (face->mSize) { returntrue;
} #endif returnfalse;
}
// Order to check fallback faces in a simple family, depending on the // requested style. staticconst uint8_t simpleFallbacks[4][3] = {
{kBoldFaceIndex, kItalicFaceIndex,
kBoldItalicFaceIndex}, // fallback sequence for Regular
{kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex}, // Bold
{kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex}, // Italic
{kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex} // BoldItalic
}; const uint8_t* order = simpleFallbacks[faceIndex];
for (uint8_t trial = 0; trial < 3; ++trial) { // check remaining faces in order of preference to find the first that // actually exists
face = facePtrs[order[trial]].ToPtr<Face>(aList); if (face && face->HasValidDescriptor()) {
aFaceList.AppendElement(face); #ifdef MOZ_WIDGET_GTK if (face->mSize) { returntrue;
} #endif returnfalse;
}
}
// We can only reach here if we failed to resolve the face pointer, which // can happen if we're on a stylo thread and caught the font list being // updated; in that case we just fail quietly and let font fallback do // something for the time being. returnfalse;
}
// Pick the font(s) that are closest to the desired weight, style, and // stretch. Iterate over all fonts, measuring the weight/style distance. // Because of unicode-range values, there may be more than one font for a // given but the 99% use case is only a single font entry per // weight/style/stretch distance value. To optimize this, only add entries // to the matched font array when another entry already has the same // weight/style/stretch distance and add the last matched font entry. For // normal platform fonts with a single font entry for each // weight/style/stretch combination, only the last matched font entry will // be added. double minDistance = INFINITY;
Face* matched = nullptr; // Keep track of whether we've included any non-scalable font resources in // the selected set. bool anyNonScalable = false; for (uint32_t i = 0; i < NumFaces(); i++) { auto* face = facePtrs[i].ToPtr<Face>(aList); if (face) { // weight/style/stretch priority: stretch >> style >> weight double distance = WSSDistance(face, aStyle); if (distance < minDistance) {
matched = face; if (!aFaceList.IsEmpty()) {
aFaceList.Clear();
}
minDistance = distance;
} elseif (distance == minDistance) { if (matched) {
aFaceList.AppendElement(matched); #ifdef MOZ_WIDGET_GTK if (matched->mSize) {
anyNonScalable = true;
} #endif
}
matched = face;
}
}
}
MOZ_ASSERT(matched, "didn't match a font within a family"); if (matched) {
aFaceList.AppendElement(matched); #ifdef MOZ_WIDGET_GTK if (matched->mSize) {
anyNonScalable = true;
} #endif
}
#ifdef MOZ_WIDGET_GTK // aFaceList now contains whatever faces are the best style match for // the requested style. If specifically-sized bitmap faces are supported, // we need to additionally filter the list to choose the appropriate size. // // It would be slightly more efficient to integrate this directly into the // face-selection algorithm above, but it's a rare case that doesn't apply // at all to most font families. // // Currently we only support pixel-sized bitmap font faces on Linux/Gtk (i.e. // when using the gfxFcPlatformFontList implementation), so this filtering is // not needed on other platforms. // // (Note that color-bitmap emoji fonts like Apple Color Emoji or Noto Color // Emoji don't count here; they package multiple bitmap sizes into a single // OpenType wrapper, so they appear as a single "scalable" face in our list.) if (anyNonScalable) {
uint16_t best = 0;
gfxFloat dist = 0.0; for (constauto& f : aFaceList) { if (f->mSize == 0) { // Scalable face; no size distance to compute. continue;
}
gfxFloat d = fabs(gfxFloat(f->mSize) - aStyle.size); if (!aIgnoreSizeTolerance && (d * 5.0 > f->mSize)) { continue; // Too far from the requested size, ignore.
} // If we haven't found a "best" bitmap size yet, or if this is a better // match, remember it. if (!best || d < dist) {
best = f->mSize;
dist = d;
}
} // Discard all faces except the chosen "best" size; or if no pixel size was // chosen, all except scalable faces. // This may eliminate *all* faces in the family, if all were bitmaps and // none was a good enough size match, in which case we'll fall back to the // next font-family name.
aFaceList.RemoveElementsBy([=](constauto& e) { return e->mSize != best; });
} #endif
}
void Family::SearchAllFontsForChar(FontList* aList,
GlobalFontMatch* aMatchData) { auto* charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList); if (!charmap) { // If the face list is not yet initialized, or if character maps have // not been loaded, go ahead and do this now (by sending a message to the // parent process, if we're running in a child). // After this, all faces should have their mCharacterMap set up, and the // family's mCharacterMap should also be set; but in the code below we // don't assume this all succeeded, so it still checks. if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this, true)) { return;
}
charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList);
} if (charmap && !charmap->test(aMatchData->mCh)) { return;
}
uint32_t numFaces = NumFaces();
uint32_t charMapsLoaded = 0; // number of faces whose charmap is loaded
Pointer* facePtrs = Faces(aList); if (!facePtrs) { return;
} for (uint32_t i = 0; i < numFaces; i++) { auto* face = facePtrs[i].ToPtr<Face>(aList); if (!face) { continue;
}
MOZ_ASSERT(face->HasValidDescriptor()); // Get the face's character map, if available (may be null!)
charmap = face->mCharacterMap.ToPtr<const SharedBitSet>(aList); if (charmap) {
++charMapsLoaded;
} // Check style distance if the char is supported, or if charmap not known // (so that we don't trigger cmap-loading for faces that would be a worse // match than what we've already found). if (!charmap || charmap->test(aMatchData->mCh)) { double distance = WSSDistance(face, aMatchData->mStyle); if (distance < aMatchData->mMatchDistance) { // It's a better style match: get a fontEntry, and if we haven't // already checked character coverage, do it now (note that // HasCharacter() will trigger loading the fontEntry's cmap, if // needed).
RefPtr<gfxFontEntry> fe =
gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face, this); if (!fe) { continue;
} if (!charmap && !fe->HasCharacter(aMatchData->mCh)) { continue;
} if (aMatchData->mPresentation != eFontPresentation::Any) {
RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle); if (!font) { continue;
} bool hasColorGlyph =
font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh); if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) {
distance += kPresentationMismatch; if (distance >= aMatchData->mMatchDistance) { continue;
}
}
}
aMatchData->mBestMatch = fe;
aMatchData->mMatchDistance = distance;
aMatchData->mMatchedSharedFamily = this;
}
}
} if (mCharacterMap.IsNull() && charMapsLoaded == numFaces) {
SetupFamilyCharMap(aList);
}
}
void Family::SetFacePtrs(FontList* aList, nsTArray<Pointer>& aFaces) { if (aFaces.Length() >= 2 && aFaces.Length() <= 4) { // Check whether the faces meet the criteria for a "simple" family: no more // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store // them at the appropriate slots in mFaces and set the mIsSimple flag to // accelerate font-matching. bool isSimple = true;
Pointer slots[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(),
Pointer::Null()}; for (const Pointer& fp : aFaces) { auto* f = fp.ToPtr<const Face>(aList); if (!f->mWeight.IsSingle() || !f->mStyle.IsSingle() ||
!f->mStretch.IsSingle()) {
isSimple = false; break;
} if (!f->mStretch.Min().IsNormal()) {
isSimple = false; break;
}
size_t slot = 0; if (f->mWeight.Min().IsBold()) {
slot |= kBoldMask;
} if (f->mStyle.Min().IsItalic() || f->mStyle.Min().IsOblique()) {
slot |= kItalicMask;
} if (!slots[slot].IsNull()) {
isSimple = false; break;
}
slots[slot] = fp;
} if (isSimple) {
size_t size = 4 * sizeof(Pointer);
mFaces = aList->Alloc(size);
memcpy(mFaces.ToPtr(aList, size), slots, size);
mFaceCount.store(4);
mIsSimple = true; return;
}
}
size_t size = aFaces.Length() * sizeof(Pointer);
mFaces = aList->Alloc(size);
memcpy(mFaces.ToPtr(aList, size), aFaces.Elements(), size);
mFaceCount.store(aFaces.Length());
}
void Family::SetupFamilyCharMap(FontList* aList) { // Set the character map of the family to the union of all the face cmaps, // to allow font fallback searches to more rapidly reject the family. if (!mCharacterMap.IsNull()) { return;
} if (!XRE_IsParentProcess()) { // |this| could be a Family record in either the Families() or Aliases() // arrays; FindIndex will map it back to its index and which array.
Maybe<std::pair<uint32_t, bool>> index = FindIndex(aList); if (!index) {
NS_WARNING("Family index not found! Ignoring SetupFamilyCharMap"); return;
} if (NS_IsMainThread()) {
dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
aList->GetGeneration(), index->first, index->second); return;
}
NS_DispatchToMainThread(NS_NewRunnableFunction( "SetupFamilyCharMap callback",
[gen = aList->GetGeneration(), idx = index->first,
alias = index->second] {
dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(gen, idx,
alias);
})); return;
}
gfxSparseBitSet familyMap;
Pointer firstMapShmPointer; const SharedBitSet* firstMap = nullptr; bool merged = false;
Pointer* faces = Faces(aList); if (!faces) { return;
} for (size_t i = 0; i < NumFaces(); i++) { auto* f = faces[i].ToPtr<const Face>(aList); if (!f) { continue; // Skip missing face (in an incomplete "simple" family)
} auto* faceMap = f->mCharacterMap.ToPtr<const SharedBitSet>(aList); if (!faceMap) { continue; // If there's a face where setting up the cmap failed, we skip // it as unusable.
} if (!firstMap) {
firstMap = faceMap;
firstMapShmPointer = f->mCharacterMap;
} elseif (faceMap != firstMap) { if (!merged) {
familyMap.Union(*firstMap);
merged = true;
}
familyMap.Union(*faceMap);
}
} // If we created a merged cmap, we need to save that on the family; or if we // found no usable cmaps at all, we need to store the empty familyMap so that // we won't repeatedly attempt this for an unusable family. if (merged || firstMapShmPointer.IsNull()) {
mCharacterMap =
gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap);
} else { // If all [usable] faces had the same cmap, we can just share it.
mCharacterMap = firstMapShmPointer;
}
}
Maybe<std::pair<uint32_t, bool>> Family::FindIndex(FontList* aList) const { constauto* start = aList->Families(); constauto* end = start + aList->NumFamilies(); if (this >= start && this < end) {
uint32_t index = this - start;
MOZ_RELEASE_ASSERT(start + index == this, "misaligned Family ptr!"); return Some(std::pair(index, false));
}
start = aList->AliasFamilies();
end = start + aList->NumAliases(); if (this >= start && this < end) {
uint32_t index = this - start;
MOZ_RELEASE_ASSERT(start + index == this, "misaligned AliasFamily ptr!"); return Some(std::pair(index, true));
}
return Nothing();
}
FontList::FontList(uint32_t aGeneration) { if (XRE_IsParentProcess()) { // Create the initial shared block, and initialize Header if (AppendShmBlock(SHM_BLOCK_SIZE)) {
Header& header = GetHeader();
header.mBlockHeader.mAllocated.store(sizeof(Header));
header.mGeneration = aGeneration;
header.mFamilyCount = 0;
header.mBlockCount.store(1);
header.mAliasCount.store(0);
header.mLocalFaceCount.store(0);
header.mFamilies = Pointer::Null();
header.mAliases = Pointer::Null();
header.mLocalFaces = Pointer::Null();
} else {
MOZ_CRASH("parent: failed to initialize FontList");
}
} else { // Initialize using the list of shmem blocks passed by the parent via // SetXPCOMProcessAttributes. auto& blocks = dom::ContentChild::GetSingleton()->SharedFontListBlocks(); for (auto& handle : blocks) { auto newShm = MakeRefPtr<ipc::SharedMemory>(); if (!newShm->IsHandleValid(handle)) { // Bail out and let UpdateShmBlocks try to do its thing below. break;
} if (!newShm->SetHandle(std::move(handle),
ipc::SharedMemory::OpenRights::RightsReadOnly)) {
MOZ_CRASH("failed to set shm handle");
} if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->Memory()) {
MOZ_CRASH("failed to map shared memory");
}
uint32_t size = static_cast<BlockHeader*>(newShm->Memory())->mBlockSize;
MOZ_ASSERT(size >= SHM_BLOCK_SIZE); if (size != SHM_BLOCK_SIZE) {
newShm->Unmap(); if (!newShm->Map(size) || !newShm->Memory()) {
MOZ_CRASH("failed to map shared memory");
}
}
mBlocks.AppendElement(new ShmBlock(std::move(newShm)));
}
blocks.Clear(); // Update in case of any changes since the initial message was sent. for (unsigned retryCount = 0; retryCount < 3; ++retryCount) { if (UpdateShmBlocks(false)) { return;
} // The only reason for UpdateShmBlocks to fail is if the parent recreated // the list after we read its first block, but before we finished getting // them all, and so the generation check failed on a subsequent request. // Discarding whatever we've got and retrying should get us a new, // consistent set of memory blocks in this case. If this doesn't work // after a couple of retries, bail out.
DetachShmBlocks();
}
NS_WARNING("child: failed to initialize shared FontList");
}
}
FontList::~FontList() { DetachShmBlocks(); }
FontList::Header& FontList::GetHeader() const MOZ_NO_THREAD_SAFETY_ANALYSIS { // We only need to lock if we're not on the main thread. bool isMainThread = NS_IsMainThread(); if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Lock();
}
// It's invalid to try and access this before the first block exists.
MOZ_ASSERT(mBlocks.Length() > 0); auto& result = *static_cast<Header*>(mBlocks[0]->Memory());
if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Unlock();
}
return result;
}
bool FontList::AppendShmBlock(uint32_t aSizeNeeded) {
MOZ_ASSERT(XRE_IsParentProcess());
uint32_t size = std::max(aSizeNeeded, SHM_BLOCK_SIZE); auto newShm = MakeRefPtr<ipc::SharedMemory>(); if (!newShm->CreateFreezable(size)) {
MOZ_CRASH("failed to create shared memory"); returnfalse;
} if (!newShm->Map(size) || !newShm->Memory()) {
MOZ_CRASH("failed to map shared memory"); returnfalse;
} auto readOnly = MakeRefPtr<ipc::SharedMemory>(); if (!newShm->ReadOnlyCopy(readOnly.get())) {
MOZ_CRASH("failed to create read-only copy"); returnfalse;
}
ShmBlock* block = new ShmBlock(std::move(newShm));
block->StoreAllocated(sizeof(BlockHeader));
block->BlockSize() = size;
// We don't need to broadcast the addition of the initial block, // because child processes can't have initialized their list at all // prior to the first block being set up. if (mBlocks.Length() > 1) { if (NS_IsMainThread()) {
dom::ContentParent::BroadcastShmBlockAdded(GetGeneration(),
mBlocks.Length() - 1);
} else {
NS_DispatchToMainThread(NS_NewRunnableFunction( "ShmBlockAdded callback",
[generation = GetGeneration(), index = mBlocks.Length() - 1] {
dom::ContentParent::BroadcastShmBlockAdded(generation, index);
}));
}
}
auto newShm = MakeRefPtr<ipc::SharedMemory>(); if (!newShm->IsHandleValid(aHandle)) { return;
} if (!newShm->SetHandle(std::move(aHandle),
ipc::SharedMemory::RightsReadOnly)) {
MOZ_CRASH("failed to set shm handle");
}
if (aIndex != mBlocks.Length()) { return;
} if (aGeneration != GetGeneration()) { return;
}
if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->Memory()) {
MOZ_CRASH("failed to map shared memory");
}
uint32_t size = static_cast<BlockHeader*>(newShm->Memory())->mBlockSize;
MOZ_ASSERT(size >= SHM_BLOCK_SIZE); if (size != SHM_BLOCK_SIZE) {
newShm->Unmap(); if (!newShm->Map(size) || !newShm->Memory()) {
MOZ_CRASH("failed to map shared memory");
}
}
void FontList::DetachShmBlocks() { for (auto& i : mBlocks) {
i->mShmem = nullptr;
}
mBlocks.Clear();
mReadOnlyShmems.Clear();
}
FontList::ShmBlock* FontList::GetBlockFromParent(uint32_t aIndex) {
MOZ_ASSERT(!XRE_IsParentProcess()); // If we have no existing blocks, we don't want a generation check yet; // the header in the first block will define the generation of this list
uint32_t generation = aIndex == 0 ? 0 : GetGeneration();
ipc::SharedMemory::Handle handle = ipc::SharedMemory::NULLHandle(); if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
generation, aIndex, &handle)) { return nullptr;
} auto newShm = MakeRefPtr<ipc::SharedMemory>(); if (!newShm->IsHandleValid(handle)) { return nullptr;
} if (!newShm->SetHandle(std::move(handle),
ipc::SharedMemory::RightsReadOnly)) {
MOZ_CRASH("failed to set shm handle");
} if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->Memory()) {
MOZ_CRASH("failed to map shared memory");
}
uint32_t size = static_cast<BlockHeader*>(newShm->Memory())->mBlockSize;
MOZ_ASSERT(size >= SHM_BLOCK_SIZE); if (size != SHM_BLOCK_SIZE) {
newShm->Unmap(); if (!newShm->Map(size) || !newShm->Memory()) {
MOZ_CRASH("failed to map shared memory");
}
} returnnew ShmBlock(std::move(newShm));
}
// We don't take the lock when called from the constructor, so disable thread- // safety analysis here. bool FontList::UpdateShmBlocks(bool aMustLock) MOZ_NO_THREAD_SAFETY_ANALYSIS {
MOZ_ASSERT(!XRE_IsParentProcess()); if (aMustLock) {
gfxPlatformFontList::PlatformFontList()->Lock();
} bool result = true; while (!mBlocks.Length() || mBlocks.Length() < GetHeader().mBlockCount) {
ShmBlock* newBlock = GetBlockFromParent(mBlocks.Length()); if (!newBlock) {
result = false; break;
}
mBlocks.AppendElement(newBlock);
} if (aMustLock) {
gfxPlatformFontList::PlatformFontList()->Unlock();
} return result;
}
void FontList::ShareBlocksToProcess(
nsTArray<ipc::SharedMemory::Handle>* aBlocks, base::ProcessId aPid) {
MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length()); for (auto& shmem : mReadOnlyShmems) { auto handle = shmem->CloneHandle(); if (!handle) { // If something went wrong here, we just bail out; the child will need to // request the blocks as needed, at some performance cost. (Although in // practice this may mean resources are so constrained the child process // isn't really going to work at all. But that's not our problem here.)
aBlocks->Clear(); return;
}
aBlocks->AppendElement(std::move(handle));
}
}
Pointer FontList::Alloc(uint32_t aSize) { // Only the parent process does allocation.
MOZ_ASSERT(XRE_IsParentProcess());
// 4-byte alignment is good enough for anything we allocate in the font list, // as our "Pointer" (block index/offset) is a 32-bit value even on x64. auto align = [](uint32_t aSize) -> size_t { return (aSize + 3u) & ~3u; };
aSize = align(aSize);
int32_t blockIndex = -1;
uint32_t curAlloc, size;
if (aSize < SHM_BLOCK_SIZE - sizeof(BlockHeader)) { // Try to allocate in the most recently added block first, as this is // highly likely to succeed; if not, try earlier blocks (to fill gaps). const int32_t blockCount = mBlocks.Length(); for (blockIndex = blockCount - 1; blockIndex >= 0; --blockIndex) {
size = mBlocks[blockIndex]->BlockSize();
curAlloc = mBlocks[blockIndex]->Allocated(); if (size - curAlloc >= aSize) { break;
}
}
}
if (blockIndex < 0) { // Couldn't find enough space (or the requested size is too large to use // a part of a block): create a new block. if (!AppendShmBlock(aSize + sizeof(BlockHeader))) { return Pointer::Null();
}
blockIndex = mBlocks.Length() - 1;
curAlloc = mBlocks[blockIndex]->Allocated();
}
// We've found a block; allocate space from it, and return
mBlocks[blockIndex]->StoreAllocated(curAlloc + aSize);
return Pointer(blockIndex, curAlloc);
}
void FontList::SetFamilyNames(nsTArray<Family::InitData>& aFamilies) { // Only the parent process should ever assign the list of families.
MOZ_ASSERT(XRE_IsParentProcess());
// Any font resources with an empty family-name will have been collected in // a family with empty name, and sorted to the start of the list. Such fonts // are not generally usable, or recognized as "installed", so we drop them. if (count > 1 && aFamilies[0].mKey.IsEmpty()) {
aFamilies.RemoveElementAt(0);
--count;
}
// Check for duplicate family entries (can occur if there is a bundled font // that has the same name as a system-installed one); in this case we keep // the bundled one as it will always be exposed. if (count > 1) { for (size_t i = 1; i < count; ++i) { if (aFamilies[i].mKey.Equals(aFamilies[i - 1].mKey)) { // Decide whether to discard the current entry or the preceding one
size_t discard =
aFamilies[i].mBundled && !aFamilies[i - 1].mBundled ? i - 1 : i;
aFamilies.RemoveElementAt(discard);
--count;
--i;
}
}
}
header.mFamilies = Alloc(count * sizeof(Family)); if (header.mFamilies.IsNull()) { return;
}
// We can't call Families() here because the mFamilyCount field has not yet // been set! auto* families = header.mFamilies.ToArray<Family>(this, count); for (size_t i = 0; i < count; i++) {
(void)new (&families[i]) Family(this, aFamilies[i]);
LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i,
aFamilies[i].mName.get()));
}
// Build an array of Family::InitData records based on the entries in // aAliasTable, then sort them and store into the fontlist.
nsTArray<Family::InitData> aliasArray;
aliasArray.SetCapacity(aAliasTable.Count()); for (constauto& entry : aAliasTable) {
aliasArray.AppendElement(Family::InitData(
entry.GetKey(), entry.GetData()->mBaseFamily, entry.GetData()->mIndex,
entry.GetData()->mVisibility, entry.GetData()->mBundled,
entry.GetData()->mBadUnderline, entry.GetData()->mForceClassic, true));
}
aliasArray.Sort();
size_t count = aliasArray.Length();
// Drop any entry with empty family-name as being unusable. if (count && aliasArray[0].mKey.IsEmpty()) {
aliasArray.RemoveElementAt(0);
--count;
}
if (count < header.mAliasCount) { // This shouldn't happen, but handle it safely by just bailing out.
NS_WARNING("cannot reduce number of aliases"); return;
}
fontlist::Pointer ptr = Alloc(count * sizeof(Family)); auto* aliases = ptr.ToArray<Family>(this, count); for (size_t i = 0; i < count; i++) {
(void)new (&aliases[i]) Family(this, aliasArray[i]);
LOG_FONTLIST(("(shared-fontlist) alias family %u (%s: %s)", (unsigned)i,
aliasArray[i].mKey.get(), aliasArray[i].mName.get()));
aliases[i].SetFacePtrs(this, aAliasTable.Get(aliasArray[i].mKey)->mFaces); if (LOG_FONTLIST_ENABLED()) { constauto& faces = aAliasTable.Get(aliasArray[i].mKey)->mFaces; for (unsigned j = 0; j < faces.Length(); j++) { auto* face = faces[j].ToPtr<const Face>(this); const nsCString& desc = face->mDescriptor.AsString(this);
nsAutoCString weight, style, stretch;
face->mWeight.ToString(weight);
face->mStyle.ToString(style);
face->mStretch.ToString(stretch);
LOG_FONTLIST(
("(shared-fontlist) face (%s) index %u, weight %s, style %s, " "stretch %s",
desc.get(), face->mIndex, weight.get(), style.get(),
stretch.get()));
}
}
}
// Set the pointer before the count, so that any other process trying to read // will not risk out-of-bounds access to the array.
header.mAliases = ptr;
header.mAliasCount.store(count);
}
void FontList::SetLocalNames(
nsTHashMap<nsCStringHashKey, LocalFaceRec::InitData>& aLocalNameTable) {
MOZ_ASSERT(XRE_IsParentProcess());
Header& header = GetHeader(); if (header.mLocalFaceCount > 0) { return; // already been done!
} auto faceArray = ToTArray<nsTArray<nsCString>>(aLocalNameTable.Keys());
faceArray.Sort();
size_t count = faceArray.Length();
Family* families = Families();
fontlist::Pointer ptr = Alloc(count * sizeof(LocalFaceRec)); auto* faces = ptr.ToArray<LocalFaceRec>(this, count); for (size_t i = 0; i < count; i++) {
(void)new (&faces[i]) LocalFaceRec(); constauto& rec = aLocalNameTable.Get(faceArray[i]);
faces[i].mKey.Assign(faceArray[i], this); // Local face name records will refer to the canonical family name; we don't // need to search aliases here. constauto* family = FindFamily(rec.mFamilyName, /*aPrimaryNameOnly*/ true); if (!family) { // Skip this record if the family was excluded by the font whitelist pref. continue;
}
faces[i].mFamilyIndex = family - families; if (rec.mFaceIndex == uint32_t(-1)) { // The InitData record contains an mFaceDescriptor rather than an index, // so now we need to look for the appropriate index in the family.
faces[i].mFaceIndex = 0; const Pointer* faceList = static_cast<const Pointer*>(family->Faces(this)); for (uint32_t j = 0; j < family->NumFaces(); j++) { if (!faceList[j].IsNull()) { auto* f = faceList[j].ToPtr<const Face>(this); if (f && rec.mFaceDescriptor == f->mDescriptor.AsString(this)) {
faces[i].mFaceIndex = j; break;
}
}
}
} else {
faces[i].mFaceIndex = rec.mFaceIndex;
}
}
header.mLocalFaces = ptr;
header.mLocalFaceCount.store(count);
}
nsCString FontList::LocalizedFamilyName(const Family* aFamily) { // If the given family was created for an alternate locale or legacy name, // search for a standard family that corresponds to it. This is a linear // search of the font list, but (a) this is only used to show names in // Preferences, so is not performance-critical for layout etc.; and (b) few // such family names are normally present anyway, the vast majority of fonts // just have a single family name and we return it directly. if (aFamily->IsAltLocaleFamily()) { // Currently only the Windows backend actually does this; on other systems, // the family index is unused and will be kNoIndex for all fonts. if (aFamily->Index() != Family::kNoIndex) { const Family* families = Families(); for (uint32_t i = 0; i < NumFamilies(); ++i) { if (families[i].Index() == aFamily->Index() &&
families[i].IsBundled() == aFamily->IsBundled() &&
!families[i].IsAltLocaleFamily()) { return families[i].DisplayName().AsString(this);
}
}
}
}
// For standard families (or if we failed to find the expected standard // family for some reason), just return the DisplayName. return aFamily->DisplayName().AsString(this);
}
#ifdef XP_WIN // For Windows only, because of how DWrite munges font family names in some // cases (see // https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/02/24/90/36/WPF%20Font%20Selection%20Model.pdf // and discussion on the OpenType list), try stripping a possible style-name // suffix from the end of the requested family name. // After the deferred font loader has finished, this is no longer needed as // the "real" family names will have been found in AliasFamilies() above. if (aName.Contains(' ')) { auto pfl = gfxPlatformFontList::PlatformFontList();
pfl->mLock.AssertCurrentThreadIn(); if (header.mAliasCount) { // Aliases have been fully loaded by the parent process, so just discard // any stray mAliasTable and mLocalNameTable entries from earlier calls // to this code, and return.
pfl->mAliasTable.Clear();
pfl->mLocalNameTable.Clear();
mFaceNamesRead.Clear(); return nullptr;
}
// Do we already have an aliasData record for this name? If so, we just // return its base family. if (auto lookup = pfl->mAliasTable.Lookup(aName)) { return FindFamily(lookup.Data()->mBaseFamily, true);
}
// Strip the style suffix (after last space in the name) to get a "base" // family name. constchar* data = aName.BeginReading();
int32_t index = aName.Length(); while (--index > 0) { if (data[index] == ' ') { break;
}
} if (index <= 0) { return nullptr;
}
nsAutoCString base(Substring(aName, 0, index)); auto familyCount = header.mFamilyCount; if (BinarySearchIf(families, 0, familyCount,
FamilyNameComparator(this, base), &match)) { // Check to see if we have already read the face names for this base // family. Note: EnsureLengthAtLeast will default new entries to false.
mFaceNamesRead.EnsureLengthAtLeast(familyCount); if (mFaceNamesRead[match]) { return nullptr;
} // This may be a possible base family to satisfy the search; call // ReadFaceNamesForFamily and see if the desired name ends up in // mAliasTable. // Note that ReadFaceNamesForFamily may store entries in mAliasTable // (and mLocalNameTable), but if this is happening in a content // process (which is the common case) those entries will not be saved // into the shared font list; they're just used here until the "real" // alias list is ready, then discarded.
Family* baseFamily = &families[match];
pfl->ReadFaceNamesForFamily(baseFamily, false);
mFaceNamesRead[match] = true; if (auto lookup = pfl->mAliasTable.Lookup(aName)) { if (lookup.Data()->mFaces.Length() != baseFamily->NumFaces()) { // If the alias family doesn't have all the faces of the base family, // then style matching may end up resolving to a face that isn't // supposed to be available in the legacy styled family. To ensure // such mis-styling will get fixed, we start the async font info // loader (if it hasn't yet been triggered), which will pull in the // full metadata we need and then force a reflow.
pfl->InitOtherFamilyNames(/* aDeferOtherFamilyNamesLoading */ true);
} return baseFamily;
}
}
} #endif
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.