Bug 935862 p3 - implement selective reflow for downloadable fonts. r=heycam

This commit is contained in:
John Daggett 2015-03-06 17:44:23 +09:00
parent 16051fe356
commit af0ef7b9f2
7 changed files with 182 additions and 16 deletions

View File

@ -66,6 +66,7 @@
#include "nsPIWindowRoot.h"
#include "mozilla/Preferences.h"
#include "gfxTextRun.h"
#include "nsFontFaceUtils.h"
// Needed for Start/Stop of Image Animation
#include "imgIContainer.h"
@ -2145,24 +2146,47 @@ nsPresContext::RebuildUserFontSet()
}
void
nsPresContext::UserFontSetUpdated()
nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont)
{
if (!mShell)
return;
// Changes to the set of available fonts can cause updates to layout by:
//
// 1. Changing the font used for text, which changes anything that
// depends on text measurement, including line breaking and
// intrinsic widths, and any other parts of layout that depend on
// font metrics. This requires a style change reflow to update.
//
// 2. Changing the value of the 'ex' and 'ch' units in style data,
// which also depend on font metrics. Updating this information
// requires rebuilding the rule tree from the top, avoiding the
// reuse of cached data even when no style rules have changed.
bool usePlatformFontList = true;
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
usePlatformFontList = false;
#endif
PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, eRestyle_ForceDescendants);
// xxx - until the Linux platform font list is always used, use full
// restyle to force updates with gfxPangoFontGroup usage
// Note: this method is called without a font when rules in the userfont set
// are updated, which may occur during reflow as a result of the lazy
// initialization of the userfont set. It would be better to avoid a full
// restyle but until this method is only called outside of reflow, schedule a
// full restyle in these cases.
if (!usePlatformFontList || !aUpdatedFont) {
PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, eRestyle_ForceDescendants);
return;
}
// Special case - if either the 'ex' or 'ch' units are used, these
// depend upon font metrics. Updating this information requires
// rebuilding the rule tree from the top, avoiding the reuse of cached
// data even when no style rules have changed.
if (UsesExChUnits()) {
// xxx - dbaron said this should work but get ex/ch related reftest failures
// PostRebuildAllStyleDataEvent(nsChangeHint(0), eRestyle_ForceDescendants);
PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, eRestyle_ForceDescendants);
return;
}
// Iterate over the frame tree looking for frames associated with the
// downloadable font family in question. If a frame's nsStyleFont has
// the name, check the font group associated with the metrics to see if
// it contains that specific font (i.e. the one chosen within the family
// given the weight, width, and slant from the nsStyleFont). If it does,
// mark that frame dirty and skip inspecting its descendants.
nsFontFaceUtils::MarkDirtyForFontChange(mShell->GetRootFrame(), aUpdatedFont);
}
FontFaceSet*

View File

@ -56,6 +56,7 @@ class nsICSSPseudoComparator;
struct nsStyleBackground;
struct nsStyleBorder;
class nsIRunnable;
class gfxUserFontEntry;
class gfxUserFontSet;
class gfxTextPerfMetrics;
struct nsFontFaceRuleContainer;
@ -890,7 +891,7 @@ public:
// Should be called whenever the set of fonts available in the user
// font set changes (e.g., because a new font loads, or because the
// user font set is changed and fonts become unavailable).
void UserFontSetUpdated();
void UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont = nullptr);
gfxMissingFontRecorder *MissingFontRecorder() { return mMissingFonts; }
void NotifyMissingFonts();

View File

@ -141,6 +141,7 @@ UNIFIED_SOURCES += [
'nsDOMCSSRGBColor.cpp',
'nsDOMCSSValueList.cpp',
'nsFontFaceLoader.cpp',
'nsFontFaceUtils.cpp',
'nsHTMLCSSStyleSheet.cpp',
'nsHTMLStyleSheet.cpp',
'nsLayoutStylesheetCache.cpp',

View File

@ -121,7 +121,7 @@ nsFontFaceLoader::LoadTimerCallback(nsITimer* aTimer, void* aClosure)
NS_ASSERTION(ctx, "userfontset doesn't have a presContext?");
if (ctx) {
loader->mFontFaceSet->IncrementGeneration();
ctx->UserFontSetUpdated();
ctx->UserFontSetUpdated(loader->GetUserFontEntry());
LOG(("userfonts (%p) timeout reflow\n", loader));
}
}
@ -193,7 +193,7 @@ nsFontFaceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
if (fontUpdate) {
// Update layout for the presence of the new font. Since this is
// asynchronous, reflows will coalesce.
ctx->UserFontSetUpdated();
ctx->UserFontSetUpdated(mUserFontEntry);
LOG(("userfonts (%p) reflow\n", this));
}

