Bug 1072102 - Part 1: Implement FontFaceSet load and check. r=jdaggett,bzbarsky

This commit is contained in:
Cameron McCormack 2015-06-27 11:41:10 +10:00
parent 69b3f6b78a
commit 55dbc6f840
7 changed files with 232 additions and 18 deletions

View File

@ -495,6 +495,10 @@ DOMInterfaces = {
'wrapperCache': False,
},
'FontFaceSet': {
'implicitJSContext': [ 'load' ],
},
'FontFaceSetIterator': {
'wrapperCache': False,
},

View File

@ -50,13 +50,11 @@ interface FontFaceSet : EventTarget {
// check and start loads if appropriate
// and fulfill promise when all loads complete
// Not implemented yet: bug 1072102.
// [Throws] Promise<sequence<FontFace>> load(DOMString font, optional DOMString text = " ");
[NewObject] Promise<sequence<FontFace>> load(DOMString font, optional DOMString text = " ");
// return whether all fonts in the fontlist are loaded
// (does not initiate load if not available)
// Not implemented yet: bug 1072102.
// [Throws] boolean check(DOMString font, optional DOMString text = " ");
[Throws] boolean check(DOMString font, optional DOMString text = " ");
// async notification that font loading and layout operations are done
[Throws] readonly attribute Promise<void> ready;

View File

@ -19,6 +19,7 @@
#define NS_FONT_WEIGHT_NORMAL 400
#define NS_FONT_WEIGHT_BOLD 700
#define NS_FONT_WEIGHT_THIN 100
#define NS_FONT_STRETCH_ULTRA_CONDENSED (-4)
#define NS_FONT_STRETCH_EXTRA_CONDENSED (-3)

View File

@ -386,8 +386,8 @@ FontFace::Load(ErrorResult& aRv)
return mLoaded;
}
void
FontFace::DoLoad()
gfxUserFontEntry*
FontFace::CreateUserFontEntry()
{
if (!mUserFontEntry) {
MOZ_ASSERT(!HasRule(),
@ -396,13 +396,20 @@ FontFace::DoLoad()
nsRefPtr<gfxUserFontEntry> newEntry =
mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this);
if (!newEntry) {
return;
if (newEntry) {
SetUserFontEntry(newEntry);
}
SetUserFontEntry(newEntry);
}
return mUserFontEntry;
}
void
FontFace::DoLoad()
{
if (!CreateUserFontEntry()) {
return;
}
mUserFontEntry->Load();
}

View File

@ -53,6 +53,7 @@ public:
aUnicodeRanges) {}
virtual void SetLoadState(UserFontLoadState aLoadState) override;
const nsAutoTArray<FontFace*,1>& GetFontFaces() { return mFontFaces; }
protected:
// The FontFace objects that use this user font entry. We need to store
@ -76,6 +77,7 @@ public:
void GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const;
gfxUserFontEntry* CreateUserFontEntry();
gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; }
void SetUserFontEntry(gfxUserFontEntry* aEntry);

View File

