void NS_GetComplexLineBreaks(const char16_t* aText, uint32_t aLength, uint8_t* aBreakBefore) { NS_ASSERTION(aText, "aText shouldn't be null"); int outItems = 0; HRESULT result; AutoTArray<SCRIPT_ITEM, 64> items; char16ptr_t text = aText; memset(aBreakBefore, false, aLength); if (!items.AppendElements(64)) return; do { result = ScriptItemize(text, aLength, items.Length(), nullptr, nullptr, items.Elements(), &outItems); if (result == E_OUTOFMEMORY) { if (!items.AppendElements(items.Length())) return; } } while (result == E_OUTOFMEMORY); for (int iItem = 0; iItem < outItems; ++iItem) { uint32_t endOffset = (iItem + 1 == outItems ? aLength : items[iItem + 1].iCharPos); uint32_t startOffset = items[iItem].iCharPos; AutoTArray<SCRIPT_LOGATTR, 64> sla; if (!sla.AppendElements(endOffset - startOffset)) return; if (ScriptBreak(text + startOffset, endOffset - startOffset, &items[iItem].a, sla.Elements()) < 0) return; for (uint32_t j=0; j+startOffset < endOffset; ++j) { aBreakBefore[j+startOffset] = sla[j].fSoftBreak; } } }
void GMPDecryptorChild::KeyStatusChanged(const char* aSessionId, uint32_t aSessionIdLength, const uint8_t* aKeyId, uint32_t aKeyIdLength, GMPMediaKeyStatus aStatus) { AutoTArray<uint8_t, 16> kid; kid.AppendElements(aKeyId, aKeyIdLength); CALL_ON_GMP_THREAD(SendKeyStatusChanged, nsCString(aSessionId, aSessionIdLength), kid, aStatus); }
void NS_GetComplexLineBreaks(const char16_t* aText, uint32_t aLength, uint8_t* aBreakBefore) { NS_ASSERTION(aText, "aText shouldn't be null"); memset(aBreakBefore, false, aLength * sizeof(uint8_t)); AutoTArray<PangoLogAttr, 2000> attrBuffer; if (!attrBuffer.AppendElements(aLength + 1)) return; NS_ConvertUTF16toUTF8 aUTF8(aText, aLength); const gchar* p = aUTF8.Data(); const gchar* end = p + aUTF8.Length(); uint32_t u16Offset = 0; static PangoLanguage* language = pango_language_from_string("en"); while (p < end) { PangoLogAttr* attr = attrBuffer.Elements(); pango_get_log_attrs(p, end - p, -1, language, attr, attrBuffer.Length()); while (p < end) { aBreakBefore[u16Offset] = attr->is_line_break; if (NS_IS_LOW_SURROGATE(aText[u16Offset])) aBreakBefore[++u16Offset] = false; // Skip high surrogate ++u16Offset; bool err; uint32_t ch = UTF8CharEnumerator::NextChar(&p, end, &err); ++attr; if (ch == 0 || err) { // pango_break (pango 1.16.2) only analyses text before the // first NUL (but sets one extra attr). Workaround loop to call // pango_break again to analyse after the NUL is done somewhere else // (gfx/thebes/gfxFontconfigFonts.cpp: SetupClusterBoundaries()). // So, we do the same here for pango_get_log_attrs. break; } } } }
void RestyleTracker::DoProcessRestyles() { nsAutoCString docURL; if (profiler_is_active()) { nsIURI *uri = Document()->GetDocumentURI(); if (uri) { uri->GetSpec(docURL); } else { docURL = "N/A"; } } PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles", js::ProfileEntry::Category::CSS, "(%s)", docURL.get()); nsDocShell* docShell = static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell()); RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get(); bool isTimelineRecording = timelines && timelines->HasConsumer(docShell); // Create a AnimationsWithDestroyedFrame during restyling process to // stop animations and transitions on elements that have no frame at the end // of the restyling process. RestyleManager::AnimationsWithDestroyedFrame animationsWithDestroyedFrame(mRestyleManager); // Create a ReframingStyleContexts struct on the stack and put it in our // mReframingStyleContexts for almost all of the remaining scope of // this function. // // It needs to be *in* scope during BeginProcessingRestyles, which // might (if mDoRebuildAllStyleData is true) do substantial amounts of // restyle processing. // // However, it needs to be *out* of scope during // EndProcessingRestyles, since we should release the style contexts // it holds prior to any EndReconstruct call that // EndProcessingRestyles makes. This is because in EndReconstruct we // try to destroy the old rule tree using the GC mechanism, which // means it only gets destroyed if it's unreferenced (and if it's // referenced, we assert). So we want the ReframingStyleContexts // (which holds old style contexts) to be destroyed before the // EndReconstruct so those style contexts go away before // EndReconstruct. { RestyleManager::ReframingStyleContexts reframingStyleContexts(mRestyleManager); mRestyleManager->BeginProcessingRestyles(*this); LOG_RESTYLE("Processing %d pending %srestyles with %d restyle roots for %s", mPendingRestyles.Count(), mRestyleManager->PresContext()->TransitionManager()-> InAnimationOnlyStyleUpdate() ? (const char*) "animation " : (const char*) "", static_cast<int>(mRestyleRoots.Length()), GetDocumentURI(Document()).get()); LOG_RESTYLE_INDENT(); // loop so that we process any restyle events generated by processing while (mPendingRestyles.Count()) { if (mHaveLaterSiblingRestyles) { // Convert them to individual restyles on all the later siblings AutoTArray<RefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr; for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) { auto element = static_cast<dom::Element*>(iter.Key()); MOZ_ASSERT(!element->IsStyledByServo(), "Should not have Servo-styled elements here"); // Only collect the entries that actually need restyling by us (and // haven't, for example, already been restyled). // It's important to not mess with the flags on entries not in our // document. if (element->GetComposedDoc() == Document() && element->HasFlag(RestyleBit()) && (iter.Data()->mRestyleHint & eRestyle_LaterSiblings)) { laterSiblingArr.AppendElement(element); } } for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) { Element* element = laterSiblingArr[i]; MOZ_ASSERT(!element->IsStyledByServo()); for (nsIContent* sibling = element->GetNextSibling(); sibling; sibling = sibling->GetNextSibling()) { if (sibling->IsElement()) { LOG_RESTYLE("adding pending restyle for %s due to " "eRestyle_LaterSiblings hint on %s", FrameTagToString(sibling->AsElement()).get(), FrameTagToString(element->AsElement()).get()); if (AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree, nsChangeHint(0))) { // Nothing else to do here; we'll handle the following // siblings when we get to |sibling| in laterSiblingArr. break; } } } } // Now remove all those eRestyle_LaterSiblings bits for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) { Element* element = laterSiblingArr[i]; NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?"); RestyleData* data; #ifdef DEBUG bool found = #endif mPendingRestyles.Get(element, &data); NS_ASSERTION(found, "Where did our entry go?"); data->mRestyleHint = nsRestyleHint(data->mRestyleHint & ~eRestyle_LaterSiblings); } LOG_RESTYLE("%d pending restyles after expanding out " "eRestyle_LaterSiblings", mPendingRestyles.Count()); mHaveLaterSiblingRestyles = false; } uint32_t rootCount; while ((rootCount = mRestyleRoots.Length())) { // Make sure to pop the element off our restyle root array, so // that we can freely append to the array as we process this // element. RefPtr<Element> element; element.swap(mRestyleRoots[rootCount - 1]); mRestyleRoots.RemoveElementAt(rootCount - 1); LOG_RESTYLE("processing style root %s at index %d", FrameTagToString(element).get(), rootCount - 1); LOG_RESTYLE_INDENT(); // Do the document check before calling GetRestyleData, since we // don't want to do the sibling-processing GetRestyleData does if // the node is no longer relevant. if (element->GetComposedDoc() != Document()) { // Content node has been removed from our document; nothing else // to do here LOG_RESTYLE("skipping, no longer in the document"); continue; } nsAutoPtr<RestyleData> data; if (!GetRestyleData(element, data)) { LOG_RESTYLE("skipping, already restyled"); continue; } if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( data->mRestyleHint, MarkerTracingType::START))); } #if defined(MOZ_ENABLE_PROFILER_SPS) Maybe<GeckoProfilerTracingRAII> profilerRAII; if (profiler_feature_active("restyle")) { profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace)); } #endif ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint, data->mRestyleHintData); AddRestyleRootsIfAwaitingRestyle(data->mDescendants); if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( data->mRestyleHint, MarkerTracingType::END))); } } if (mHaveLaterSiblingRestyles) { // Keep processing restyles for now continue; } // Now we only have entries with change hints left. To be safe in // case of reentry from the handing of the change hint, use a // scratch array instead of calling out to ProcessOneRestyle while // enumerating the hashtable. Use the stack if we can, otherwise // fall back on heap-allocation. AutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr; RestyleEnumerateData* restylesToProcess = restyleArr.AppendElements(mPendingRestyles.Count()); if (restylesToProcess) { RestyleEnumerateData* restyle = restylesToProcess; #ifdef RESTYLE_LOGGING uint32_t count = 0; #endif for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) { auto element = static_cast<dom::Element*>(iter.Key()); RestyleTracker::RestyleData* data = iter.Data(); // Only collect the entries that actually need restyling by us (and // haven't, for example, already been restyled). // It's important to not mess with the flags on entries not in our // document. if (element->GetComposedDoc() != Document() || !element->HasFlag(RestyleBit())) { LOG_RESTYLE("skipping pending restyle %s, already restyled or no " "longer in the document", FrameTagToString(element).get()); continue; } NS_ASSERTION( !element->HasFlag(RootBit()) || // Maybe we're just not reachable via the frame tree? (element->GetFlattenedTreeParent() && (!element->GetFlattenedTreeParent()->GetPrimaryFrame() || element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf() || element->GetComposedDoc()->GetShell()->FrameManager() ->GetDisplayContentsStyleFor(element))) || // Or not reachable due to an async reinsert we have // pending? If so, we'll have a reframe hint around. // That incidentally makes it safe that we still have // the bit, since any descendants that didn't get added // to the roots list because we had the bits will be // completely restyled in a moment. (data->mChangeHint & nsChangeHint_ReconstructFrame), "Why did this not get handled while processing mRestyleRoots?"); // Unset the restyle bits now, so if they get readded later as we // process we won't clobber that adding of the bit. element->UnsetFlags(RestyleBit() | RootBit() | ConditionalDescendantsBit()); restyle->mElement = element; restyle->mRestyleHint = data->mRestyleHint; restyle->mChangeHint = data->mChangeHint; // We can move data since we'll be clearing mPendingRestyles after // we finish enumerating it. restyle->mRestyleHintData = Move(data->mRestyleHintData); #if defined(MOZ_ENABLE_PROFILER_SPS) restyle->mBacktrace = Move(data->mBacktrace); #endif #ifdef RESTYLE_LOGGING count++; #endif // Increment to the next slot in the array restyle++; } RestyleEnumerateData* lastRestyle = restyle; // Clear the hashtable now that we don't need it anymore mPendingRestyles.Clear(); #ifdef RESTYLE_LOGGING uint32_t index = 0; #endif for (RestyleEnumerateData* currentRestyle = restylesToProcess; currentRestyle != lastRestyle; ++currentRestyle) { LOG_RESTYLE("processing pending restyle %s at index %d/%d", FrameTagToString(currentRestyle->mElement).get(), index++, count); LOG_RESTYLE_INDENT(); #if defined(MOZ_ENABLE_PROFILER_SPS) Maybe<GeckoProfilerTracingRAII> profilerRAII; if (profiler_feature_active("restyle")) { profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace)); } #endif if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( currentRestyle->mRestyleHint, MarkerTracingType::START))); } ProcessOneRestyle(currentRestyle->mElement, currentRestyle->mRestyleHint, currentRestyle->mChangeHint, currentRestyle->mRestyleHintData); if (isTimelineRecording) { timelines->AddMarkerForDocShell(docShell, Move( MakeUnique<RestyleTimelineMarker>( currentRestyle->mRestyleHint, MarkerTracingType::END))); } } } } } // mPendingRestyles is now empty. mHaveSelectors = false; mRestyleManager->EndProcessingRestyles(); }
nsresult nsLineBreaker::FlushCurrentWord() { uint32_t length = mCurrentWord.Length(); AutoTArray<uint8_t,4000> breakState; if (!breakState.AppendElements(length)) return NS_ERROR_OUT_OF_MEMORY; nsTArray<bool> capitalizationState; if (!mCurrentWordContainsComplexChar) { // For break-strict set everything internal to "break", otherwise // to "no break"! memset(breakState.Elements(), mWordBreak == nsILineBreaker::kWordBreak_BreakAll ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE, length*sizeof(uint8_t)); } else { nsContentUtils::LineBreaker()-> GetJISx4051Breaks(mCurrentWord.Elements(), length, mWordBreak, breakState.Elements()); } bool autoHyphenate = mCurrentWordLanguage && !mCurrentWordContainsMixedLang; uint32_t i; for (i = 0; autoHyphenate && i < mTextItems.Length(); ++i) { TextItem* ti = &mTextItems[i]; if (!(ti->mFlags & BREAK_USE_AUTO_HYPHENATION)) { autoHyphenate = false; } } if (autoHyphenate) { RefPtr<nsHyphenator> hyphenator = nsHyphenationManager::Instance()->GetHyphenator(mCurrentWordLanguage); if (hyphenator) { FindHyphenationPoints(hyphenator, mCurrentWord.Elements(), mCurrentWord.Elements() + length, breakState.Elements()); } } uint32_t offset = 0; for (i = 0; i < mTextItems.Length(); ++i) { TextItem* ti = &mTextItems[i]; NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?"); if ((ti->mFlags & BREAK_SUPPRESS_INITIAL) && ti->mSinkOffset == 0) { breakState[offset] = gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } if (ti->mFlags & BREAK_SUPPRESS_INSIDE) { uint32_t exclude = ti->mSinkOffset == 0 ? 1 : 0; memset(breakState.Elements() + offset + exclude, gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE, (ti->mLength - exclude)*sizeof(uint8_t)); } // Don't set the break state for the first character of the word, because // it was already set correctly earlier and we don't know what the true // value should be. uint32_t skipSet = i == 0 ? 1 : 0; if (ti->mSink) { ti->mSink->SetBreaks(ti->mSinkOffset + skipSet, ti->mLength - skipSet, breakState.Elements() + offset + skipSet); if (ti->mFlags & BREAK_NEED_CAPITALIZATION) { if (capitalizationState.Length() == 0) { if (!capitalizationState.AppendElements(length)) return NS_ERROR_OUT_OF_MEMORY; memset(capitalizationState.Elements(), false, length*sizeof(bool)); SetupCapitalization(mCurrentWord.Elements(), length, capitalizationState.Elements()); } ti->mSink->SetCapitalization(ti->mSinkOffset, ti->mLength, capitalizationState.Elements() + offset); } } offset += ti->mLength; } mCurrentWord.Clear(); mTextItems.Clear(); mCurrentWordContainsComplexChar = false; mCurrentWordContainsMixedLang = false; mCurrentWordLanguage = nullptr; return NS_OK; }
nsresult nsLineBreaker::AppendText(nsIAtom* aHyphenationLanguage, const uint8_t* aText, uint32_t aLength, uint32_t aFlags, nsILineBreakSink* aSink) { NS_ASSERTION(aLength > 0, "Appending empty text..."); if (aFlags & (BREAK_NEED_CAPITALIZATION | BREAK_USE_AUTO_HYPHENATION)) { // Defer to the Unicode path if capitalization or hyphenation is required nsAutoString str; const char* cp = reinterpret_cast<const char*>(aText); CopyASCIItoUTF16(nsDependentCSubstring(cp, cp + aLength), str); return AppendText(aHyphenationLanguage, str.get(), aLength, aFlags, aSink); } uint32_t offset = 0; // Continue the current word if (mCurrentWord.Length() > 0) { NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set"); while (offset < aLength && !IsSpace(aText[offset])) { mCurrentWord.AppendElement(aText[offset]); if (!mCurrentWordContainsComplexChar && IsComplexASCIIChar(aText[offset])) { mCurrentWordContainsComplexChar = true; } ++offset; } if (offset > 0) { mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags)); } if (offset == aLength) { // We did not encounter whitespace so the word hasn't finished yet. return NS_OK; } // We encountered whitespace, so we're done with this word nsresult rv = FlushCurrentWord(); if (NS_FAILED(rv)) return rv; } AutoTArray<uint8_t,4000> breakState; if (aSink) { if (!breakState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; } uint32_t start = offset; bool noBreaksNeeded = !aSink || ((aFlags & NO_BREAKS_NEEDED_FLAGS) == NO_BREAKS_NEEDED_FLAGS && !mBreakHere && !mAfterBreakableSpace); if (noBreaksNeeded) { // Skip to the space before the last word, since either the break data // here is not needed, or no breaks are set in the sink and there cannot // be any breaks in this chunk; all we need is the context for the next // chunk (if any) offset = aLength; while (offset > start) { --offset; if (IsSpace(aText[offset])) break; } } uint32_t wordStart = offset; bool wordHasComplexChar = false; for (;;) { uint8_t ch = aText[offset]; bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); if (aSink) { // Consider word-break style. Since the break position of CJK scripts // will be set by nsILineBreaker, we don't consider CJK at this point. breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) || (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } mBreakHere = false; mAfterBreakableSpace = isBreakableSpace; if (isSpace) { if (offset > wordStart && wordHasComplexChar) { if (aSink && !(aFlags & BREAK_SUPPRESS_INSIDE)) { // Save current start-of-word state because GetJISx4051Breaks will // set it to false uint8_t currentStart = breakState[wordStart]; nsContentUtils::LineBreaker()-> GetJISx4051Breaks(aText + wordStart, offset - wordStart, mWordBreak, breakState.Elements() + wordStart); breakState[wordStart] = currentStart; } wordHasComplexChar = false; } ++offset; if (offset >= aLength) break; wordStart = offset; } else { if (!wordHasComplexChar && IsComplexASCIIChar(ch)) { wordHasComplexChar = true; } ++offset; if (offset >= aLength) { // Save this word mCurrentWordContainsComplexChar = wordHasComplexChar; uint32_t len = offset - wordStart; char16_t* elems = mCurrentWord.AppendElements(len); if (!elems) return NS_ERROR_OUT_OF_MEMORY; uint32_t i; for (i = wordStart; i < offset; ++i) { elems[i - wordStart] = aText[i]; } mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags)); // Ensure that the break-before for this word is written out offset = wordStart + 1; break; } } } if (!noBreaksNeeded) { aSink->SetBreaks(start, offset - start, breakState.Elements() + start); } return NS_OK; }
nsresult nsLineBreaker::AppendText(nsIAtom* aHyphenationLanguage, const char16_t* aText, uint32_t aLength, uint32_t aFlags, nsILineBreakSink* aSink) { NS_ASSERTION(aLength > 0, "Appending empty text..."); uint32_t offset = 0; // Continue the current word if (mCurrentWord.Length() > 0) { NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set"); while (offset < aLength && !IsSpace(aText[offset])) { mCurrentWord.AppendElement(aText[offset]); if (!mCurrentWordContainsComplexChar && IsComplexChar(aText[offset])) { mCurrentWordContainsComplexChar = true; } UpdateCurrentWordLanguage(aHyphenationLanguage); ++offset; } if (offset > 0) { mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags)); } if (offset == aLength) return NS_OK; // We encountered whitespace, so we're done with this word nsresult rv = FlushCurrentWord(); if (NS_FAILED(rv)) return rv; } AutoTArray<uint8_t,4000> breakState; if (aSink) { if (!breakState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; } bool noCapitalizationNeeded = true; nsTArray<bool> capitalizationState; if (aSink && (aFlags & BREAK_NEED_CAPITALIZATION)) { if (!capitalizationState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; memset(capitalizationState.Elements(), false, aLength*sizeof(bool)); noCapitalizationNeeded = false; } uint32_t start = offset; bool noBreaksNeeded = !aSink || ((aFlags & NO_BREAKS_NEEDED_FLAGS) == NO_BREAKS_NEEDED_FLAGS && !mBreakHere && !mAfterBreakableSpace); if (noBreaksNeeded && noCapitalizationNeeded) { // Skip to the space before the last word, since either the break data // here is not needed, or no breaks are set in the sink and there cannot // be any breaks in this chunk; and we don't need to do word-initial // capitalization. All we need is the context for the next chunk (if any). offset = aLength; while (offset > start) { --offset; if (IsSpace(aText[offset])) break; } } uint32_t wordStart = offset; bool wordHasComplexChar = false; RefPtr<nsHyphenator> hyphenator; if ((aFlags & BREAK_USE_AUTO_HYPHENATION) && !(aFlags & BREAK_SUPPRESS_INSIDE) && aHyphenationLanguage) { hyphenator = nsHyphenationManager::Instance()->GetHyphenator(aHyphenationLanguage); } for (;;) { char16_t ch = aText[offset]; bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); if (aSink && !noBreaksNeeded) { breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) || (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } mBreakHere = false; mAfterBreakableSpace = isBreakableSpace; if (isSpace) { if (offset > wordStart && aSink) { if (!(aFlags & BREAK_SUPPRESS_INSIDE)) { if (wordHasComplexChar) { // Save current start-of-word state because GetJISx4051Breaks will // set it to false uint8_t currentStart = breakState[wordStart]; nsContentUtils::LineBreaker()-> GetJISx4051Breaks(aText + wordStart, offset - wordStart, mWordBreak, breakState.Elements() + wordStart); breakState[wordStart] = currentStart; } if (hyphenator) { FindHyphenationPoints(hyphenator, aText + wordStart, aText + offset, breakState.Elements() + wordStart); } } if (!noCapitalizationNeeded) { SetupCapitalization(aText + wordStart, offset - wordStart, capitalizationState.Elements() + wordStart); } } wordHasComplexChar = false; ++offset; if (offset >= aLength) break; wordStart = offset; } else { if (!wordHasComplexChar && IsComplexChar(ch)) { wordHasComplexChar = true; } ++offset; if (offset >= aLength) { // Save this word mCurrentWordContainsComplexChar = wordHasComplexChar; uint32_t len = offset - wordStart; char16_t* elems = mCurrentWord.AppendElements(len); if (!elems) return NS_ERROR_OUT_OF_MEMORY; memcpy(elems, aText + wordStart, sizeof(char16_t)*len); mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags)); // Ensure that the break-before for this word is written out offset = wordStart + 1; UpdateCurrentWordLanguage(aHyphenationLanguage); break; } } } if (aSink) { if (!noBreaksNeeded) { aSink->SetBreaks(start, offset - start, breakState.Elements() + start); } if (!noCapitalizationNeeded) { aSink->SetCapitalization(start, offset - start, capitalizationState.Elements() + start); } } return NS_OK; }