Rebuild the user font set when style sheets are enabled/disabled/added/removed, rules are modified/added/removed, or media changes change which style sheets apply. (Bug 457821) sr=bzbarsky r=jdaggett,bzbarsky a=blocking1.9.1+

This commit is contained in:
L. David Baron 2008-11-25 13:27:54 -08:00
parent 88620d4af0
commit 49d3fcc1ac
5 changed files with 157 additions and 26 deletions

View File

@ -1438,6 +1438,9 @@ nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint)
// We must have been torn down. Nothing to do here.
return;
}
RebuildUserFontSet();
mShell->FrameConstructor()->RebuildAllStyleData(aExtraHint);
}
@ -1693,40 +1696,130 @@ InsertFontFaceRule(nsCSSFontFaceRule *aRule, gfxUserFontSet* aFontSet)
}
}
gfxUserFontSet*
gfxUserFontSet*
nsPresContext::GetUserFontSet()
{
// We want to initialize the user font set lazily the first time the
// user asks for it, rather than building it too early and forcing
// rule cascade creation. Thus we try to enforce the invariant that
// we *never* build the user font set until the first call to
// GetUserFontSet. However, once it's been requested, we can't wait
// for somebody to call GetUserFontSet in order to rebuild it (see
// comments below in RebuildUserFontSet for why).
if (mUserFontSetDirty) {
NS_IF_RELEASE(mUserFontSet);
// If this assertion fails, and there have actually been changes to
// @font-face rules, then we will call StyleChangeReflow in
// FlushUserFontSet. Since we're likely in the middle of reflow,
// that's a bad thing to do.
NS_ASSERTION(!mGetUserFontSetCalled,
"FlushUserFontSet should have been called first");
FlushUserFontSet();
}
mGetUserFontSetCalled = PR_TRUE;
return mUserFontSet;
}
void
nsPresContext::FlushUserFontSet()
{
if (!mShell)
return; // we've been torn down
if (mUserFontSetDirty) {
if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
nsRefPtr<gfxUserFontSet> oldUserFontSet = mUserFontSet;
nsTArray< nsRefPtr<nsCSSFontFaceRule> > rules;
if (!mShell->StyleSet()->AppendFontFaceRules(this, rules))
return nsnull;
if (rules.Length() > 0) {
nsFontFaceLoaderContext *loaderCtx =
new nsFontFaceLoaderContext(this);
if (!loaderCtx)
return nsnull;
gfxUserFontSet *fs = new gfxUserFontSet(loaderCtx);
// user font set owns loader context
if (!fs) {
delete loaderCtx;
return nsnull;
}
mUserFontSet = fs;
NS_ADDREF(mUserFontSet);
return;
PRBool differ;
if (rules.Length() == mFontFaceRules.Length()) {
differ = PR_FALSE;
for (PRUint32 i = 0, i_end = rules.Length(); i < i_end; ++i) {
InsertFontFaceRule(rules[i], fs);
if (rules[i] != mFontFaceRules[i]) {
differ = PR_TRUE;
break;
}
}
} else {
differ = PR_TRUE;
}
// Only rebuild things if the set of @font-face rules is different.
if (differ) {
NS_IF_RELEASE(mUserFontSet);
if (rules.Length() > 0) {
nsFontFaceLoaderContext *loaderCtx =
new nsFontFaceLoaderContext(this);
if (!loaderCtx)
return;
gfxUserFontSet *fs = new gfxUserFontSet(loaderCtx);
// user font set owns loader context
if (!fs) {
delete loaderCtx;
return;
}
mUserFontSet = fs;
NS_ADDREF(mUserFontSet);
for (PRUint32 i = 0, i_end = rules.Length(); i < i_end; ++i) {
InsertFontFaceRule(rules[i], fs);
}
}
}
#ifdef DEBUG
PRBool success =
#endif
rules.SwapElements(mFontFaceRules);
NS_ASSERTION(success, "should never fail given both are heap arrays");
if (mGetUserFontSetCalled && oldUserFontSet != mUserFontSet) {
// If we've changed, created, or destroyed a user font set, we
// need to trigger a style change reflow.
// We need to enqueue a style change reflow (for later) to
// reflect that we're dropping @font-face rules. This is the
// same thing nsFontFaceLoader does when font downloads
// complete. (However, without a reflow, nothing will happen
// to start any downloads that are needed.)
mShell->StyleChangeReflow();
}
}
mUserFontSetDirty = PR_FALSE;
}
return mUserFontSet;
}
void
nsPresContext::RebuildUserFontSet()
{
if (!mGetUserFontSetCalled) {
// We want to lazily build the user font set the first time it's
// requested (so we don't force creation of rule cascades too
// early), so don't do anything now.
return;
}
mUserFontSetDirty = PR_TRUE;
// Somebody has already asked for the user font set, so we need to
// post an event to rebuild it. Setting the user font set to be dirty
// and lazily rebuilding it isn't sufficient, since it is only the act
// of rebuilding it that will trigger the style change reflow that
// calls GetUserFontSet. (This reflow causes rebuilding of text runs,
// which starts font loads, whose completion causes another style
// change reflow).
if (!mPostedFlushUserFontSet) {
nsCOMPtr<nsIRunnable> ev =
new nsRunnableMethod<nsPresContext>(this,
&nsPresContext::HandleRebuildUserFontSet);
if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
mPostedFlushUserFontSet = PR_TRUE;
}
}
}
void

