Bug 619521 - Part 1: Send a notification of any scripts for which font coverage is lacking. r=jdaggett

This commit is contained in:
Jonathan Kew 2014-12-22 16:35:54 +00:00
parent 1d3e35e67b
commit 65c4ca0e3f
14 changed files with 254 additions and 49 deletions

View File

@ -3252,6 +3252,22 @@ CanvasRenderingContext2D::GetHitRegionRect(Element* aElement, nsRect& aRect)
*/ */
struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
{ {
CanvasBidiProcessor()
: nsBidiPresUtils::BidiProcessor()
{
if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
mMissingFonts = new gfxMissingFontRecorder();
}
}
~CanvasBidiProcessor()
{
// notify front-end code if we encountered missing glyphs in any script
if (mMissingFonts) {
mMissingFonts->Flush();
}
}
typedef CanvasRenderingContext2D::ContextState ContextState; typedef CanvasRenderingContext2D::ContextState ContextState;
virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction) virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction)
@ -3268,7 +3284,8 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
length, length,
mThebes, mThebes,
mAppUnitsPerDevPixel, mAppUnitsPerDevPixel,
flags); flags,
mMissingFonts);
} }
virtual nscoord GetWidth() virtual nscoord GetWidth()
@ -3514,6 +3531,10 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcess
// current font // current font
gfxFontGroup* mFontgrp; gfxFontGroup* mFontgrp;
// to record any unsupported characters found in the text,
// and notify front-end if it is interested
nsAutoPtr<gfxMissingFontRecorder> mMissingFonts;
// dev pixel conversion factor // dev pixel conversion factor
int32_t mAppUnitsPerDevPixel; int32_t mAppUnitsPerDevPixel;

View File

@ -33,7 +33,8 @@ public:
reinterpret_cast<const uint8_t*>(aString), aLength, reinterpret_cast<const uint8_t*>(aString), aLength,
aRC->ThebesContext(), aRC->ThebesContext(),
aMetrics->AppUnitsPerDevPixel(), aMetrics->AppUnitsPerDevPixel(),
ComputeFlags(aMetrics)); ComputeFlags(aMetrics),
nullptr);
} }
AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC, AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
@ -43,7 +44,8 @@ public:
aString, aLength, aString, aLength,
aRC->ThebesContext(), aRC->ThebesContext(),
aMetrics->AppUnitsPerDevPixel(), aMetrics->AppUnitsPerDevPixel(),
ComputeFlags(aMetrics)); ComputeFlags(aMetrics),
nullptr);
} }
gfxTextRun *get() { return mTextRun; } gfxTextRun *get() { return mTextRun; }

View File