@ -6,8 +6,8 @@
#include "FontFaceSet.h"
#include "mozilla/Logging.h"
#include "gfxFontConstants.h"
#include "mozilla/css/Declaration.h"
#include "mozilla/css/Loader.h"
#include "mozilla/dom/CSSFontFaceLoadEvent.h"
#include "mozilla/dom/CSSFontFaceLoadEventBinding.h"
@ -15,9 +15,12 @@
#include "mozilla/dom/FontFaceSetIterator.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "mozilla/Snprintf.h"
#include "nsCORSListenerProxy.h"
#include "nsCSSParser.h"
#include "nsDeviceContext.h"
#include "nsFontFaceLoader.h"
#include "nsIConsoleService.h"
#include "nsIContentPolicy.h"
@ -33,8 +36,10 @@
#include "nsPresContext.h"
#include "nsPrintfCString.h"
#include "nsStyleSet.h"
#include "nsUTF8Utils.h"
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::dom;
#define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
@ -161,13 +166,181 @@ FontFaceSet::RemoveDOMContentLoadedListener()
}
}
void
FontFaceSet::ParseFontShorthandForMatching(
const nsAString& aFont,
nsRefPtr<FontFamilyListRefCnt>& aFamilyList,
uint32_t& aWeight,
int32_t& aStretch,
uint32_t& aItalicStyle,
ErrorResult& aRv)
{
// Parse aFont as a 'font' property value.
Declaration declaration;
declaration.InitializeEmpty();
bool changed = false;
nsCSSParser parser;
parser.ParseProperty(eCSSProperty_font,
aFont,
mDocument->GetDocumentURI(),
mDocument->GetDocumentURI(),
mDocument->NodePrincipal(),
&declaration,
&changed,
/* aIsImportant */ false);
// All of the properties we are interested in should have been set at once.
MOZ_ASSERT(changed == (declaration.HasProperty(eCSSProperty_font_family) &&
declaration.HasProperty(eCSSProperty_font_style) &&
declaration.HasProperty(eCSSProperty_font_weight) &&
declaration.HasProperty(eCSSProperty_font_stretch)));
if (!changed) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return;
}
nsCSSCompressedDataBlock* data = declaration.GetNormalBlock();
MOZ_ASSERT(!declaration.GetImportantBlock());
const nsCSSValue* family = data->ValueFor(eCSSProperty_font_family);
if (family->GetUnit() != eCSSUnit_FontFamilyList) {
// We got inherit, initial, unset, a system font, or a token stream.
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return;
}
aFamilyList =
static_cast<FontFamilyListRefCnt*>(family->GetFontFamilyListValue());
int32_t weight = data->ValueFor(eCSSProperty_font_weight)->GetIntValue();
// Resolve relative font weights against the initial of font-weight
// (normal, which is equivalent to 400).
if (weight == NS_STYLE_FONT_WEIGHT_BOLDER) {
weight = NS_FONT_WEIGHT_BOLD;
} else if (weight == NS_STYLE_FONT_WEIGHT_LIGHTER) {
weight = NS_FONT_WEIGHT_THIN;
}
aWeight = weight;
aStretch = data->ValueFor(eCSSProperty_font_stretch)->GetIntValue();
aItalicStyle = data->ValueFor(eCSSProperty_font_style)->GetIntValue();
}
static bool
HasAnyCharacterInUnicodeRange(gfxUserFontEntry* aEntry,
const nsAString& aInput)
{
const char16_t* p = aInput.Data();
const char16_t* end = p + aInput.Length();
while (p < end) {
uint32_t c = UTF16CharEnumerator::NextChar(&p, end);
if (aEntry->CharacterInUnicodeRange(c)) {
return true;
}
}
return false;
}
void
FontFaceSet::FindMatchingFontFaces(const nsAString& aFont,
const nsAString& aText,
nsTArray<FontFace*>& aFontFaces,
ErrorResult& aRv)
{
nsRefPtr<FontFamilyListRefCnt> familyList;
uint32_t weight;
int32_t stretch;
uint32_t italicStyle;
ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle,
aRv);
if (aRv.Failed()) {
return;
}
gfxFontStyle style;
style.style = italicStyle;
style.weight = weight;
style.stretch = stretch;
nsTArray<FontFaceRecord>* arrays[2];
arrays[0] = &mNonRuleFaces;
arrays[1] = &mRuleFaces;
// Set of FontFaces that we want to return.
nsTHashtable<nsPtrHashKey<FontFace>> matchingFaces;
for (const FontFamilyName& fontFamilyName : familyList->GetFontlist()) {
nsRefPtr<gfxFontFamily> family =
mUserFontSet->LookupFamily(fontFamilyName.mName);
if (!family) {
continue;
}
nsAutoTArray<gfxFontEntry*,4> entries;
bool needsBold;
family->FindAllFontsForStyle(style, entries, needsBold);
for (gfxFontEntry* e : entries) {
FontFace::Entry* entry = static_cast<FontFace::Entry*>(e);
if (HasAnyCharacterInUnicodeRange(entry, aText)) {
for (FontFace* f : entry->GetFontFaces()) {
matchingFaces.PutEntry(f);
}
}
}
}
// Add all FontFaces in matchingFaces to aFontFaces, in the order
// they appear in the FontFaceSet.
for (nsTArray<FontFaceRecord>* array : arrays) {
for (FontFaceRecord& record : *array) {
FontFace* f = record.mFontFace;
if (matchingFaces.Contains(f)) {
aFontFaces.AppendElement(f);
}
}
}
}
already_AddRefed<Promise>
FontFaceSet::Load(const nsAString& aFont,
FontFaceSet::Load(JSContext* aCx,
const nsAString& aFont,
const nsAString& aText,
ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
FlushUserFontSet();
nsTArray<nsRefPtr<Promise>> promises;
nsTArray<FontFace*> faces;
FindMatchingFontFaces(aFont, aText, faces, aRv);
if (aRv.Failed()) {
return nullptr;
}
for (FontFace* f : faces) {
nsRefPtr<Promise> promise = f->Load(aRv);
if (aRv.Failed()) {
return nullptr;
}
if (!promises.AppendElement(promise, fallible)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
}
nsIGlobalObject* globalObject = GetParentObject();
JS::Rooted<JSObject*> jsGlobal(aCx, globalObject->GetGlobalJSObject());
GlobalObject global(aCx, jsGlobal);
nsRefPtr<Promise> result = Promise::All(global, promises, aRv);
return result.forget();
}
bool
@ -175,8 +348,21 @@ FontFaceSet::Check(const nsAString& aFont,
const nsAString& aText,
ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return false;
FlushUserFontSet();
nsTArray<FontFace*> faces;
FindMatchingFontFaces(aFont, aText, faces, aRv);
if (aRv.Failed()) {
return false;
}
for (FontFace* f : faces) {
if (f->Status() != FontFaceLoadStatus::Loaded) {
return false;
}
}
return true;
}
Promise*

View File

@ -21,6 +21,9 @@ class nsIPrincipal;
class nsPIDOMWindow;
namespace mozilla {
namespace css {
class FontFamilyListRefCnt;
}
namespace dom {
class FontFace;
class Promise;
@ -151,7 +154,8 @@ public:
IMPL_EVENT_HANDLER(loading)
IMPL_EVENT_HANDLER(loadingdone)
IMPL_EVENT_HANDLER(loadingerror)
already_AddRefed<mozilla::dom::Promise> Load(const nsAString& aFont,
already_AddRefed<mozilla::dom::Promise> Load(JSContext* aCx,
const nsAString& aFont,
const nsAString& aText,
mozilla::ErrorResult& aRv);
bool Check(const nsAString& aFont,
@ -271,6 +275,18 @@ private:
// Helper function for HasLoadingFontFaces.
void UpdateHasLoadingFontFaces();
void ParseFontShorthandForMatching(
const nsAString& aFont,
nsRefPtr<mozilla::css::FontFamilyListRefCnt>& aFamilyList,
uint32_t& aWeight,
int32_t& aStretch,
uint32_t& aItalicStyle,
ErrorResult& aRv);
void FindMatchingFontFaces(const nsAString& aFont,
const nsAString& aText,
nsTArray<FontFace*>& aFontFaces,
mozilla::ErrorResult& aRv);
nsRefPtr<UserFontSet> mUserFontSet;
// The document this is a FontFaceSet for.