View File

@ -45,6 +45,7 @@ public:
static nsresult CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
nsIURI* aTargetURI,
nsISupports* aContext);
gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; }
protected:
virtual ~nsFontFaceLoader();

View File

@ -0,0 +1,116 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:ts=2:et:sw=2:
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxUserFontSet.h"
#include "nsFontFaceUtils.h"
#include "nsFontMetrics.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
#include "nsPlaceholderFrame.h"
#include "nsTArray.h"
static bool StyleContextContainsFont(nsStyleContext* aStyleContext,
const gfxUserFontSet* aUserFontSet,
const gfxUserFontEntry* aFont)
{
// if the font is null, simply check to see whether fontlist includes
// downloadable fonts
if (!aFont) {
const FontFamilyList& fontlist =
aStyleContext->StyleFont()->mFont.fontlist;
return aUserFontSet->ContainsUserFontSetFonts(fontlist);
}
// first, check if the family name is in the fontlist
const nsString& familyName = aFont->FamilyName();
if (!aStyleContext->StyleFont()->mFont.fontlist.Contains(familyName)) {
return false;
}
// family name is in the fontlist, check to see if the font group
// associated with the frame includes the specific userfont
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
getter_AddRefs(fm),
1.0f);
if (fm->GetThebesFontGroup()->ContainsUserFont(aFont)) {
return true;
}
return false;
}
static bool
FrameUsesFont(nsIFrame* aFrame, const gfxUserFontEntry* aFont)
{
// check the style context of the frame
gfxUserFontSet* ufs = aFrame->PresContext()->GetUserFontSet();
if (StyleContextContainsFont(aFrame->StyleContext(), ufs, aFont)) {
return true;
}
// check additional style contexts
int32_t contextIndex = 0;
for (nsStyleContext* extraContext;
(extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
++contextIndex) {
if (StyleContextContainsFont(extraContext, ufs, aFont)) {
return true;
}
}
return false;
}
/* static */ void
nsFontFaceUtils::MarkDirtyForFontChange(nsIFrame* aSubtreeRoot,
const gfxUserFontEntry* aFont)
{
nsAutoTArray<nsIFrame*, 4> subtrees;
subtrees.AppendElement(aSubtreeRoot);
nsIPresShell* ps = aSubtreeRoot->PresContext()->PresShell();
// check descendants, iterating over subtrees that may include
// additional subtrees associated with placeholders
do {
nsIFrame* subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
subtrees.RemoveElementAt(subtrees.Length() - 1);
// Check all descendants to see if they use the font
nsAutoTArray<nsIFrame*, 32> stack;
stack.AppendElement(subtreeRoot);
do {
nsIFrame* f = stack.ElementAt(stack.Length() - 1);
stack.RemoveElementAt(stack.Length() - 1);
// if this frame uses the font, mark its descendants dirty
// and skip checking its children
if (FrameUsesFont(f, aFont)) {
ps->FrameNeedsReflow(f, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
} else {
if (f->GetType() == nsGkAtoms::placeholderFrame) {
nsIFrame* oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
// We have another distinct subtree we need to mark.
subtrees.AppendElement(oof);
}
}
nsIFrame::ChildListIterator lists(f);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* kid = childFrames.get();
stack.AppendElement(kid);
}
}
}
} while (!stack.IsEmpty());
} while (!subtrees.IsEmpty());
}

View File

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:ts=2:et:sw=2:
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* helper utilities for working with downloadable fonts */
#ifndef nsFontFaceUtils_h_
#define nsFontFaceUtils_h_
class gfxUserFontEntry;
class nsIFrame;
class nsFontFaceUtils
{
public:
// mark dirty frames affected by a downloadable font
static void MarkDirtyForFontChange(nsIFrame* aSubtreeRoot,
const gfxUserFontEntry* aFont);
};
#endif /* !defined(nsFontFaceUtils_h_) */