@ -28,6 +28,7 @@
using namespace mozilla; using namespace mozilla;
using namespace mozilla::gfx; using namespace mozilla::gfx;
using namespace mozilla::unicode; using namespace mozilla::unicode;
using mozilla::services::GetObserverService;
static const char16_t kEllipsisChar[] = { 0x2026, 0x0 }; static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 }; static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
@ -2039,12 +2040,12 @@ gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
gfxFont *font = GetFirstValidFont(uint32_t(hyphen)); gfxFont *font = GetFirstValidFont(uint32_t(hyphen));
if (font->HasCharacter(hyphen)) { if (font->HasCharacter(hyphen)) {
return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit, return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
gfxFontGroup::TEXT_IS_PERSISTENT); gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
} }
static const uint8_t dash = '-'; static const uint8_t dash = '-';
return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit, return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
gfxFontGroup::TEXT_IS_PERSISTENT); gfxFontGroup::TEXT_IS_PERSISTENT, nullptr);
} }
gfxFloat gfxFloat
@ -2065,7 +2066,8 @@ gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
gfxTextRun * gfxTextRun *
gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength, gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
const Parameters *aParams, uint32_t aFlags) const Parameters *aParams, uint32_t aFlags,
gfxMissingFontRecorder *aMFR)
{ {
if (aLength == 0) { if (aLength == 0) {
return MakeEmptyTextRun(aParams, aFlags); return MakeEmptyTextRun(aParams, aFlags);
@ -2089,7 +2091,7 @@ gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
return nullptr; return nullptr;
} }
InitTextRun(aParams->mContext, textRun, aString, aLength); InitTextRun(aParams->mContext, textRun, aString, aLength, aMFR);
textRun->FetchGlyphExtents(aParams->mContext); textRun->FetchGlyphExtents(aParams->mContext);
@ -2098,7 +2100,8 @@ gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
gfxTextRun * gfxTextRun *
gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength, gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
const Parameters *aParams, uint32_t aFlags) const Parameters *aParams, uint32_t aFlags,
gfxMissingFontRecorder *aMFR)
{ {
if (aLength == 0) { if (aLength == 0) {
return MakeEmptyTextRun(aParams, aFlags); return MakeEmptyTextRun(aParams, aFlags);
@ -2116,7 +2119,7 @@ gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
return nullptr; return nullptr;
} }
InitTextRun(aParams->mContext, textRun, aString, aLength); InitTextRun(aParams->mContext, textRun, aString, aLength, aMFR);
textRun->FetchGlyphExtents(aParams->mContext); textRun->FetchGlyphExtents(aParams->mContext);
@ -2128,7 +2131,8 @@ void
gfxFontGroup::InitTextRun(gfxContext *aContext, gfxFontGroup::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun, gfxTextRun *aTextRun,
const T *aString, const T *aString,
uint32_t aLength) uint32_t aLength,
gfxMissingFontRecorder *aMFR)
{ {
NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run"); NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
@ -2208,7 +2212,7 @@ gfxFontGroup::InitTextRun(gfxContext *aContext,
// the text is still purely 8-bit; bypass the script-run itemizer // the text is still purely 8-bit; bypass the script-run itemizer
// and treat it as a single Latin run // and treat it as a single Latin run
InitScriptRun(aContext, aTextRun, aString, InitScriptRun(aContext, aTextRun, aString,
0, aLength, MOZ_SCRIPT_LATIN); 0, aLength, MOZ_SCRIPT_LATIN, aMFR);
} else { } else {
const char16_t *textPtr; const char16_t *textPtr;
if (transformedString) { if (transformedString) {
@ -2256,7 +2260,7 @@ gfxFontGroup::InitTextRun(gfxContext *aContext,
#endif #endif
InitScriptRun(aContext, aTextRun, textPtr + runStart, InitScriptRun(aContext, aTextRun, textPtr + runStart,
runStart, runLimit - runStart, runScript); runStart, runLimit - runStart, runScript, aMFR);
} }
} }
@ -2292,6 +2296,14 @@ gfxFontGroup::InitTextRun(gfxContext *aContext,
aTextRun->SortGlyphRuns(); aTextRun->SortGlyphRuns();
} }
static inline bool
IsPUA(uint32_t aUSV)
{
// We could look up the General Category of the codepoint here,
// but it's simpler to check PUA codepoint ranges.
return (aUSV >= 0xE000 && aUSV <= 0xF8FF) || (aUSV >= 0xF0000);
}
template<typename T> template<typename T>
void void
gfxFontGroup::InitScriptRun(gfxContext *aContext, gfxFontGroup::InitScriptRun(gfxContext *aContext,
@ -2301,7 +2313,8 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
uint32_t aOffset, // position of the script run uint32_t aOffset, // position of the script run
// within the textrun // within the textrun
uint32_t aLength, // length of the script run uint32_t aLength, // length of the script run
int32_t aRunScript) int32_t aRunScript,
gfxMissingFontRecorder *aMFR)
{ {
NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run"); NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted, NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
@ -2322,6 +2335,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
ComputeRanges(fontRanges, aString, aLength, aRunScript, ComputeRanges(fontRanges, aString, aLength, aRunScript,
aTextRun->GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK); aTextRun->GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK);
uint32_t numRanges = fontRanges.Length(); uint32_t numRanges = fontRanges.Length();
bool missingChars = false;
for (uint32_t r = 0; r < numRanges; r++) { for (uint32_t r = 0; r < numRanges; r++) {
const gfxTextRange& range = fontRanges[r]; const gfxTextRange& range = fontRanges[r];
@ -2468,11 +2482,15 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
index + 1 < aLength && index + 1 < aLength &&
NS_IS_LOW_SURROGATE(aString[index + 1])) NS_IS_LOW_SURROGATE(aString[index + 1]))
{ {
uint32_t usv =
SURROGATE_TO_UCS4(ch, aString[index + 1]);
aTextRun->SetMissingGlyph(aOffset + index, aTextRun->SetMissingGlyph(aOffset + index,
SURROGATE_TO_UCS4(ch, usv,
aString[index + 1]),
mainFont); mainFont);
index++; index++;
if (!mSkipDrawing && !IsPUA(usv)) {
missingChars = true;
}
continue; continue;
} }
@ -2507,11 +2525,18 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
// record char code so we can draw a box with the Unicode value // record char code so we can draw a box with the Unicode value
aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont); aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
if (!mSkipDrawing && !IsPUA(ch)) {
missingChars = true;
}
} }
} }
runStart += matchedLength; runStart += matchedLength;
} }
if (aMFR && missingChars) {
aMFR->RecordScript(aRunScript);
}
} }
gfxTextRun * gfxTextRun *
@ -2537,7 +2562,8 @@ gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
}; };
gfxTextRun* textRun = gfxTextRun* textRun =
MakeTextRun(ellipsis.get(), ellipsis.Length(), &params, TEXT_IS_PERSISTENT); MakeTextRun(ellipsis.get(), ellipsis.Length(), &params,
TEXT_IS_PERSISTENT, nullptr);
if (!textRun) { if (!textRun) {
return nullptr; return nullptr;
} }
@ -3109,3 +3135,41 @@ gfxFontGroup::Shutdown()
} }
nsILanguageAtomService* gfxFontGroup::gLangService = nullptr; nsILanguageAtomService* gfxFontGroup::gLangService = nullptr;
void
gfxMissingFontRecorder::Flush()
{
static bool mNotifiedFontsInitialized = false;
static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords];
if (!mNotifiedFontsInitialized) {
memset(&mNotifiedFonts, 0, sizeof(mNotifiedFonts));
mNotifiedFontsInitialized = true;
}
nsAutoString fontNeeded;
for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) {
mMissingFonts[i] &= ~mNotifiedFonts[i];
if (!mMissingFonts[i]) {
continue;
}
for (uint32_t j = 0; j < 32; ++j) {
if (!(mMissingFonts[i] & (1 << j))) {
continue;
}
mNotifiedFonts[i] |= (1 << j);
if (!fontNeeded.IsEmpty()) {
fontNeeded.Append(PRUnichar(','));
}
uint32_t tag = GetScriptTagForCode(i * 32 + j);
fontNeeded.Append(char16_t(tag >> 24));
fontNeeded.Append(char16_t((tag >> 16) & 0xff));
fontNeeded.Append(char16_t((tag >> 8) & 0xff));
fontNeeded.Append(char16_t(tag & 0xff));
}
mMissingFonts[i] = 0;
}
if (!fontNeeded.IsEmpty()) {
nsCOMPtr<nsIObserverService> service = GetObserverService();
service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
}
}

