// static already_AddRefed<InternalHeaders> InternalHeaders::CORSHeaders(InternalHeaders* aHeaders) { RefPtr<InternalHeaders> cors = new InternalHeaders(aHeaders->mGuard); ErrorResult result; nsAutoCString acExposedNames; aHeaders->Get(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result); MOZ_ASSERT(!result.Failed()); AutoTArray<nsCString, 5> exposeNamesArray; nsCCharSeparatedTokenizer exposeTokens(acExposedNames, ','); while (exposeTokens.hasMoreTokens()) { const nsDependentCSubstring& token = exposeTokens.nextToken(); if (token.IsEmpty()) { continue; } if (!NS_IsValidHTTPToken(token)) { NS_WARNING("Got invalid HTTP token in Access-Control-Expose-Headers. Header value is:"); NS_WARNING(acExposedNames.get()); exposeNamesArray.Clear(); break; } exposeNamesArray.AppendElement(token); } nsCaseInsensitiveCStringArrayComparator comp; for (uint32_t i = 0; i < aHeaders->mList.Length(); ++i) { const Entry& entry = aHeaders->mList[i]; if (entry.mName.EqualsASCII("cache-control") || entry.mName.EqualsASCII("content-language") || entry.mName.EqualsASCII("content-type") || entry.mName.EqualsASCII("expires") || entry.mName.EqualsASCII("last-modified") || entry.mName.EqualsASCII("pragma") || exposeNamesArray.Contains(entry.mName, comp)) { cors->Append(entry.mName, entry.mValue, result); MOZ_ASSERT(!result.Failed()); } } return cors.forget(); }
nsresult gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText, uint32_t aOffset, uint32_t aLength, CTRunRef aCTRun, int32_t aStringOffset) { // The word has been bidi-wrapped; aStringOffset is the number // of chars at the beginning of the CTLine that we should skip. // aCTRun is a glyph run from the CoreText layout process. int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1; int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun); if (numGlyphs == 0) { return NS_OK; } int32_t wordLength = aLength; // character offsets get really confusing here, as we have to keep track of // (a) the text in the actual textRun we're constructing // (c) the string that was handed to CoreText, which contains the text of the font run // plus directional-override padding // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line // (but may extend beyond the actual font run into the bidi wrapping text). // aStringOffset tells us how many initial characters of the line to ignore. // get the source string range within the CTLine's text CFRange stringRange = ::CTRunGetStringRange(aCTRun); // skip the run if it is entirely outside the actual range of the font run if (stringRange.location - aStringOffset + stringRange.length <= 0 || stringRange.location - aStringOffset >= wordLength) { return NS_OK; } // retrieve the laid-out glyph data from the CTRun UniquePtr<CGGlyph[]> glyphsArray; UniquePtr<CGPoint[]> positionsArray; UniquePtr<CFIndex[]> glyphToCharArray; const CGGlyph* glyphs = nullptr; const CGPoint* positions = nullptr; const CFIndex* glyphToChar = nullptr; // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds, // and so allocating a new array and copying data with CTRunGetGlyphs // will be extremely rare. // If this were not the case, we could use an AutoTArray<> to // try and avoid the heap allocation for small runs. // It's possible that some future change to CoreText will mean that // CTRunGetGlyphsPtr fails more often; if this happens, AutoTArray<> // may become an attractive option. glyphs = ::CTRunGetGlyphsPtr(aCTRun); if (!glyphs) { glyphsArray = MakeUniqueFallible<CGGlyph[]>(numGlyphs); if (!glyphsArray) { return NS_ERROR_OUT_OF_MEMORY; } ::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get()); glyphs = glyphsArray.get(); } positions = ::CTRunGetPositionsPtr(aCTRun); if (!positions) { positionsArray = MakeUniqueFallible<CGPoint[]>(numGlyphs); if (!positionsArray) { return NS_ERROR_OUT_OF_MEMORY; } ::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get()); positions = positionsArray.get(); } // Remember that the glyphToChar indices relate to the CoreText line, // not to the beginning of the textRun, the font run, // or the stringRange of the glyph run glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun); if (!glyphToChar) { glyphToCharArray = MakeUniqueFallible<CFIndex[]>(numGlyphs); if (!glyphToCharArray) { return NS_ERROR_OUT_OF_MEMORY; } ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get()); glyphToChar = glyphToCharArray.get(); } double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0), nullptr, nullptr, nullptr); AutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs; gfxShapedText::CompressedGlyph *charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset; // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph // to a source text character; we also need the charindex-to-glyphindex mapping to // find the glyph for a given char. Note that some chars may not map to any glyph // (ligature continuations), and some may map to several glyphs (eg Indic split vowels). // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we // record the last glyph index for cases where the char maps to several glyphs, // so that our clumping will include all the glyph fragments for the character. // The charToGlyph array is indexed by char position within the stringRange of the glyph run. static const int32_t NO_GLYPH = -1; AutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray; if (!charToGlyphArray.SetLength(stringRange.length, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } int32_t *charToGlyph = charToGlyphArray.Elements(); for (int32_t offset = 0; offset < stringRange.length; ++offset) { charToGlyph[offset] = NO_GLYPH; } for (int32_t i = 0; i < numGlyphs; ++i) { int32_t loc = glyphToChar[i] - stringRange.location; if (loc >= 0 && loc < stringRange.length) { charToGlyph[loc] = i; } } // Find character and glyph clumps that correspond, allowing for ligatures, // indic reordering, split glyphs, etc. // // The idea is that we'll find a character sequence starting at the first char of stringRange, // and extend it until it includes the character associated with the first glyph; // we also extend it as long as there are "holes" in the range of glyphs. So we // will eventually have a contiguous sequence of characters, starting at the beginning // of the range, that map to a contiguous sequence of glyphs, starting at the beginning // of the glyph array. That's a clump; then we update the starting positions and repeat. // // NB: In the case of RTL layouts, we iterate over the stringRange in reverse. // // This may find characters that fall outside the range 0:wordLength, // so we won't necessarily use everything we find here. bool isRightToLeft = aShapedText->IsRightToLeft(); int32_t glyphStart = 0; // looking for a clump that starts at this glyph index int32_t charStart = isRightToLeft ? stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run) while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for bool inOrder = true; int32_t charEnd = glyphToChar[glyphStart] - stringRange.location; NS_WARN_IF_FALSE(charEnd >= 0 && charEnd < stringRange.length, "glyph-to-char mapping points outside string range"); // clamp charEnd to the valid range of the string charEnd = std::max(charEnd, 0); charEnd = std::min(charEnd, int32_t(stringRange.length)); int32_t glyphEnd = glyphStart; int32_t charLimit = isRightToLeft ? -1 : stringRange.length; do { // This is normally executed once for each iteration of the outer loop, // but in unusual cases where the character/glyph association is complex, // the initial character range might correspond to a non-contiguous // glyph range with "holes" in it. If so, we will repeat this loop to // extend the character range until we have a contiguous glyph sequence. NS_ASSERTION((direction > 0 && charEnd < charLimit) || (direction < 0 && charEnd > charLimit), "no characters left in range?"); charEnd += direction; while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) { charEnd += direction; } // find the maximum glyph index covered by the clump so far if (isRightToLeft) { for (int32_t i = charStart; i > charEnd; --i) { if (charToGlyph[i] != NO_GLYPH) { // update extent of glyph range glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1); } } } else { for (int32_t i = charStart; i < charEnd; ++i) { if (charToGlyph[i] != NO_GLYPH) { // update extent of glyph range glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1); } } } if (glyphEnd == glyphStart + 1) { // for the common case of a single-glyph clump, we can skip the following checks break; } if (glyphEnd == glyphStart) { // no glyphs, try to extend the clump continue; } // check whether all glyphs in the range are associated with the characters // in our clump; if not, we have a discontinuous range, and should extend it // unless we've reached the end of the text bool allGlyphsAreWithinCluster = true; int32_t prevGlyphCharIndex = charStart; for (int32_t i = glyphStart; i < glyphEnd; ++i) { int32_t glyphCharIndex = glyphToChar[i] - stringRange.location; if (isRightToLeft) { if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) { allGlyphsAreWithinCluster = false; break; } if (glyphCharIndex > prevGlyphCharIndex) { inOrder = false; } prevGlyphCharIndex = glyphCharIndex; } else { if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) { allGlyphsAreWithinCluster = false; break; } if (glyphCharIndex < prevGlyphCharIndex) { inOrder = false; } prevGlyphCharIndex = glyphCharIndex; } } if (allGlyphsAreWithinCluster) { break; } } while (charEnd != charLimit); NS_WARN_IF_FALSE(glyphStart < glyphEnd, "character/glyph clump contains no glyphs!"); if (glyphStart == glyphEnd) { ++glyphStart; // make progress - avoid potential infinite loop charStart = charEnd; continue; } NS_WARN_IF_FALSE(charStart != charEnd, "character/glyph clump contains no characters!"); if (charStart == charEnd) { glyphStart = glyphEnd; // this is bad - we'll discard the glyph(s), // as there's nowhere to attach them continue; } // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd; // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature), // and endCharIndex to the limit (position beyond the last char), // adjusting for the offset of the stringRange relative to the textRun. int32_t baseCharIndex, endCharIndex; if (isRightToLeft) { while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) { charEnd--; } baseCharIndex = charEnd + stringRange.location - aStringOffset + 1; endCharIndex = charStart + stringRange.location - aStringOffset + 1; } else { while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) { charEnd++; } baseCharIndex = charStart + stringRange.location - aStringOffset; endCharIndex = charEnd + stringRange.location - aStringOffset; } // Then we check if the clump falls outside our actual string range; if so, just go to the next. if (endCharIndex <= 0 || baseCharIndex >= wordLength) { glyphStart = glyphEnd; charStart = charEnd; continue; } // Ensure we won't try to go beyond the valid length of the word's text baseCharIndex = std::max(baseCharIndex, 0); endCharIndex = std::min(endCharIndex, wordLength); // Now we're ready to set the glyph info in the textRun; measure the glyph width // of the first (perhaps only) glyph, to see if it is "Simple" int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit(); double toNextGlyph; if (glyphStart < numGlyphs-1) { toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x; } else { toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x; } int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit); // Check if it's a simple one-to-one mapping int32_t glyphsInClump = glyphEnd - glyphStart; if (glyphsInClump == 1 && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && charGlyphs[baseCharIndex].IsClusterStart() && positions[glyphStart].y == 0.0) { charGlyphs[baseCharIndex].SetSimpleGlyph(advance, glyphs[glyphStart]); } else { // collect all glyphs in a list to be assigned to the first char; // there must be at least one in the clump, and we already measured its advance, // hence the placement of the loop-exit test and the measurement of the next glyph while (1) { gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement(); details->mGlyphID = glyphs[glyphStart]; details->mXOffset = 0; details->mYOffset = -positions[glyphStart].y * appUnitsPerDevUnit; details->mAdvance = advance; if (++glyphStart >= glyphEnd) { break; } if (glyphStart < numGlyphs-1) { toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x; } else { toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x; } advance = int32_t(toNextGlyph * appUnitsPerDevUnit); } gfxTextRun::CompressedGlyph textRunGlyph; textRunGlyph.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(), true, detailedGlyphs.Length()); aShapedText->SetGlyphs(aOffset + baseCharIndex, textRunGlyph, detailedGlyphs.Elements()); detailedGlyphs.Clear(); } // the rest of the chars in the group are ligature continuations, no associated glyphs while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) { gfxShapedText::CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex]; NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph"); shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0); } glyphStart = glyphEnd; charStart = charEnd; } return NS_OK; }
gfxFontconfigUtils::LangSupportEntry * gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, bool aWithFonts) { // Currently any unrecognized languages from documents will be converted // to x-unicode by nsILanguageAtomService, so there is a limit on the // langugages that will be added here. Reconsider when/if document // languages are passed to this routine. LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang); if (!entry) return nullptr; FcLangResult best = FcLangDifferentLang; if (!entry->IsKeyInitialized()) { entry->InitKey(aLang); } else { // mSupport is already initialized. if (!aWithFonts) return entry; best = entry->mSupport; // If there is support for this language, an empty font list indicates // that the list hasn't been initialized yet. if (best == FcLangDifferentLang || entry->mFonts.Length() > 0) return entry; } // These FcFontSets are owned by fontconfig FcFontSet *fontSets[] = { FcConfigGetFonts(nullptr, FcSetSystem) #ifdef MOZ_BUNDLED_FONTS , FcConfigGetFonts(nullptr, FcSetApplication) #endif }; AutoTArray<FcPattern*,100> fonts; for (unsigned fs = 0; fs < ArrayLength(fontSets); ++fs) { FcFontSet *fontSet = fontSets[fs]; if (!fontSet) { continue; } for (int f = 0; f < fontSet->nfont; ++f) { FcPattern *font = fontSet->fonts[f]; FcLangResult support = GetLangSupport(font, aLang); if (support < best) { // lower is better best = support; if (aWithFonts) { fonts.Clear(); } else if (best == FcLangEqual) { break; } } // The font list in the LangSupportEntry is expected to be used // only when no default fonts support the language. There would // be a large number of fonts in entries for languages using Latin // script but these do not need to be created because default // fonts already support these languages. if (aWithFonts && support != FcLangDifferentLang && support == best) { fonts.AppendElement(font); } } } entry->mSupport = best; if (aWithFonts) { if (fonts.Length() != 0) { entry->mFonts.AppendElements(fonts.Elements(), fonts.Length()); } else if (best != FcLangDifferentLang) { // Previously there was a font that supported this language at the // level of entry->mSupport, but it has now disappeared. At least // entry->mSupport needs to be recalculated, but this is an // indication that the set of installed fonts has changed, so // update all caches. mLastConfig = nullptr; // invalidates caches UpdateFontListInternal(true); return GetLangSupportEntry(aLang, aWithFonts); } } return entry; }