View File

@ -66,6 +66,8 @@
// This also pulls in gfxTypes.h, which we cannot include directly.
#include "gfxRect.h"
#include "nsRegion.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
class nsImageLoader;
#ifdef IBMBIDI
@ -93,6 +95,7 @@ struct nsStyleBackground;
template <class T> class nsRunnableMethod;
class nsIRunnable;
class gfxUserFontSet;
class nsCSSFontFaceRule;
#ifdef MOZ_REFLOW_PERF
class nsIRenderingContext;
@ -743,6 +746,8 @@ public:
PRBool SupressingResizeReflow() const { return mSupressResizeReflow; }
gfxUserFontSet* GetUserFontSet();
void FlushUserFontSet();
void RebuildUserFontSet(); // asynchronously
void NotifyInvalidation(const nsRect& aRect, PRBool aIsCrossDoc);
void FireDOMPaintEvent();
@ -751,7 +756,7 @@ protected:
friend class nsRunnableMethod<nsPresContext>;
NS_HIDDEN_(void) ThemeChangedInternal();
NS_HIDDEN_(void) SysColorChangedInternal();
NS_HIDDEN_(void) SetImgAnimations(nsIContent *aParent, PRUint16 aMode);
NS_HIDDEN_(void) GetDocumentColorPreferences();
@ -766,6 +771,11 @@ protected:
NS_HIDDEN_(void) UpdateCharSet(const nsAFlatCString& aCharSet);
void HandleRebuildUserFontSet() {
mPostedFlushUserFontSet = PR_FALSE;
FlushUserFontSet();
}
// IMPORTANT: The ownership implicit in the following member variables
// has been explicitly checked. If you add any members to this class,
// please make the ownership explicit (pinkerton, scc).
@ -812,6 +822,8 @@ protected:
// container for per-context fonts (downloadable, SVG, etc.)
gfxUserFontSet* mUserFontSet;
// The list of @font-face rules that we put into mUserFontSet
nsTArray< nsRefPtr<nsCSSFontFaceRule> > mFontFaceRules;
PRInt32 mFontScaler;
nscoord mMinimumFontSize;
@ -870,7 +882,13 @@ protected:
unsigned mPendingMediaFeatureValuesChanged : 1;
unsigned mPrefChangePendingNeedsReflow : 1;
unsigned mRenderedPositionVaryingContent : 1;
// Is the current mUserFontSet valid?
unsigned mUserFontSetDirty : 1;
// Has GetUserFontSet() been called?
unsigned mGetUserFontSetCalled : 1;
// Do we currently have an event posted to call FlushUserFontSet?
unsigned mPostedFlushUserFontSet : 1;
// resize reflow is supressed when the only change has been to zoom
// the document rather than to change the document's dimensions

View File

@ -4558,11 +4558,16 @@ PresShell::DoFlushPendingNotifications(mozFlushType aType,
mFrameConstructor->ProcessPendingRestyles();
}
// There might be more pending constructors now, but we're not going to
// worry about them. They can't be triggered during reflow, so we should
// be good.
if (aType >= Flush_Layout && !mIsDestroying) {
// Flush any pending update of the user font set, since that could
// post a style change reflow.
mPresContext->FlushUserFontSet();
mFrameConstructor->RecalcQuotesAndCounters();
mViewManager->FlushDelayedResize();
ProcessReflowCommands(aInterruptibleReflow);
@ -4777,17 +4782,21 @@ nsIPresShell::ReconstructStyleDataInternal()
{
mStylesHaveChanged = PR_FALSE;
if (!mDidInitialReflow) {
// Nothing to do here, since we have no frames yet
return;
}
if (mIsDestroying) {
// We don't want to mess with restyles at this point
return;
}
if (mPresContext) {
mPresContext->RebuildUserFontSet();
}
nsIContent* root = mDocument->GetRootContent();
if (!mDidInitialReflow) {
// Nothing to do here, since we have no frames yet
return;
}
if (!root) {
// No content to restyle
return;
@ -6242,6 +6251,8 @@ PresShell::WillDoReflow()
mCaret->UpdateCaretPosition();
}
mPresContext->FlushUserFontSet();
mFrameConstructor->BeginUpdate();
}

View File

@ -2166,6 +2166,10 @@ nsCSSRuleProcessor::AppendFontFaceRules(
nsresult
nsCSSRuleProcessor::ClearRuleCascades()
{
// We rely on our caller (perhaps indirectly) to do something that
// will rebuild style data and the user font set (either
// nsIPresShell::ReconstructStyleData or
// nsPresContext::RebuildAllStyleData).
RuleCascadeData *data = mRuleCascades;
mRuleCascades = nsnull;
while (data) {

View File

@ -127,7 +127,12 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
nsIPresShell *ps = loaderCtx->mPresContext->PresShell();
if (ps) {
// reflow async so that reflows coalesce
// Update layout for the presence of the new font. Since this is
// asynchronous, reflows will coalesce.
// nsPresContext::FlushUserFontSet does the same thing when we
// remove a user font set, for fonts becoming unavailable, or when
// we add one, because if we change it dynamically we need to
// trigger reflow to cause gfx to request the fonts.
ps->StyleChangeReflow();
LOG(("fontdownloader (%p) reflow\n", this));
}