View File

@ -16,6 +16,7 @@
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "DrawMode.h" #include "DrawMode.h"
#include "harfbuzz/hb.h" #include "harfbuzz/hb.h"
#include "nsUnicodeScriptCodes.h"
#ifdef DEBUG #ifdef DEBUG
#include <stdio.h> #include <stdio.h>
@ -27,6 +28,7 @@ class gfxUserFontSet;
class gfxTextContextPaint; class gfxTextContextPaint;
class nsIAtom; class nsIAtom;
class nsILanguageAtomService; class nsILanguageAtomService;
class gfxMissingFontRecorder;
/** /**
* Callback for Draw() to use when drawing text with mode * Callback for Draw() to use when drawing text with mode
@ -769,7 +771,8 @@ public:
* This calls FetchGlyphExtents on the textrun. * This calls FetchGlyphExtents on the textrun.
*/ */
virtual gfxTextRun *MakeTextRun(const char16_t *aString, uint32_t aLength, virtual gfxTextRun *MakeTextRun(const char16_t *aString, uint32_t aLength,
const Parameters *aParams, uint32_t aFlags); const Parameters *aParams, uint32_t aFlags,
gfxMissingFontRecorder *aMFR);
/** /**
* Make a textrun for a given string. * Make a textrun for a given string.
* If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
@ -777,7 +780,8 @@ public:
* This calls FetchGlyphExtents on the textrun. * This calls FetchGlyphExtents on the textrun.
*/ */
virtual gfxTextRun *MakeTextRun(const uint8_t *aString, uint32_t aLength, virtual gfxTextRun *MakeTextRun(const uint8_t *aString, uint32_t aLength,
const Parameters *aParams, uint32_t aFlags); const Parameters *aParams, uint32_t aFlags,
gfxMissingFontRecorder *aMFR);
/** /**
* Textrun creation helper for clients that don't want to pass * Textrun creation helper for clients that don't want to pass
@ -787,12 +791,13 @@ public:
gfxTextRun *MakeTextRun(const T *aString, uint32_t aLength, gfxTextRun *MakeTextRun(const T *aString, uint32_t aLength,
gfxContext *aRefContext, gfxContext *aRefContext,
int32_t aAppUnitsPerDevUnit, int32_t aAppUnitsPerDevUnit,
uint32_t aFlags) uint32_t aFlags,
gfxMissingFontRecorder *aMFR)
{ {
gfxTextRunFactory::Parameters params = { gfxTextRunFactory::Parameters params = {
aRefContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit aRefContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
}; };
return MakeTextRun(aString, aLength, &params, aFlags); return MakeTextRun(aString, aLength, &params, aFlags, aMFR);
} }
/** /**
@ -1088,7 +1093,8 @@ protected:
void InitTextRun(gfxContext *aContext, void InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun, gfxTextRun *aTextRun,
const T *aString, const T *aString,
uint32_t aLength); uint32_t aLength,
gfxMissingFontRecorder *aMFR);
// InitTextRun helper to handle a single script run, by finding font ranges // InitTextRun helper to handle a single script run, by finding font ranges
// and calling each font's InitTextRun() as appropriate // and calling each font's InitTextRun() as appropriate
@ -1098,7 +1104,8 @@ protected:
const T *aString, const T *aString,
uint32_t aScriptRunStart, uint32_t aScriptRunStart,
uint32_t aScriptRunEnd, uint32_t aScriptRunEnd,
int32_t aRunScript); int32_t aRunScript,
gfxMissingFontRecorder *aMFR);
// Helper for font-matching: // Helper for font-matching:
// When matching the italic case, allow use of the regular face // When matching the italic case, allow use of the regular face
@ -1124,4 +1131,56 @@ protected:
static nsILanguageAtomService* gLangService; static nsILanguageAtomService* gLangService;
}; };
// A "missing font recorder" is to be used during text-run creation to keep
// a record of any scripts encountered for which font coverage was lacking;
// when Flush() is called, it sends a notification that front-end code can use
// to download fonts on demand (or whatever else it wants to do).
#define GFX_MISSING_FONTS_NOTIFY_PREF "gfx.missing_fonts.notify"
class gfxMissingFontRecorder {
public:
gfxMissingFontRecorder()
{
MOZ_COUNT_CTOR(gfxMissingFontRecorder);
memset(&mMissingFonts, 0, sizeof(mMissingFonts));
}
~gfxMissingFontRecorder()
{
#ifdef DEBUG
for (uint32_t i = 0; i < kNumScriptBitsWords; i++) {
NS_ASSERTION(mMissingFonts[i] == 0,
"failed to flush the missing-font recorder");
}
#endif
MOZ_COUNT_DTOR(gfxMissingFontRecorder);
}
// record this script code in our mMissingFonts bitset
void RecordScript(int32_t aScriptCode)
{
mMissingFonts[uint32_t(aScriptCode) >> 5] |=
(1 << (uint32_t(aScriptCode) & 0x1f));
}
// send a notification of any missing-scripts that have been
// recorded, and clear the mMissingFonts set for re-use
void Flush();
// forget any missing-scripts that have been recorded up to now;
// called before discarding a recorder we no longer care about
void Clear()
{
memset(&mMissingFonts, 0, sizeof(mMissingFonts));
}
private:
// Number of 32-bit words needed for the missing-script flags
static const uint32_t kNumScriptBitsWords =
((MOZ_NUM_SCRIPT_CODES + 31) / 32);
uint32_t mMissingFonts[kNumScriptBitsWords];
};
#endif #endif

View File

@ -64,6 +64,7 @@
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsPIWindowRoot.h" #include "nsPIWindowRoot.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "gfxTextRun.h"
// Needed for Start/Stop of Image Animation // Needed for Start/Stop of Image Animation
#include "imgIContainer.h" #include "imgIContainer.h"
@ -249,6 +250,10 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
if (log && log->level >= PR_LOG_WARNING) { if (log && log->level >= PR_LOG_WARNING) {
mTextPerf = new gfxTextPerfMetrics(); mTextPerf = new gfxTextPerfMetrics();
} }
if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
mMissingFonts = new gfxMissingFontRecorder();
}
} }
void void
@ -341,6 +346,9 @@ nsPresContext::LastRelease()
if (IsRoot()) { if (IsRoot()) {
static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer(); static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
} }
if (mMissingFonts) {
mMissingFonts->Clear();
}
} }
NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext) NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
@ -873,6 +881,20 @@ nsPresContext::PreferenceChanged(const char* aPrefName)
} }
return; return;
} }
if (prefName.EqualsLiteral(GFX_MISSING_FONTS_NOTIFY_PREF)) {
if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
if (!mMissingFonts) {
mMissingFonts = new gfxMissingFontRecorder();
// trigger reflow to detect missing fonts on the current page
mPrefChangePendingNeedsReflow = true;
}
} else {
if (mMissingFonts) {
mMissingFonts->Clear();
}
mMissingFonts = nullptr;
}
}
if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font."))) { if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font."))) {
// Changes to font family preferences don't change anything in the // Changes to font family preferences don't change anything in the
// computed style data, so the style system won't generate a reflow // computed style data, so the style system won't generate a reflow
@ -2194,6 +2216,14 @@ nsPresContext::RebuildCounterStyles()
} }
} }
void
nsPresContext::NotifyMissingFonts()
{
if (mMissingFonts) {
mMissingFonts->Flush();
}
}
void void
nsPresContext::EnsureSafeToHandOutCSSRules() nsPresContext::EnsureSafeToHandOutCSSRules()
{ {

View File

@ -65,6 +65,7 @@ class nsAnimationManager;
class nsRefreshDriver; class nsRefreshDriver;
class nsIWidget; class nsIWidget;
class nsDeviceContext; class nsDeviceContext;
class gfxMissingFontRecorder;
namespace mozilla { namespace mozilla {
class EventStateManager; class EventStateManager;
@ -876,6 +877,9 @@ public:
// user font set is changed and fonts become unavailable). // user font set is changed and fonts become unavailable).
void UserFontSetUpdated(); void UserFontSetUpdated();
gfxMissingFontRecorder *MissingFontRecorder() { return mMissingFonts; }
void NotifyMissingFonts();
mozilla::dom::FontFaceSet* Fonts(); mozilla::dom::FontFaceSet* Fonts();
void FlushCounterStyles(); void FlushCounterStyles();
@ -1251,6 +1255,8 @@ protected:
// text performance metrics // text performance metrics
nsAutoPtr<gfxTextPerfMetrics> mTextPerf; nsAutoPtr<gfxTextPerfMetrics> mTextPerf;
nsAutoPtr<gfxMissingFontRecorder> mMissingFonts;
nsRect mVisibleArea; nsRect mVisibleArea;
nsSize mPageSize; nsSize mPageSize;
float mPageScale; float mPageScale;

View File

@ -9119,6 +9119,8 @@ PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted)
mTouchCaret->UpdatePositionIfNeeded(); mTouchCaret->UpdatePositionIfNeeded();
} }
mPresContext->NotifyMissingFonts();
if (!aWasInterrupted) { if (!aWasInterrupted) {
ClearReflowOnZoomPending(); ClearReflowOnZoomPending();
} }

View File

@ -529,7 +529,8 @@ MathVariant(uint32_t aCh, uint8_t aMathVar)
void void
MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
gfxContext* aRefContext) gfxContext* aRefContext,
gfxMissingFontRecorder* aMFR)
{ {
gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
@ -766,7 +767,7 @@ MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
} else { } else {
cachedChild = newFontGroup->MakeTextRun( cachedChild = newFontGroup->MakeTextRun(
convertedString.BeginReading(), convertedString.Length(), convertedString.BeginReading(), convertedString.Length(),
&innerParams, flags); &innerParams, flags, aMFR);
child = cachedChild.get(); child = cachedChild.get();
} }
if (!child) if (!child)
@ -778,7 +779,7 @@ MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
canBreakBeforeArray.Elements(), aRefContext); canBreakBeforeArray.Elements(), aRefContext);
if (transformedChild) { if (transformedChild) {
transformedChild->FinishSettingProperties(aRefContext); transformedChild->FinishSettingProperties(aRefContext, aMFR);
} }
if (mergeNeeded) { if (mergeNeeded) {

View File

@ -22,7 +22,8 @@ public:
mSSTYScriptLevel(aSSTYScriptLevel) {} mSSTYScriptLevel(aSSTYScriptLevel) {}
virtual void RebuildTextRun(nsTransformedTextRun* aTextRun, virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
gfxContext* aRefContext) MOZ_OVERRIDE; gfxContext* aRefContext,
gfxMissingFontRecorder* aMFR) MOZ_OVERRIDE;
enum { enum {
// Style effects which may override single character <mi> behaviour // Style effects which may override single character <mi> behaviour
MATH_FONT_STYLING_NORMAL = 0x1, // fontstyle="normal" has been set. MATH_FONT_STYLING_NORMAL = 0x1, // fontstyle="normal" has been set.

View File

@ -561,10 +561,11 @@ template<typename T>
gfxTextRun * gfxTextRun *
MakeTextRun(const T *aText, uint32_t aLength, MakeTextRun(const T *aText, uint32_t aLength,
gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams, gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams,
uint32_t aFlags) uint32_t aFlags, gfxMissingFontRecorder *aMFR)
{ {
nsAutoPtr<gfxTextRun> textRun(aFontGroup->MakeTextRun(aText, aLength, nsAutoPtr<gfxTextRun> textRun(aFontGroup->MakeTextRun(aText, aLength,
aParams, aFlags)); aParams, aFlags,
aMFR));
if (!textRun) { if (!textRun) {
return nullptr; return nullptr;
} }
@ -842,6 +843,7 @@ public:
mCurrentFramesAllSameTextRun(nullptr), mCurrentFramesAllSameTextRun(nullptr),
mContext(aContext), mContext(aContext),
mLineContainer(aLineContainer), mLineContainer(aLineContainer),
mMissingFonts(aPresContext->MissingFontRecorder()),
mBidiEnabled(aPresContext->BidiEnabled()), mBidiEnabled(aPresContext->BidiEnabled()),
mSkipIncompleteTextRuns(false), mSkipIncompleteTextRuns(false),
mWhichTextRun(aWhichTextRun), mWhichTextRun(aWhichTextRun),
@ -971,7 +973,7 @@ public:
aCapitalize, mContext); aCapitalize, mContext);
} }
void Finish() { void Finish(gfxMissingFontRecorder* aMFR) {
NS_ASSERTION(!(mTextRun->GetFlags() & NS_ASSERTION(!(mTextRun->GetFlags() &
(gfxTextRunFactory::TEXT_UNUSED_FLAGS | (gfxTextRunFactory::TEXT_UNUSED_FLAGS |
nsTextFrameUtils::TEXT_UNUSED_FLAG)), nsTextFrameUtils::TEXT_UNUSED_FLAG)),
@ -979,7 +981,7 @@ public:
if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) { if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
nsTransformedTextRun* transformedTextRun = nsTransformedTextRun* transformedTextRun =
static_cast<nsTransformedTextRun*>(mTextRun); static_cast<nsTransformedTextRun*>(mTextRun);
transformedTextRun->FinishSettingProperties(mContext); transformedTextRun->FinishSettingProperties(mContext, aMFR);
} }
// The way nsTransformedTextRun is implemented, its glyph runs aren't // The way nsTransformedTextRun is implemented, its glyph runs aren't
// available until after nsTransformedTextRun::FinishSettingProperties() // available until after nsTransformedTextRun::FinishSettingProperties()
@ -1007,6 +1009,7 @@ private:
// The common ancestor of the current frame and the previous leaf frame // The common ancestor of the current frame and the previous leaf frame
// on the line, or null if there was no previous leaf frame. // on the line, or null if there was no previous leaf frame.
nsIFrame* mCommonAncestorWithLastFrame; nsIFrame* mCommonAncestorWithLastFrame;
gfxMissingFontRecorder* mMissingFonts;
// mMaxTextLength is an upper bound on the size of the text in all mapped frames // mMaxTextLength is an upper bound on the size of the text in all mapped frames
// The value UINT32_MAX represents overflow; text will be discarded // The value UINT32_MAX represents overflow; text will be discarded
uint32_t mMaxTextLength; uint32_t mMaxTextLength;
@ -1506,7 +1509,7 @@ void BuildTextRunsScanner::FlushLineBreaks(gfxTextRun* aTrailingTextRun)
// TODO cause frames associated with the textrun to be reflowed, if they // TODO cause frames associated with the textrun to be reflowed, if they
// aren't being reflowed already! // aren't being reflowed already!
} }
mBreakSinks[i]->Finish(); mBreakSinks[i]->Finish(mMissingFonts);
} }
mBreakSinks.Clear(); mBreakSinks.Clear();
@ -2138,27 +2141,31 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
if (mDoubleByteText) { if (mDoubleByteText) {
const char16_t* text = static_cast<const char16_t*>(textPtr); const char16_t* text = static_cast<const char16_t*>(textPtr);
if (transformingFactory) { if (transformingFactory) {
textRun = transformingFactory->MakeTextRun(text, transformedLength, &params, textRun = transformingFactory->MakeTextRun(text, transformedLength,
fontGroup, textFlags, styles.Elements()); &params, fontGroup, textFlags,
styles.Elements(), true);
if (textRun) { if (textRun) {
// ownership of the factory has passed to the textrun // ownership of the factory has passed to the textrun
transformingFactory.forget(); transformingFactory.forget();
} }
} else { } else {
textRun = MakeTextRun(text, transformedLength, fontGroup, &params, textFlags); textRun = MakeTextRun(text, transformedLength, fontGroup, &params,
textFlags, mMissingFonts);
} }
} else { } else {
const uint8_t* text = static_cast<const uint8_t*>(textPtr); const uint8_t* text = static_cast<const uint8_t*>(textPtr);
textFlags |= gfxFontGroup::TEXT_IS_8BIT; textFlags |= gfxFontGroup::TEXT_IS_8BIT;
if (transformingFactory) { if (transformingFactory) {
textRun = transformingFactory->MakeTextRun(text, transformedLength, &params, textRun = transformingFactory->MakeTextRun(text, transformedLength,
fontGroup, textFlags, styles.Elements()); &params, fontGroup, textFlags,
styles.Elements(), true);
if (textRun) { if (textRun) {
// ownership of the factory has passed to the textrun // ownership of the factory has passed to the textrun
transformingFactory.forget(); transformingFactory.forget();
} }
} else { } else {
textRun = MakeTextRun(text, transformedLength, fontGroup, &params, textFlags); textRun = MakeTextRun(text, transformedLength, fontGroup, &params,
textFlags, mMissingFonts);
} }
} }
if (!textRun) { if (!textRun) {

View File

@ -596,7 +596,7 @@ nsCaseTransformTextRunFactory::TransformString(
void void
nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
gfxContext* aRefContext) gfxContext* aRefContext, gfxMissingFontRecorder *aMFR)
{ {
nsAutoString convertedString; nsAutoString convertedString;
nsAutoTArray<bool,50> charsToMergeArray; nsAutoTArray<bool,50> charsToMergeArray;
@ -631,7 +631,7 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
} else { } else {
cachedChild = fontGroup->MakeTextRun( cachedChild = fontGroup->MakeTextRun(
convertedString.BeginReading(), convertedString.Length(), convertedString.BeginReading(), convertedString.Length(),
&innerParams, flags); &innerParams, flags, aMFR);
child = cachedChild.get(); child = cachedChild.get();
} }
if (!child) if (!child)
@ -643,7 +643,7 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
canBreakBeforeArray.Elements(), aRefContext); canBreakBeforeArray.Elements(), aRefContext);
if (transformedChild) { if (transformedChild) {
transformedChild->FinishSettingProperties(aRefContext); transformedChild->FinishSettingProperties(aRefContext, aMFR);
} }
if (mergeNeeded) { if (mergeNeeded) {

View File

@ -21,13 +21,17 @@ public:
nsTransformedTextRun* MakeTextRun(const uint8_t* aString, uint32_t aLength, nsTransformedTextRun* MakeTextRun(const uint8_t* aString, uint32_t aLength,
const gfxFontGroup::Parameters* aParams, const gfxFontGroup::Parameters* aParams,
gfxFontGroup* aFontGroup, uint32_t aFlags, gfxFontGroup* aFontGroup, uint32_t aFlags,
nsStyleContext** aStyles, bool aOwnsFactory = true); nsStyleContext** aStyles,
bool aOwnsFactory);
nsTransformedTextRun* MakeTextRun(const char16_t* aString, uint32_t aLength, nsTransformedTextRun* MakeTextRun(const char16_t* aString, uint32_t aLength,
const gfxFontGroup::Parameters* aParams, const gfxFontGroup::Parameters* aParams,
gfxFontGroup* aFontGroup, uint32_t aFlags, gfxFontGroup* aFontGroup, uint32_t aFlags,
nsStyleContext** aStyles, bool aOwnsFactory = true); nsStyleContext** aStyles,
bool aOwnsFactory);
virtual void RebuildTextRun(nsTransformedTextRun* aTextRun, gfxContext* aRefContext) = 0; virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
gfxContext* aRefContext,
gfxMissingFontRecorder* aMFR) = 0;
}; };
/** /**
@ -48,7 +52,9 @@ public:
: mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory), : mInnerTransformingTextRunFactory(aInnerTransformingTextRunFactory),
mAllUppercase(aAllUppercase) {} mAllUppercase(aAllUppercase) {}
virtual void RebuildTextRun(nsTransformedTextRun* aTextRun, gfxContext* aRefContext) MOZ_OVERRIDE; virtual void RebuildTextRun(nsTransformedTextRun* aTextRun,
gfxContext* aRefContext,
gfxMissingFontRecorder* aMFR) MOZ_OVERRIDE;
// Perform a transformation on the given string, writing the result into // Perform a transformation on the given string, writing the result into
// aConvertedString. If aAllUppercase is true, the transform is (global) // aConvertedString. If aAllUppercase is true, the transform is (global)
@ -105,11 +111,12 @@ public:
* are done and before we request any data from the textrun. Also always * are done and before we request any data from the textrun. Also always
* called after a Create. * called after a Create.
*/ */
void FinishSettingProperties(gfxContext* aRefContext) void FinishSettingProperties(gfxContext* aRefContext,
gfxMissingFontRecorder* aMFR)
{ {
if (mNeedsRebuild) { if (mNeedsRebuild) {
mNeedsRebuild = false; mNeedsRebuild = false;
mFactory->RebuildTextRun(this, aRefContext); mFactory->RebuildTextRun(this, aRefContext, aMFR);
} }
} }

