diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 9541815b2d69..66534abc00b8 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -256,14 +256,13 @@ pref("ui.dragThresholdY", 25); // Layers Acceleration. We can only have nice things on gonk, because // they're not maintained anywhere else. +pref("layers.offmainthreadcomposition.enabled", true); #ifndef MOZ_WIDGET_GONK pref("dom.ipc.tabs.disabled", true); -pref("layers.offmainthreadcomposition.enabled", false); pref("layers.offmainthreadcomposition.async-animations", false); pref("layers.async-video.enabled", false); #else pref("dom.ipc.tabs.disabled", false); -pref("layers.offmainthreadcomposition.enabled", true); pref("layers.acceleration.disabled", false); pref("layers.offmainthreadcomposition.async-animations", true); pref("layers.async-video.enabled", true); diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 40d1233bd3fc..30b46e84082d 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -186,6 +186,7 @@ if (!this._statusPanel) { this._statusPanel = document.createElementNS(this.namespaceURI, "statuspanel"); this._statusPanel.setAttribute("inactive", "true"); + this._statusPanel.setAttribute("layer", "true"); this._appendStatusPanel(); } return this._statusPanel; diff --git a/content/canvas/src/CanvasRenderingContext2D.cpp b/content/canvas/src/CanvasRenderingContext2D.cpp index 32a3005203f3..5a69509b2de9 100644 --- a/content/canvas/src/CanvasRenderingContext2D.cpp +++ b/content/canvas/src/CanvasRenderingContext2D.cpp @@ -2208,11 +2208,13 @@ CanvasRenderingContext2D::SetFont(const nsAString& font, fontStyle->mFont.AddFontFeaturesToStyle(&style); + nsPresContext *c = presShell->GetPresContext(); CurrentState().fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name, &style, - presShell->GetPresContext()->GetUserFontSet()); + c->GetUserFontSet()); NS_ASSERTION(CurrentState().fontGroup, "Could not get font group"); + CurrentState().fontGroup->SetTextPerfMetrics(c->GetTextPerfMetrics()); // The font getter is required to be reserialized based on what we // parsed (including having line-height removed). (Older drafts of @@ -2806,6 +2808,12 @@ gfxFontGroup *CanvasRenderingContext2D::GetCurrentFontStyle() nullptr); if (CurrentState().fontGroup) { CurrentState().font = kDefaultFontStyle; + + nsIPresShell* presShell = GetPresShell(); + if (presShell) { + CurrentState().fontGroup->SetTextPerfMetrics( + presShell->GetPresContext()->GetTextPerfMetrics()); + } } else { NS_ERROR("Default canvas font is invalid"); } diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index a0f9d9e2455f..127fb1311958 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1139,7 +1139,7 @@ void WriteSnapshotLinkToDumpFile(T* aObj, FILE* aFile) nsCString string(aObj->Name()); string.Append("-"); string.AppendInt((uint64_t)aObj); - fprintf(aFile, "href=\"javascript:ViewImage('%s')\"", string.BeginReading()); + fprintf_stderr(aFile, "href=\"javascript:ViewImage('%s')\"", string.BeginReading()); } template @@ -1148,11 +1148,13 @@ void WriteSnapshotToDumpFile_internal(T* aObj, gfxASurface* aSurf) nsCString string(aObj->Name()); string.Append("-"); string.AppendInt((uint64_t)aObj); - if (gfxUtils::sDumpPaintFile) - fprintf(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); + if (gfxUtils::sDumpPaintFile) { + fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); + } aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); - if (gfxUtils::sDumpPaintFile) - fprintf(gfxUtils::sDumpPaintFile, "\";"); + if (gfxUtils::sDumpPaintFile) { + fprintf_stderr(gfxUtils::sDumpPaintFile, "\";"); + } } void WriteSnapshotToDumpFile(Layer* aLayer, gfxASurface* aSurf) @@ -1176,13 +1178,13 @@ void Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) { if (aDumpHtml) { - fprintf(aFile, "
  • "); + fprintf_stderr(aFile, ">"); } DumpSelf(aFile, aPrefix); @@ -1193,7 +1195,7 @@ Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) #endif if (aDumpHtml) { - fprintf(aFile, ""); + fprintf_stderr(aFile, ""); } if (Layer* mask = GetMaskLayer()) { @@ -1206,16 +1208,16 @@ Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) nsAutoCString pfx(aPrefix); pfx += " "; if (aDumpHtml) { - fprintf(aFile, ""); } } if (aDumpHtml) { - fprintf(aFile, "
  • "); + fprintf_stderr(aFile, ""); } if (Layer* next = GetNextSibling()) next->Dump(aFile, aPrefix, aDumpHtml); @@ -1226,11 +1228,7 @@ Layer::DumpSelf(FILE* aFile, const char* aPrefix) { nsAutoCString str; PrintInfo(str, aPrefix); - if (!aFile || aFile == stderr) { - printf_stderr("%s\n", str.get()); - } else { - fprintf(aFile, "%s\n", str.get()); - } + fprintf_stderr(aFile, "%s\n", str.get()); } void @@ -1407,34 +1405,34 @@ LayerManager::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) #ifdef MOZ_DUMP_PAINTING if (aDumpHtml) { - fprintf(file, ""); + fprintf_stderr(file, ""); } return; } if (aDumpHtml) { - fprintf(file, ""); + fprintf_stderr(file, ""); } fputc('\n', file); } @@ -1444,7 +1442,7 @@ LayerManager::DumpSelf(FILE* aFile, const char* aPrefix) { nsAutoCString str; PrintInfo(str, aPrefix); - fprintf(FILEOrDefault(aFile), "%s\n", str.get()); + fprintf_stderr(FILEOrDefault(aFile), "%s\n", str.get()); } void diff --git a/gfx/layers/composite/ContentHost.cpp b/gfx/layers/composite/ContentHost.cpp index 046dfc467da8..76d86c0a3dba 100644 --- a/gfx/layers/composite/ContentHost.cpp +++ b/gfx/layers/composite/ContentHost.cpp @@ -260,20 +260,20 @@ ContentHostBase::Dump(FILE* aFile, if (!aFile) { aFile = stderr; } - fprintf(aFile, ""); } #endif @@ -816,20 +816,20 @@ ContentHostDoubleBuffered::Dump(FILE* aFile, if (!aFile) { aFile = stderr; } - fprintf(aFile, ""); } #endif diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index d3d0c9129302..95630d0e4110 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -181,11 +181,11 @@ ImageHost::Dump(FILE* aFile, aFile = stderr; } if (mFrontBuffer) { - fprintf(aFile, "%s", aPrefix); - fprintf(aFile, aDumpHtml ? " " : " "); } } #endif @@ -391,11 +391,11 @@ DeprecatedImageHostSingle::Dump(FILE* aFile, aFile = stderr; } if (mDeprecatedTextureHost) { - fprintf(aFile, "%s", aPrefix); - fprintf(aFile, aDumpHtml ? " " : " "); } } diff --git a/gfx/layers/composite/TiledContentHost.cpp b/gfx/layers/composite/TiledContentHost.cpp index fb8430af8d87..45696266d4a3 100644 --- a/gfx/layers/composite/TiledContentHost.cpp +++ b/gfx/layers/composite/TiledContentHost.cpp @@ -331,16 +331,16 @@ TiledContentHost::Dump(FILE* aFile, TiledLayerBufferComposite::Iterator it = mVideoMemoryTiledBuffer.TilesBegin(); TiledLayerBufferComposite::Iterator stop = mVideoMemoryTiledBuffer.TilesEnd(); if (aDumpHtml) { - fprintf(aFile, ""); } } #endif diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 2dd8df4ed419..f3f71a201c18 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -554,8 +554,8 @@ CompositorParent::CompositeInTransaction() #ifdef MOZ_DUMP_PAINTING static bool gDumpCompositorTree = false; if (gDumpCompositorTree) { - fprintf(stdout, "Painting --- compositing layer tree:\n"); - mLayerManager->Dump(stdout, "", false); + printf_stderr("Painting --- compositing layer tree:\n"); + mLayerManager->Dump(); } #endif mLayerManager->EndEmptyTransaction(); diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp index 3b309bcc5d1e..84220c738a07 100644 --- a/gfx/src/nsDeviceContext.cpp +++ b/gfx/src/nsDeviceContext.cpp @@ -66,6 +66,7 @@ public: nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, gfxUserFontSet* aUserFontSet, + gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics); void FontMetricsDeleted(const nsFontMetrics* aFontMetrics); @@ -123,6 +124,7 @@ nsFontCache::Observe(nsISupports*, const char* aTopic, const PRUnichar*) nsresult nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, gfxUserFontSet* aUserFontSet, + gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics) { if (!aLanguage) @@ -152,7 +154,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, fm = new nsFontMetrics(); NS_ADDREF(fm); - nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet); + nsresult rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); if (NS_SUCCEEDED(rv)) { // the mFontMetrics list has the "head" at the end, because append // is cheaper than insert @@ -171,7 +173,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, Compact(); fm = new nsFontMetrics(); NS_ADDREF(fm); - rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet); + rv = fm->Init(aFont, aLanguage, mContext, aUserFontSet, aTextPerf); if (NS_SUCCEEDED(rv)) { mFontMetrics.AppendElement(fm); aMetrics = fm; @@ -260,6 +262,7 @@ nsresult nsDeviceContext::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, gfxUserFontSet* aUserFontSet, + gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics) { if (!mFontCache) { @@ -268,7 +271,8 @@ nsDeviceContext::GetMetricsFor(const nsFont& aFont, mFontCache->Init(this); } - return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet, aMetrics); + return mFontCache->GetMetricsFor(aFont, aLanguage, aUserFontSet, + aTextPerf, aMetrics); } nsresult diff --git a/gfx/src/nsDeviceContext.h b/gfx/src/nsDeviceContext.h index 1f46298386d8..28789de62df1 100644 --- a/gfx/src/nsDeviceContext.h +++ b/gfx/src/nsDeviceContext.h @@ -20,6 +20,7 @@ #include "mozilla/AppUnits.h" // for AppUnits class gfxASurface; +class gfxTextPerfMetrics; class gfxUserFontSet; class nsFont; class nsFontCache; @@ -119,6 +120,7 @@ public: */ nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage, gfxUserFontSet* aUserFontSet, + gfxTextPerfMetrics* aTextPerf, nsFontMetrics*& aMetrics); /** diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp index 25f8ff210f4f..3c7fdc3b935b 100644 --- a/gfx/src/nsFontMetrics.cpp +++ b/gfx/src/nsFontMetrics.cpp @@ -96,7 +96,8 @@ nsFontMetrics::~nsFontMetrics() nsresult nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage, nsDeviceContext *aContext, - gfxUserFontSet *aUserFontSet) + gfxUserFontSet *aUserFontSet, + gfxTextPerfMetrics *aTextPerf) { NS_ABORT_IF_FALSE(mP2A == 0, "already initialized"); @@ -119,6 +120,7 @@ nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage, mFontGroup = gfxPlatform::GetPlatform()-> CreateFontGroup(aFont.name, &style, aUserFontSet); + mFontGroup->SetTextPerfMetrics(aTextPerf); if (mFontGroup->FontListLength() < 1) return NS_ERROR_UNEXPECTED; diff --git a/gfx/src/nsFontMetrics.h b/gfx/src/nsFontMetrics.h index c4bdc72032e1..ec61eccfe522 100644 --- a/gfx/src/nsFontMetrics.h +++ b/gfx/src/nsFontMetrics.h @@ -19,6 +19,7 @@ #include "nscore.h" // for PRUnichar class gfxUserFontSet; +class gfxTextPerfMetrics; class nsDeviceContext; class nsIAtom; class nsRenderingContext; @@ -58,7 +59,8 @@ public: */ nsresult Init(const nsFont& aFont, nsIAtom* aLanguage, nsDeviceContext *aContext, - gfxUserFontSet *aUserFontSet = nullptr); + gfxUserFontSet *aUserFontSet, + gfxTextPerfMetrics *aTextPerf); /** * Destroy this font metrics. This breaks the association between diff --git a/gfx/thebes/gfxASurface.h b/gfx/thebes/gfxASurface.h index 8897b00a3631..2f3317dca466 100644 --- a/gfx/thebes/gfxASurface.h +++ b/gfx/thebes/gfxASurface.h @@ -6,10 +6,6 @@ #ifndef GFX_ASURFACE_H #define GFX_ASURFACE_H -#ifdef MOZ_DUMP_PAINTING - #define MOZ_DUMP_IMAGES -#endif - #include "mozilla/MemoryReporting.h" #include "gfxTypes.h" #include "mozilla/Scoped.h" diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 3135f2a794e0..d9590695e541 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -3083,6 +3083,12 @@ HashMix(uint32_t aHash, PRUnichar aCh) return (aHash >> 28) ^ (aHash << 4) ^ aCh; } +#ifdef __GNUC__ +#define GFX_MAYBE_UNUSED __attribute__((unused)) +#else +#define GFX_MAYBE_UNUSED +#endif + template gfxShapedWord* gfxFont::GetShapedWord(gfxContext *aContext, @@ -3091,7 +3097,8 @@ gfxFont::GetShapedWord(gfxContext *aContext, uint32_t aHash, int32_t aRunScript, int32_t aAppUnitsPerDevUnit, - uint32_t aFlags) + uint32_t aFlags, + gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED) { // if the cache is getting too big, flush it and start over uint32_t wordCacheMaxEntries = @@ -3121,12 +3128,23 @@ gfxFont::GetShapedWord(gfxContext *aContext, Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT : Telemetry::WORD_CACHE_HITS_CHROME), aLength); +#ifndef RELEASE_BUILD + if (aTextPerf) { + aTextPerf->current.wordCacheHit++; + } +#endif return sw; } Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT : Telemetry::WORD_CACHE_MISSES_CHROME), aLength); +#ifndef RELEASE_BUILD + if (aTextPerf) { + aTextPerf->current.wordCacheMiss++; + } +#endif + sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength, aRunScript, aAppUnitsPerDevUnit, @@ -3371,6 +3389,12 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext, return ok; } +#ifndef RELEASE_BUILD +#define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0) +#else +#define TEXT_PERF_INCR(tp, m) +#endif + inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; } inline static bool IsChar8Bit(PRUnichar aCh) { return aCh < 0x100; } @@ -3387,7 +3411,25 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, return true; } + gfxTextPerfMetrics *tp = nullptr; + +#ifndef RELEASE_BUILD + tp = aTextRun->GetFontGroup()->GetTextPerfMetrics(); + if (tp) { + if (mStyle.systemFont) { + tp->current.numChromeTextRuns++; + } else { + tp->current.numContentTextRuns++; + } + tp->current.numChars += aRunLength; + if (aRunLength > tp->current.maxTextRunLen) { + tp->current.maxTextRunLen = aRunLength; + } + } +#endif + if (BypassShapedWordCache(aRunScript)) { + TEXT_PERF_INCR(tp, wordCacheSpaceRules); return ShapeTextWithoutWordCache(aContext, aString + aRunStart, aRunStart, aRunLength, aRunScript, aTextRun); @@ -3436,6 +3478,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, // For words longer than the limit, we don't use the // font's word cache but just shape directly into the textrun. if (length > wordCacheCharLimit) { + TEXT_PERF_INCR(tp, wordCacheLong); bool ok = ShapeFragmentWithoutWordCache(aContext, text + wordStart, aRunStart + wordStart, @@ -3459,7 +3502,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, text + wordStart, length, hash, aRunScript, appUnitsPerDevUnit, - wordFlags); + wordFlags, tp); if (sw) { aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart); } else { @@ -3478,7 +3521,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, &space, 1, HashMix(0, ' '), aRunScript, appUnitsPerDevUnit, - flags | gfxTextRunFactory::TEXT_IS_8BIT); + flags | gfxTextRunFactory::TEXT_IS_8BIT, tp); if (sw) { aTextRun->CopyGlyphDataFrom(sw, aRunStart + i); } else { @@ -4017,6 +4060,7 @@ gfxFontGroup::gfxFontGroup(const nsAString& aFamilies, , mStyle(*aStyle) , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET) , mHyphenWidth(-1) + , mTextPerf(nullptr) { mUserFontSet = nullptr; SetUserFontSet(aUserFontSet); @@ -4177,7 +4221,9 @@ gfxFontGroup::~gfxFontGroup() gfxFontGroup * gfxFontGroup::Copy(const gfxFontStyle *aStyle) { - return new gfxFontGroup(mFamilies, aStyle, mUserFontSet); + gfxFontGroup *fg = new gfxFontGroup(mFamilies, aStyle, mUserFontSet); + fg->SetTextPerfMetrics(mTextPerf); + return fg; } bool @@ -5013,6 +5059,16 @@ void gfxFontGroup::ComputeRanges(nsTArray& aRanges, nsRefPtr font = FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType); +#ifndef RELEASE_BUILD + if (MOZ_UNLIKELY(mTextPerf)) { + if (matchType == gfxTextRange::kPrefsFallback) { + mTextPerf->current.fallbackPrefs++; + } else if (matchType == gfxTextRange::kSystemFallback) { + mTextPerf->current.fallbackSystem++; + } + } +#endif + prevCh = ch; if (lastRangeIndex == -1) { @@ -5569,6 +5625,13 @@ gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams, MOZ_COUNT_CTOR(gfxTextRun); NS_ADDREF(mFontGroup); +#ifndef RELEASE_BUILD + gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics(); + if (tp) { + tp->current.textrunConst++; + } +#endif + mCharacterGlyphs = reinterpret_cast(this + 1); if (aParams->mSkipChars) { @@ -5596,6 +5659,12 @@ gfxTextRun::~gfxTextRun() // been told to release its reference to the group, so we mustn't do that // again here. if (!mReleasedFontGroup) { +#ifndef RELEASE_BUILD + gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics(); + if (tp) { + tp->current.textrunDestr++; + } +#endif NS_RELEASE(mFontGroup); } @@ -6648,7 +6717,8 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, mAppUnitsPerDevUnit, gfxTextRunFactory::TEXT_IS_8BIT | gfxTextRunFactory::TEXT_IS_ASCII | - gfxTextRunFactory::TEXT_IS_PERSISTENT); + gfxTextRunFactory::TEXT_IS_PERSISTENT, + nullptr); if (sw) { AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false); CopyGlyphDataFrom(sw, aCharIndex); diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index cf5479e6c540..3bbbbd973026 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1014,6 +1014,59 @@ protected: nsCOMPtr mWordCacheExpirationTimer; }; +class gfxTextPerfMetrics { +public: + + struct TextCounts { + uint32_t numContentTextRuns; + uint32_t numChromeTextRuns; + uint32_t numChars; + uint32_t maxTextRunLen; + uint32_t wordCacheSpaceRules; + uint32_t wordCacheLong; + uint32_t wordCacheHit; + uint32_t wordCacheMiss; + uint32_t fallbackPrefs; + uint32_t fallbackSystem; + uint32_t textrunConst; + uint32_t textrunDestr; + }; + + uint32_t reflowCount; + + // counts per reflow operation + TextCounts current; + + // totals for the lifetime of a document + TextCounts cumulative; + + gfxTextPerfMetrics() { + memset(this, 0, sizeof(gfxTextPerfMetrics)); + } + + // add current totals to cumulative ones + void Accumulate() { + if (current.numChars == 0) { + return; + } + cumulative.numContentTextRuns += current.numContentTextRuns; + cumulative.numChromeTextRuns += current.numChromeTextRuns; + cumulative.numChars += current.numChars; + if (current.maxTextRunLen > cumulative.maxTextRunLen) { + cumulative.maxTextRunLen = current.maxTextRunLen; + } + cumulative.wordCacheSpaceRules += current.wordCacheSpaceRules; + cumulative.wordCacheLong += current.wordCacheLong; + cumulative.wordCacheHit += current.wordCacheHit; + cumulative.wordCacheMiss += current.wordCacheMiss; + cumulative.fallbackPrefs += current.fallbackPrefs; + cumulative.fallbackSystem += current.fallbackSystem; + cumulative.textrunConst += current.textrunConst; + cumulative.textrunDestr += current.textrunDestr; + memset(¤t, 0, sizeof(current)); + } +}; + class gfxTextRunFactory { NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory) @@ -1645,7 +1698,8 @@ public: uint32_t aHash, int32_t aRunScript, int32_t aAppUnitsPerDevUnit, - uint32_t aFlags); + uint32_t aFlags, + gfxTextPerfMetrics *aTextPerf); // Ensure the ShapedWord cache is initialized. This MUST be called before // any attempt to use GetShapedWord(). @@ -3456,6 +3510,10 @@ public: // with no @font-face rule, this always returns 0. uint64_t GetGeneration(); + // used when logging text performance + gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; } + void SetTextPerfMetrics(gfxTextPerfMetrics *aTextPerf) { mTextPerf = aTextPerf; } + // If there is a user font set, check to see whether the font list or any // caches need updating. virtual void UpdateFontList(); @@ -3486,6 +3544,8 @@ protected: gfxUserFontSet* mUserFontSet; uint64_t mCurrGeneration; // track the current user font set generation, rebuild font list if needed + gfxTextPerfMetrics *mTextPerf; + // Cache a textrun representing an ellipsis (useful for CSS text-overflow) // at a specific appUnitsPerDevPixel size nsAutoPtr mCachedEllipsisTextRun; diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 88febd801e8b..a0ad3719a5df 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -120,6 +120,7 @@ static PRLogModuleInfo *sFontInitLog = nullptr; static PRLogModuleInfo *sTextrunLog = nullptr; static PRLogModuleInfo *sTextrunuiLog = nullptr; static PRLogModuleInfo *sCmapDataLog = nullptr; +static PRLogModuleInfo *sTextPerfLog = nullptr; #endif /* Class to listen for pref changes so that chrome code can dynamically @@ -364,11 +365,12 @@ gfxPlatform::Init() gEverInitialized = true; #ifdef PR_LOGGING - sFontlistLog = PR_NewLogModule("fontlist");; - sFontInitLog = PR_NewLogModule("fontinit");; - sTextrunLog = PR_NewLogModule("textrun");; - sTextrunuiLog = PR_NewLogModule("textrunui");; - sCmapDataLog = PR_NewLogModule("cmapdata");; + sFontlistLog = PR_NewLogModule("fontlist"); + sFontInitLog = PR_NewLogModule("fontinit"); + sTextrunLog = PR_NewLogModule("textrun"); + sTextrunuiLog = PR_NewLogModule("textrunui"); + sCmapDataLog = PR_NewLogModule("cmapdata"); + sTextPerfLog = PR_NewLogModule("textperf"); #endif gGfxPlatformPrefsLock = new Mutex("gfxPlatform::gGfxPlatformPrefsLock"); @@ -1945,6 +1947,9 @@ gfxPlatform::GetLog(eGfxLog aWhichLog) case eGfxLog_cmapdata: return sCmapDataLog; break; + case eGfxLog_textperf: + return sTextPerfLog; + break; default: break; } diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 0900e90eb7b5..0785dd21c537 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -117,7 +117,9 @@ enum eGfxLog { // dump text runs, font matching, system fallback for chrome eGfxLog_textrunui = 3, // dump cmap coverage data as they are loaded - eGfxLog_cmapdata = 4 + eGfxLog_cmapdata = 4, + // text perf data + eGfxLog_textperf = 5 }; // when searching through pref langs, max number of pref langs diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 8f4b8b22c7cf..f0ba0fbf92d6 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -180,7 +180,7 @@ public: #ifdef DEBUG_DISPLAY_ITEM_DATA void Dump(const char *aPrefix = "") { - printf("%sLayerManagerData %p\n", aPrefix, this); + printf_stderr("%sLayerManagerData %p\n", aPrefix, this); nsAutoCString prefix; prefix += aPrefix; prefix += " "; @@ -899,7 +899,7 @@ InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsIntRegion& aRegion, if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { nsAutoCString str; AppendToString(str, rgn); - printf("Invalidating layer %p: %s\n", aLayer, str.get()); + printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get()); } #endif } @@ -967,7 +967,7 @@ FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame* aFrame, while (rootData->mParent) { rootData = rootData->mParent; } - printf("Removing frame %p - dumping display data\n", aFrame); + printf_stderr("Removing frame %p - dumping display data\n", aFrame); rootData->Dump(); } #endif @@ -1057,7 +1057,7 @@ FrameLayerBuilder::ProcessRemovedDisplayItems(nsRefPtrHashKey* if (t) { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t); + printf_stderr("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t); } #endif InvalidatePostTransformRegion(t, @@ -1116,11 +1116,11 @@ FrameLayerBuilder::DumpDisplayItemDataForFrame(nsRefPtrHashKey* } str += "\n"; - printf("%s", str.get()); + printf_stderr("%s", str.get()); if (data->mInactiveManager) { prefix += " "; - printf("%sDumping inactive layer info:\n", prefix.get()); + printf_stderr("%sDumping inactive layer info:\n", prefix.get()); LayerManagerData* lmd = static_cast (data->mInactiveManager->GetUserData(&gLayerManagerUserData)); lmd->Dump(prefix.get()); @@ -1352,7 +1352,7 @@ InvalidateEntireThebesLayer(ThebesLayer* aLayer, const nsIFrame* aAnimatedGeomet { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Invalidating entire layer %p\n", aLayer); + printf_stderr("Invalidating entire layer %p\n", aLayer); } #endif nsIntRect invalidate = aLayer->GetValidRegion().GetBounds(); @@ -1402,7 +1402,7 @@ ContainerState::CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot if (!data->mRegionToInvalidate.IsEmpty()) { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Invalidating deleted frame content from layer %p\n", layer.get()); + printf_stderr("Invalidating deleted frame content from layer %p\n", layer.get()); } #endif layer->InvalidateRegion(data->mRegionToInvalidate); @@ -1410,7 +1410,7 @@ ContainerState::CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { nsAutoCString str; AppendToString(str, data->mRegionToInvalidate); - printf("Invalidating layer %p: %s\n", layer.get(), str.get()); + printf_stderr("Invalidating layer %p: %s\n", layer.get(), str.get()); } #endif data->mRegionToInvalidate.SetEmpty(); @@ -2053,9 +2053,9 @@ DumpPaintedImage(nsDisplayItem* aItem, gfxASurface* aSurf) nsCString string(aItem->Name()); string.Append("-"); string.AppendInt((uint64_t)aItem); - fprintf(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); + fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); - fprintf(gfxUtils::sDumpPaintFile, "\";"); + fprintf_stderr(gfxUtils::sDumpPaintFile, "\";"); } #endif @@ -2396,7 +2396,7 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, // or a new scale here #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), aItem->Frame(), t, aNewLayer); + printf_stderr("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), aItem->Frame(), t, aNewLayer); } #endif InvalidatePostTransformRegion(t, @@ -2439,7 +2439,7 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, combined = aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion()); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Display item type %s(%p) added to layer %p!\n", aItem->Name(), aItem->Frame(), aNewLayer); + printf_stderr("Display item type %s(%p) added to layer %p!\n", aItem->Name(), aItem->Frame(), aNewLayer); } #endif } else if (isInvalid || (aItem->IsInvalid(invalid) && invalid.IsEmpty())) { @@ -2449,7 +2449,7 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, combined.Or(combined, aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion())); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), aItem->Frame(), aNewLayer); + printf_stderr("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), aItem->Frame(), aNewLayer); } #endif } else { @@ -2476,7 +2476,7 @@ ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { if (!combined.IsEmpty()) { - printf("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), aItem->Frame(), aNewLayer); + printf_stderr("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), aItem->Frame(), aNewLayer); } } #endif @@ -2583,7 +2583,7 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer, if (!invalid.IsEmpty()) { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Inactive LayerManager(%p) for display item %s(%p) has an invalid region - invalidating layer %p\n", tempManager.get(), aItem->Name(), aItem->Frame(), aLayer); + printf_stderr("Inactive LayerManager(%p) for display item %s(%p) has an invalid region - invalidating layer %p\n", tempManager.get(), aItem->Name(), aItem->Frame(), aLayer); } #endif if (hasClip) { diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index acfc04361310..517fec5d1235 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2234,7 +2234,7 @@ nsDisplayThemedBackground::~nsDisplayThemedBackground() void nsDisplayThemedBackground::WriteDebugInfo(FILE *aOutput) { - fprintf(aOutput, "(themed, appearance:%d) ", mAppearance); + fprintf_stderr(aOutput, "(themed, appearance:%d) ", mAppearance); } #endif @@ -4713,32 +4713,32 @@ nsDisplaySVGEffects::PrintEffects(FILE* aOutput) bool isOK = true; nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); bool first = true; - fprintf(aOutput, " effects=("); + fprintf_stderr(aOutput, " effects=("); if (mFrame->StyleDisplay()->mOpacity != 1.0f) { first = false; - fprintf(aOutput, "opacity(%f)", mFrame->StyleDisplay()->mOpacity); + fprintf_stderr(aOutput, "opacity(%f)", mFrame->StyleDisplay()->mOpacity); } if (clipPathFrame) { if (!first) { - fprintf(aOutput, ", "); + fprintf_stderr(aOutput, ", "); } - fprintf(aOutput, "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial"); + fprintf_stderr(aOutput, "clip(%s)", clipPathFrame->IsTrivial() ? "trivial" : "non-trivial"); first = false; } if (effectProperties.GetFilterFrame(&isOK)) { if (!first) { - fprintf(aOutput, ", "); + fprintf_stderr(aOutput, ", "); } - fprintf(aOutput, "filter"); + fprintf_stderr(aOutput, "filter"); first = false; } if (effectProperties.GetMaskFrame(&isOK)) { if (!first) { - fprintf(aOutput, ", "); + fprintf_stderr(aOutput, ", "); } - fprintf(aOutput, "mask"); + fprintf_stderr(aOutput, "mask"); } - fprintf(aOutput, ")"); + fprintf_stderr(aOutput, ")"); } #endif diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index ed8b16f36e5c..5add64e6499c 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1934,17 +1934,32 @@ public: virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE { - return new nsDisplayItemBoundsGeometry(this, aBuilder); + return new nsDisplaySolidColorGeometry(this, aBuilder, mColor); } virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) MOZ_OVERRIDE { - const nsDisplayItemBoundsGeometry* geometry = static_cast(aGeometry); + const nsDisplaySolidColorGeometry* geometry = + static_cast(aGeometry); + if (mColor != geometry->mColor) { + bool dummy; + aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &dummy)); + return; + } ComputeInvalidationRegionDifference(aBuilder, geometry, aInvalidRegion); } +#ifdef MOZ_DUMP_PAINTING + virtual void WriteDebugInfo(FILE *aOutput) MOZ_OVERRIDE + { + fprintf(aOutput, "(rgba %d,%d,%d,%d)", + NS_GET_R(mColor), NS_GET_G(mColor), + NS_GET_B(mColor), NS_GET_A(mColor)); + } +#endif + NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR) private: @@ -2155,7 +2170,7 @@ public: NS_DISPLAY_DECL_NAME("BackgroundColor", TYPE_BACKGROUND_COLOR) #ifdef MOZ_DUMP_PAINTING virtual void WriteDebugInfo(FILE *aOutput) MOZ_OVERRIDE { - fprintf(aOutput, "(rgba %d,%d,%d,%d)", + fprintf_stderr(aOutput, "(rgba %d,%d,%d,%d)", NS_GET_R(mColor), NS_GET_G(mColor), NS_GET_B(mColor), NS_GET_A(mColor)); @@ -2489,7 +2504,7 @@ public: NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY) #ifdef MOZ_DUMP_PAINTING virtual void WriteDebugInfo(FILE *aOutput) MOZ_OVERRIDE { - fprintf(aOutput, "(opacity %f)", mFrame->StyleDisplay()->mOpacity); + fprintf_stderr(aOutput, "(opacity %f)", mFrame->StyleDisplay()->mOpacity); } #endif diff --git a/layout/base/nsDisplayListInvalidation.h b/layout/base/nsDisplayListInvalidation.h index e947c2fcddf2..843714922f6b 100644 --- a/layout/base/nsDisplayListInvalidation.h +++ b/layout/base/nsDisplayListInvalidation.h @@ -116,4 +116,17 @@ public: nsRect mPaddingRect; }; +class nsDisplaySolidColorGeometry : public nsDisplayItemBoundsGeometry +{ +public: + nsDisplaySolidColorGeometry(nsDisplayItem* aItem, + nsDisplayListBuilder* aBuilder, + nscolor aColor) + : nsDisplayItemBoundsGeometry(aItem, aBuilder) + , mColor(aColor) + { } + + nscolor mColor; +}; + #endif /*NSDISPLAYLISTINVALIDATION_H_*/ diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 50a8976ba882..4fbebf31ece6 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1025,6 +1025,7 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) // mPresShell could have been removed now, see bug 378682/421432 if (mPresShell) { mPresShell->ScrollToAnchor(); + mPresShell->LoadComplete(); } } diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 49ee59fb7e3b..cda4d27f25ad 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -417,6 +417,11 @@ public: */ virtual bool IsLayoutFlushObserver() = 0; + /** + * Called when document load completes. + */ + virtual NS_HIDDEN_(void) LoadComplete() = 0; + /** * This calls through to the frame manager to get the root frame. */ diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index e677257eebfd..d9ae46388b45 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -127,16 +127,16 @@ PrintDisplayListTo(nsDisplayListBuilder* aBuilder, const nsDisplayList& aList, FILE* aOutput, bool aDumpHtml) { if (aDumpHtml) { - fprintf(aOutput, ""); } } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 07416d35f6c6..c4ae586ccf9d 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2086,7 +2086,7 @@ nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect, #ifdef MOZ_DUMP_PAINTING if (gDumpEventList) { - fprintf(stdout, "Event handling --- (%d,%d):\n", aRect.x, aRect.y); + fprintf_stderr(stderr, "Event handling --- (%d,%d):\n", aRect.x, aRect.y); nsFrame::PrintDisplayList(&builder, list); } #endif @@ -2277,16 +2277,16 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram string.Append(".html"); gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w"); } else { - gfxUtils::sDumpPaintFile = stdout; + gfxUtils::sDumpPaintFile = stderr; } if (gfxUtils::sDumpPaintingToFile) { - fprintf(gfxUtils::sDumpPaintFile, ""); + fprintf_stderr(gfxUtils::sDumpPaintFile, ""); } - fprintf(gfxUtils::sDumpPaintFile, "Painting --- before optimization (dirty %d,%d,%d,%d):\n", + fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- before optimization (dirty %d,%d,%d,%d):\n", dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height); nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile); if (gfxUtils::sDumpPaintingToFile) { - fprintf(gfxUtils::sDumpPaintFile, ""); + fprintf_stderr(gfxUtils::sDumpPaintFile, ""); } - fprintf(gfxUtils::sDumpPaintFile, "Painting --- after optimization:\n"); + fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- after optimization:\n"); nsFrame::PrintDisplayList(&builder, list, gfxUtils::sDumpPaintFile, gfxUtils::sDumpPaintingToFile); - fprintf(gfxUtils::sDumpPaintFile, "Painting --- retained layer tree:\n"); + fprintf_stderr(gfxUtils::sDumpPaintFile, "Painting --- retained layer tree:\n"); nsIWidget* widget = aFrame->GetNearestWidget(); if (widget) { nsRefPtr layerManager = widget->GetLayerManager(); @@ -2599,7 +2599,9 @@ nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext, float aInflation) { // pass the user font set object into the device context to pass along to CreateFontGroup - gfxUserFontSet* fs = aStyleContext->PresContext()->GetUserFontSet(); + nsPresContext* pc = aStyleContext->PresContext(); + gfxUserFontSet* fs = pc->GetUserFontSet(); + gfxTextPerfMetrics* tp = pc->GetTextPerfMetrics(); nsFont font = aStyleContext->StyleFont()->mFont; // We need to not run font.size through floats when it's large since @@ -2608,9 +2610,9 @@ nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext* aStyleContext, if (aInflation != 1.0f) { font.size = NSToCoordRound(font.size * aInflation); } - return aStyleContext->PresContext()->DeviceContext()->GetMetricsFor( + return pc->DeviceContext()->GetMetricsFor( font, aStyleContext->StyleFont()->mLanguage, - fs, *aFontMetrics); + fs, tp, *aFontMetrics); } nsIFrame* @@ -2947,9 +2949,9 @@ nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext, NS_PRECONDITION(aType == MIN_WIDTH || aType == PREF_WIDTH, "bad type"); #ifdef DEBUG_INTRINSIC_WIDTH - nsFrame::IndentBy(stdout, gNoiseIndent); - static_cast(aFrame)->ListTag(stdout); - printf(" %s intrinsic width for container:\n", + nsFrame::IndentBy(stderr, gNoiseIndent); + static_cast(aFrame)->ListTag(stderr); + printf_stderr(" %s intrinsic width for container:\n", aType == MIN_WIDTH ? "min" : "pref"); #endif @@ -3007,9 +3009,9 @@ nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext, result = aFrame->GetPrefWidth(aRenderingContext); #ifdef DEBUG_INTRINSIC_WIDTH --gNoiseIndent; - nsFrame::IndentBy(stdout, gNoiseIndent); - static_cast(aFrame)->ListTag(stdout); - printf(" %s intrinsic width from frame is %d.\n", + nsFrame::IndentBy(stderr, gNoiseIndent); + static_cast(aFrame)->ListTag(stderr); + printf_stderr(" %s intrinsic width from frame is %d.\n", aType == MIN_WIDTH ? "min" : "pref", result); #endif @@ -3196,9 +3198,9 @@ nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext, } #ifdef DEBUG_INTRINSIC_WIDTH - nsFrame::IndentBy(stdout, gNoiseIndent); - static_cast(aFrame)->ListTag(stdout); - printf(" %s intrinsic width for container is %d twips.\n", + nsFrame::IndentBy(stderr, gNoiseIndent); + static_cast(aFrame)->ListTag(stderr); + printf_stderr(" %s intrinsic width for container is %d twips.\n", aType == MIN_WIDTH ? "min" : "pref", result); #endif diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 4b9ce74c275a..9e098d2578e8 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -235,6 +235,12 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType) mUserFontSet = nullptr; mUserFontSetDirty = true; + // if text perf logging enabled, init stats struct + PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf); + if (log && PR_LOG_TEST(log, PR_LOG_WARNING)) { + mTextPerf = new gfxTextPerfMetrics(); + } + PR_INIT_CLIST(&mDOMMediaQueryLists); } diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index f90af174b964..6e692ea8c739 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -56,6 +56,7 @@ struct nsStyleBackground; struct nsStyleBorder; class nsIRunnable; class gfxUserFontSet; +class gfxTextPerfMetrics; class nsUserFontSet; struct nsFontFaceRuleContainer; class nsObjectFrame; @@ -788,6 +789,8 @@ public: */ const nscoord* GetBorderWidthTable() { return mBorderWidthTable; } + gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; } + bool IsDynamic() { return (mType == eContext_PageLayout || mType == eContext_Galley); } bool IsScreen() { return (mMedium == nsGkAtoms::screen || mType == eContext_PageLayout || @@ -1194,6 +1197,9 @@ protected: // container for per-context fonts (downloadable, SVG, etc.) nsUserFontSet* mUserFontSet; + // text performance metrics + nsAutoPtr mTextPerf; + nsRect mVisibleArea; nsSize mPageSize; float mPageScale; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 841a9b776a26..5bae532bdd4e 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -878,12 +878,106 @@ PresShell::Init(nsIDocument* aDocument, SetupFontInflation(); } +#ifdef PR_LOGGING +enum TextPerfLogType { + eLog_reflow, + eLog_loaddone, + eLog_totals +}; + +static void +LogTextPerfStats(gfxTextPerfMetrics* aTextPerf, + PresShell* aPresShell, + const gfxTextPerfMetrics::TextCounts& aCounts, + float aTime, TextPerfLogType aLogType, const char* aURL) +{ + char prefix[256]; + + switch (aLogType) { + case eLog_reflow: + sprintf(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime); + break; + case eLog_loaddone: + sprintf(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime); + break; + default: + MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type"); + sprintf(prefix, "(textperf-totals) %p", aPresShell); + } + + PRLogModuleInfo* tpLog = gfxPlatform::GetLog(eGfxLog_textperf); + + // ignore XUL contexts unless at debug level + PRLogModuleLevel logLevel = PR_LOG_WARNING; + if (aCounts.numContentTextRuns == 0) { + logLevel = PR_LOG_DEBUG; + } + + double hitRatio = 0.0; + uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss; + if (lookups) { + hitRatio = double(aCounts.wordCacheHit) / double(lookups); + } + + if (aLogType == eLog_loaddone) { + PR_LOG(tpLog, logLevel, + ("%s reflow: %d chars: %d " + "[%s] " + "content-textruns: %d chrome-textruns: %d " + "max-textrun-len: %d " + "word-cache-lookups: %d word-cache-hit-ratio: %4.3f " + "word-cache-space: %d word-cache-long: %d " + "pref-fallbacks: %d system-fallbacks: %d " + "textruns-const: %d textruns-destr: %d " + "cumulative-textruns-destr: %d\n", + prefix, aTextPerf->reflowCount, aCounts.numChars, + (aURL ? aURL : ""), + aCounts.numContentTextRuns, aCounts.numChromeTextRuns, + aCounts.maxTextRunLen, + lookups, hitRatio, + aCounts.wordCacheSpaceRules, aCounts.wordCacheLong, + aCounts.fallbackPrefs, aCounts.fallbackSystem, + aCounts.textrunConst, aCounts.textrunDestr, + aTextPerf->cumulative.textrunDestr)); + } else { + PR_LOG(tpLog, logLevel, + ("%s reflow: %d chars: %d " + "content-textruns: %d chrome-textruns: %d " + "max-textrun-len: %d " + "word-cache-lookups: %d word-cache-hit-ratio: %4.3f " + "word-cache-space: %d word-cache-long: %d " + "pref-fallbacks: %d system-fallbacks: %d " + "textruns-const: %d textruns-destr: %d " + "cumulative-textruns-destr: %d\n", + prefix, aTextPerf->reflowCount, aCounts.numChars, + aCounts.numContentTextRuns, aCounts.numChromeTextRuns, + aCounts.maxTextRunLen, + lookups, hitRatio, + aCounts.wordCacheSpaceRules, aCounts.wordCacheLong, + aCounts.fallbackPrefs, aCounts.fallbackSystem, + aCounts.textrunConst, aCounts.textrunDestr, + aTextPerf->cumulative.textrunDestr)); + } +} +#endif + void PresShell::Destroy() { NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), "destroy called on presshell while scripts not blocked"); + // dump out cumulative text perf metrics +#ifdef PR_LOGGING + gfxTextPerfMetrics* tp; + if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) { + tp->Accumulate(); + if (tp->cumulative.numChars > 0) { + LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr); + } + } +#endif + #ifdef MOZ_REFLOW_PERF DumpReflows(); if (mReflowCountMgr) { @@ -2399,15 +2493,24 @@ PresShell::BeginLoad(nsIDocument *aDocument) mDocumentLoading = true; #ifdef PR_LOGGING - if (gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG)) { + gfxTextPerfMetrics *tp = nullptr; + if (mPresContext) { + tp = mPresContext->GetTextPerfMetrics(); + } + + bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG); + if (shouldLog || tp) { mLoadBegin = TimeStamp::Now(); + } + + if (shouldLog) { nsIURI* uri = mDocument->GetDocumentURI(); nsAutoCString spec; if (uri) { uri->GetSpec(spec); } PR_LOG(gLog, PR_LOG_DEBUG, - ("(presshell) %p begin load [%s]\n", + ("(presshell) %p load begin [%s]\n", this, spec.get())); } #endif @@ -2421,19 +2524,38 @@ PresShell::EndLoad(nsIDocument *aDocument) RestoreRootScrollPosition(); mDocumentLoading = false; +} +void +PresShell::LoadComplete() +{ #ifdef PR_LOGGING + gfxTextPerfMetrics *tp = nullptr; + if (mPresContext) { + tp = mPresContext->GetTextPerfMetrics(); + } + // log load - if (gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG)) { + bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG); + if (shouldLog || tp) { TimeDuration loadTime = TimeStamp::Now() - mLoadBegin; nsIURI* uri = mDocument->GetDocumentURI(); nsAutoCString spec; if (uri) { uri->GetSpec(spec); } - PR_LOG(gLog, PR_LOG_DEBUG, - ("(presshell) %p end load time-ms: %9.2f [%s]\n", - this, loadTime.ToMilliseconds(), spec.get())); + if (shouldLog) { + PR_LOG(gLog, PR_LOG_DEBUG, + ("(presshell) %p load done time-ms: %9.2f [%s]\n", + this, loadTime.ToMilliseconds(), spec.get())); + } + if (tp) { + tp->Accumulate(); + if (tp->cumulative.numChars > 0) { + LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(), + eLog_loaddone, spec.get()); + } + } } #endif } @@ -7877,6 +7999,14 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible) return true; } + gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics(); + TimeStamp timeStart; + if (tp) { + tp->Accumulate(); + tp->reflowCount++; + timeStart = TimeStamp::Now(); + } + target->SchedulePaint(); nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(target); while (parent) { @@ -8032,6 +8162,18 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible) MaybeScheduleReflow(); } +#ifdef PR_LOGGING + // dump text perf metrics for reflows with significant text processing + if (tp) { + if (tp->current.numChars > 100) { + TimeDuration reflowTime = TimeStamp::Now() - timeStart; + LogTextPerfStats(tp, this, tp->current, + reflowTime.ToMilliseconds(), eLog_reflow, nullptr); + } + tp->Accumulate(); + } +#endif + return !interrupted; } @@ -9141,7 +9283,9 @@ void ReflowCountMgr::PaintCount(const char* aName, // We have one frame, therefore we must have a root... aPresContext->GetPresShell()->GetRootFrame()-> StyleFont()->mLanguage, - aPresContext->GetUserFontSet(), *getter_AddRefs(fm)); + aPresContext->GetUserFontSet(), + aPresContext->GetTextPerfMetrics(), + *getter_AddRefs(fm)); aRenderingContext->SetFont(fm); char buf[16]; diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index cb6e2791a3b0..45f8d148d78f 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -318,6 +318,8 @@ public: IsLayoutFlushObserver(this); } + virtual void LoadComplete() MOZ_OVERRIDE; + void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, nsArenaMemoryStats *aArenaObjectsSize, size_t *aPresShellSize, diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 68024a779318..705c9bc05ecf 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -1199,7 +1199,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) if (mViewManagerFlushIsPending) { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Starting ProcessPendingUpdates\n"); + printf_stderr("Starting ProcessPendingUpdates\n"); } #endif #ifndef MOZ_WIDGET_GONK @@ -1215,7 +1215,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) vm->ProcessPendingUpdates(); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("Ending ProcessPendingUpdates\n"); + printf_stderr("Ending ProcessPendingUpdates\n"); } #endif } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index ee4502ef2331..63bf12e8cd38 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2017,6 +2017,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList)); } + CreateOwnLayerIfNeeded(aBuilder, &resultList); + aList->AppendToTop(&resultList); } @@ -8054,7 +8056,9 @@ void nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder, nsDisplayList* aList) { - if (GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) { + if (GetContent() && + GetContent()->IsXUL() && + GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) { aList->AppendNewToTop(new (aBuilder) nsDisplayOwnLayer(aBuilder, this, aList)); } diff --git a/layout/generic/nsPageFrame.cpp b/layout/generic/nsPageFrame.cpp index f712ca45ed60..12b1ee3cf2d2 100644 --- a/layout/generic/nsPageFrame.cpp +++ b/layout/generic/nsPageFrame.cpp @@ -575,6 +575,7 @@ nsPageFrame::PaintHeaderFooter(nsRenderingContext& aRenderingContext, nsRefPtr fontMet; pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, nullptr, pc->GetUserFontSet(), + pc->GetTextPerfMetrics(), *getter_AddRefs(fontMet)); aRenderingContext.SetFont(fontMet); diff --git a/layout/mathml/nsMathMLChar.cpp b/layout/mathml/nsMathMLChar.cpp index e65efbb851f1..d91ed07d18e4 100644 --- a/layout/mathml/nsMathMLChar.cpp +++ b/layout/mathml/nsMathMLChar.cpp @@ -924,6 +924,7 @@ SetFontFamily(nsStyleContext* aStyleContext, aRenderingContext.DeviceContext()->GetMetricsFor(font, aStyleContext->StyleFont()->mLanguage, aStyleContext->PresContext()->GetUserFontSet(), + aStyleContext->PresContext()->GetTextPerfMetrics(), *getter_AddRefs(fm)); // Set the font if it is an unicode table // or if the same family name has been found @@ -1312,7 +1313,8 @@ nsMathMLChar::StretchInternal(nsPresContext* aPresContext, nsRefPtr fm; aRenderingContext.DeviceContext()->GetMetricsFor(font, mStyleContext->StyleFont()->mLanguage, - aPresContext->GetUserFontSet(), *getter_AddRefs(fm)); + aPresContext->GetUserFontSet(), + aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm)); aRenderingContext.SetFont(fm); aDesiredStretchSize = aRenderingContext.GetBoundingMetrics(mData.get(), uint32_t(mData.Length())); @@ -1868,7 +1870,7 @@ nsMathMLChar::PaintForeground(nsPresContext* aPresContext, nsRefPtr fm; aRenderingContext.DeviceContext()->GetMetricsFor(theFont, styleContext->StyleFont()->mLanguage, - aPresContext->GetUserFontSet(), + aPresContext->GetUserFontSet(), aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm)); aRenderingContext.SetFont(fm); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 9734ab94918a..800edc8e5049 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -221,10 +221,11 @@ GetMetricsFor(nsPresContext* aPresContext, if (aUseUserFontSet) { fs = aPresContext->GetUserFontSet(); } + gfxTextPerfMetrics *tp = aPresContext->GetTextPerfMetrics(); nsRefPtr fm; aPresContext->DeviceContext()->GetMetricsFor(font, aStyleFont->mLanguage, - fs, *getter_AddRefs(fm)); + fs, tp, *getter_AddRefs(fm)); return fm.forget(); } diff --git a/toolkit/crashreporter/tools/win32/dump_syms_vc1400.exe b/toolkit/crashreporter/tools/win32/dump_syms_vc1400.exe deleted file mode 100755 index a0b4157d7329..000000000000 Binary files a/toolkit/crashreporter/tools/win32/dump_syms_vc1400.exe and /dev/null differ diff --git a/toolkit/crashreporter/tools/win32/dump_syms_vc1500.exe b/toolkit/crashreporter/tools/win32/dump_syms_vc1500.exe deleted file mode 100644 index 3256d74304cc..000000000000 Binary files a/toolkit/crashreporter/tools/win32/dump_syms_vc1500.exe and /dev/null differ diff --git a/toolkit/devtools/server/actors/webbrowser.js b/toolkit/devtools/server/actors/webbrowser.js index 25854168de80..6fb94ef25d60 100644 --- a/toolkit/devtools/server/actors/webbrowser.js +++ b/toolkit/devtools/server/actors/webbrowser.js @@ -481,6 +481,9 @@ function BrowserTabActor(aConnection, aBrowser, aTabBrowser) this._extraActors = {}; this._onWindowCreated = this.onWindowCreated.bind(this); + + // Number of event loops nested. + this._nestedEventLoopDepth = 0; } // XXX (bug 710213): BrowserTabActor attach/detach/exit/disconnect is a @@ -634,6 +637,9 @@ BrowserTabActor.prototype = { type: "tabDetached" }); } + // Pop all nested event loops if we haven't already. + while (this._nestedEventLoopDepth > 0) + this.postNest(); this._browser = null; this._tabbrowser = null; }, @@ -782,6 +788,7 @@ BrowserTabActor.prototype = { .getInterface(Ci.nsIDOMWindowUtils); windowUtils.suppressEventHandling(true); windowUtils.suspendTimeouts(); + this._nestedEventLoopDepth++; }, /** @@ -790,6 +797,8 @@ BrowserTabActor.prototype = { postNest: function BTA_postNest(aNestData) { if (!this.window) { // The tab is already closed. + dbg_assert(this._nestedEventLoopDepth === 0, + "window shouldn't be closed before all nested event loops have been popped"); return; } let windowUtils = this.window @@ -801,6 +810,7 @@ BrowserTabActor.prototype = { this._pendingNavigation.resume(); this._pendingNavigation = null; } + this._nestedEventLoopDepth--; }, /** diff --git a/view/src/nsViewManager.cpp b/view/src/nsViewManager.cpp index 149a20386135..75fd3e669955 100644 --- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -309,7 +309,7 @@ void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion) #ifdef DEBUG_roc nsRect viewRect = aView->GetDimensions(); nsRect damageRect = damageRegion.GetBounds(); - printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n", + printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n", damageRect.x, damageRect.y, damageRect.width, damageRect.height, viewRect.x, viewRect.y, viewRect.width, viewRect.height); #endif @@ -337,7 +337,7 @@ void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion) if (mPresShell) { #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("--COMPOSITE-- %p\n", mPresShell); + printf_stderr("--COMPOSITE-- %p\n", mPresShell); } #endif uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE; @@ -350,7 +350,7 @@ void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion) } #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("--ENDCOMPOSITE--\n"); + printf_stderr("--ENDCOMPOSITE--\n"); } #endif mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT); @@ -411,7 +411,7 @@ void nsViewManager::ProcessPendingUpdatesForView(nsView* aView, #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget); + printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget); } #endif nsAutoScriptBlocker scriptBlocker; @@ -421,7 +421,7 @@ void nsViewManager::ProcessPendingUpdatesForView(nsView* aView, nsIPresShell::PAINT_LAYERS); #ifdef MOZ_DUMP_PAINTING if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { - printf("---- PAINT END ----\n"); + printf_stderr("---- PAINT END ----\n"); } #endif diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 6cb40833eb71..80717ff2be31 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -3337,16 +3337,7 @@ nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, // Fall back to software if we couldn't use any hardware backends. if (!mLayerManager) { - // Try to use an async compositor first, if possible - if (ShouldUseOffMainThreadCompositing()) { - // e10s uses the parameter to pass in the shadow manager from the TabChild - // so we don't expect to see it there since this doesn't support e10s. - NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s"); - CreateCompositor(); - } - - if (!mLayerManager) - mLayerManager = CreateBasicLayerManager(); + mLayerManager = CreateBasicLayerManager(); } } diff --git a/xpcom/glue/nsCRTGlue.cpp b/xpcom/glue/nsCRTGlue.cpp index 84c11c07448d..f3a73aebe3fa 100644 --- a/xpcom/glue/nsCRTGlue.cpp +++ b/xpcom/glue/nsCRTGlue.cpp @@ -268,16 +268,19 @@ void NS_MakeRandomString(char *aBuf, int32_t aBufLen) #endif #if defined(XP_WIN) + +#define va_copy(dest, src) (dest = src) + void -printf_stderr(const char *fmt, ...) +vprintf_stderr(const char *fmt, va_list args) { if (IsDebuggerPresent()) { char buf[2048]; - va_list args; - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + va_list argsCpy; + va_copy(argsCpy, args); + vsnprintf(buf, sizeof(buf), fmt, argsCpy); buf[sizeof(buf) - 1] = '\0'; - va_end(args); + va_end(argsCpy); OutputDebugStringA(buf); } @@ -285,29 +288,47 @@ printf_stderr(const char *fmt, ...) if (!fp) return; - va_list args; - va_start(args, fmt); vfprintf(fp, fmt, args); - va_end(args); fclose(fp); } + +#undef va_copy + #elif defined(ANDROID) void -printf_stderr(const char *fmt, ...) +vprintf_stderr(const char *fmt, va_list args) { - va_list args; - va_start(args, fmt); __android_log_vprint(ANDROID_LOG_INFO, "Gecko", fmt, args); - va_end(args); } #else +void +vprintf_stderr(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); +} +#endif + void printf_stderr(const char *fmt, ...) { va_list args; va_start(args, fmt); - vfprintf(stderr, fmt, args); + vprintf_stderr(fmt, args); va_end(args); } -#endif + +void +fprintf_stderr(FILE* aFile, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (aFile == stderr) { + vprintf_stderr(fmt, args); + } else { + vfprintf(aFile, fmt, args); + } + va_end(args); +} + + diff --git a/xpcom/glue/nsDebug.h b/xpcom/glue/nsDebug.h index 8d3f543829c9..b4ff1ac25a82 100644 --- a/xpcom/glue/nsDebug.h +++ b/xpcom/glue/nsDebug.h @@ -398,6 +398,13 @@ extern "C" { NS_COM_GLUE void printf_stderr(const char *fmt, ...); +NS_COM_GLUE void +vprintf_stderr(const char *fmt, va_list args); + +// fprintf with special handling for stderr to print to the console +NS_COM_GLUE void +fprintf_stderr(FILE* aFile, const char *fmt, ...); + #ifdef __cplusplus } #endif