View File

@ -389,7 +389,7 @@ nsPropertiesTable::MakeTextRun(gfxContext* aThebesContext,
"nsPropertiesTable can only access glyphs by code point"); "nsPropertiesTable can only access glyphs by code point");
return aFontGroup-> return aFontGroup->
MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext, MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext,
aAppUnitsPerDevPixel, 0); aAppUnitsPerDevPixel, 0, nullptr);
} }
// An instance of nsOpenTypeTable is associated with one gfxFontEntry that // An instance of nsOpenTypeTable is associated with one gfxFontEntry that
@ -470,7 +470,7 @@ nsOpenTypeTable::UpdateCache(gfxContext* aThebesContext,
if (mCharCache != aChar) { if (mCharCache != aChar) {
nsAutoPtr<gfxTextRun> textRun; nsAutoPtr<gfxTextRun> textRun;
textRun = aFontGroup-> textRun = aFontGroup->
MakeTextRun(&aChar, 1, aThebesContext, aAppUnitsPerDevPixel, 0); MakeTextRun(&aChar, 1, aThebesContext, aAppUnitsPerDevPixel, 0, nullptr);
const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0]; const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
if (data.IsSimpleGlyph()) { if (data.IsSimpleGlyph()) {
mGlyphID = data.GetSimpleGlyph(); mGlyphID = data.GetSimpleGlyph();
@ -1553,7 +1553,8 @@ nsMathMLChar::StretchInternal(nsPresContext* aPresContext,
nsAutoPtr<gfxTextRun> textRun; nsAutoPtr<gfxTextRun> textRun;
textRun = fm->GetThebesFontGroup()-> textRun = fm->GetThebesFontGroup()->
MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aThebesContext, MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aThebesContext,
aPresContext->AppUnitsPerDevPixel(), 0); aPresContext->AppUnitsPerDevPixel(), 0,
aPresContext->MissingFontRecorder());
aDesiredStretchSize = MeasureTextRun(aThebesContext, textRun); aDesiredStretchSize = MeasureTextRun(aThebesContext, textRun);
mGlyphs[0] = textRun; mGlyphs[0] = textRun;

View File

@ -592,6 +592,10 @@ pref("gfx.bundled_fonts.enabled", true);
pref("gfx.bundled_fonts.force-enabled", false); pref("gfx.bundled_fonts.force-enabled", false);
#endif #endif
// Do we fire a notification about missing fonts, so the front-end can decide
// whether to try and do something about it (e.g. download additional fonts)?
pref("gfx.missing_fonts.notify", false);
pref("gfx.filter.nearest.force-enabled", false); pref("gfx.filter.nearest.force-enabled", false);
// prefs controlling the font (name/cmap) loader that runs shortly after startup // prefs controlling the font (name/cmap) loader that runs shortly after startup